【问题标题】:Simulate HTTP server time out for HTTP client request模拟 HTTP 客户端请求的 HTTP 服务器超时
【发布时间】:2014-10-19 04:27:30
【问题描述】:

参考: HttpURLConnection timeout question

-> 关于如何自动化上述单元测试用例的任何想法?

更具体地说,如果 HTTP 客户端已将 5 秒设置为超时,我希望服务器在 10 秒后发送响应。这将确保我的客户端会因超时而失败,从而自动执行此场景。

我会很感激服务器端的伪代码(任何轻量级 http 服务器,如码头或任何其他都可以)。

【问题讨论】:

    标签: java http junit mocking timeout


    【解决方案1】:

    您不想在单元测试中实际连接到真实服务器。如果您想实际连接到真实服务器,这在技术上是一个集成测试。

    由于您正在测试客户端代码,因此您应该使用单元测试,这样您就不需要连接到真实的服务器。相反,您可以使用模拟对象来模拟与服务器的连接。这真的很棒,因为您可以模拟使用真实服务器时难以实现的条件(例如会话中的连接失败等)。

    使用 mock 进行单元测试也会使测试运行得更快,因为您不需要连接任何东西,因此没有 I/O 延迟。

    由于您链接到另一个问题,我将使用该代码示例(为清楚起见,在此处重新粘贴)我创建了一个名为 MyClass 的类,其方法为 foo(),该方法连接到 URL 并在连接成功时返回 true 或 false .正如链接的问题所做的那样:

    public class MyClass {
    
    private String url = "http://example.com";
    
    public boolean foo(){
        try {
               HttpURLConnection.setFollowRedirects(false);
               HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection();
               con.setRequestMethod("HEAD");
    
               con.setConnectTimeout(5000); //set timeout to 5 seconds
    
               return (con.getResponseCode() == HttpURLConnection.HTTP_OK);
            } catch (java.net.SocketTimeoutException e) {
               return false;
            } catch (java.io.IOException e) {
               return false;
            }
    
        }
    }
    

    我将使用Mockito 来制作模拟对象,因为这是最流行的模拟对象库之一。此外,由于代码在 foo 方法中创建了一个新的 URL 对象(这不是最佳设计),我将使用 PowerMock 库,它可以拦截对 new 的调用。在实际的生产代码中,我建议使用依赖注入或至少方法提取来将URL 对象创建为工厂方法,以便您可以覆盖它以简化测试。但既然我坚持你的例子,我不会改变任何东西。

    这是使用 Mockito 和 Powermock 测试超时的测试代码:

    import java.net.HttpURLConnection;
    import java.net.SocketTimeoutException;    
    import java.net.URL;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.mockito.Mockito;
    import org.powermock.api.mockito.PowerMockito;
    import org.powermock.core.classloader.annotations.PrepareForTest;
    import org.powermock.modules.junit4.PowerMockRunner;
    
    import static org.junit.Assert.*;
    
    @RunWith(PowerMockRunner.class)
    //This tells powermock that we will modify MyClass.class in this test 
    //- needed for changing the call to new URL
    @PrepareForTest(MyClass.class) 
    public class ConnectionTimeOutTest {
    
    String url = "http://example.com";
    @Test
    public void timeout() throws Exception{
        //create a mock URL and mock HttpURLConnection objects
        //that will be our simulated server
        URL mockURL = PowerMockito.mock(URL.class);
        HttpURLConnection mockConnection = PowerMockito.mock(HttpURLConnection.class);
    
        //powermock will intercept our call to new URL( url) 
        //and return our mockURL object instead!
        PowerMockito.whenNew(URL.class).withArguments(url).thenReturn(mockURL);
        //This tells our mockURL class to return our mockConnection object when our client
        //calls the open connection method
        PowerMockito.when(mockURL.openConnection()).thenReturn(mockConnection);
    
    
    
        //this is our exception to throw to simulate a timeout
        SocketTimeoutException expectedException = new SocketTimeoutException();
    
        //tells our mockConnection to throw the timeout exception instead of returnig a response code
        PowerMockito.when(mockConnection.getResponseCode()).thenThrow(expectedException);
    
        //now we are ready to actually call the client code
        // cut = Class Under Test
        MyClass cut = new MyClass();
    
        //our code should catch the timeoutexception and return false
        assertFalse(cut.foo());
    
       // tells mockito to expect the given void methods calls
       //this will fail the test if the method wasn't called with these arguments
       //(for example, if you set the timeout to a different value)
        Mockito.verify(mockConnection).setRequestMethod("HEAD");
        Mockito.verify(mockConnection).setConnectTimeout(5000);
    
    }
    }
    

    此测试运行时间不到一秒,这比实际等待超过 5 秒才能真正超时要快得多!

    【讨论】:

    • 这是对 mockito 以及如何在用例中利用它的漂亮解释。非常感谢 dkatzel。
    • @dkatzel 正在使用 testng 框架编写相同的超时测试用例。但是我的模拟对象是空的并且我得到空指针异常。下面是我的代码
    • @Test public void testSocketExceptionEvents() 抛出异常{ String url="google.co.in"; URL mockURL = PowerMockito.mock(URL.class); PowerMockito.whenNew(URL.class).withArguments(url).thenReturn(mockURL); SocketTimeoutException expectedException = new SocketTimeoutException(); PowerMockito.when(mockURL.openConnection()).thenThrow(expectedException);发件人发件人=新发件人();字符串输入="{\"level\":3,\"event\":{\"name\":\"myevent\"}}"; JSONObject vent = new JSONObject(input); Assert.assertNotNull(sender.send(vent));
    • 这里是 another good article 关于使用 Mockito 和 Spring 来覆盖 Beans 的模拟 Beans 是通过使用 @ActiveProfiles@Profile 注释激活的。我是 Mockito(相对而言是 Spring)的新手,所以那篇文章加上这个答案非常有帮助。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2012-11-14
    • 1970-01-01
    • 1970-01-01
    • 2013-03-07
    • 1970-01-01
    • 1970-01-01
    • 2017-05-14
    • 2020-07-15
    相关资源
    最近更新 更多