java 如何为使用网络连接的类编写 jUnit 测试

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2724035/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 22:31:54  来源:igfitidea点击:

How to write a jUnit test for a class that uses a network connection

javaunit-testingtddjunit

提问by flash

I would like to know what's the best approach to test the method "pushEvent()" in the following class with a jUnit test. My problem is, that the private method "callWebsite()" always requires a connection to the network. How can I avoid this requirement or refactor my class that I can test it without a connection to the network?

我想知道在以下类中使用 jUnit 测试测试方法“pushEvent()”的最佳方法是什么。我的问题是,私有方法“callWebsite()”总是需要连接到网络。我怎样才能避免这个要求或重构我的类,我可以在没有连接到网络的情况下测试它?

class MyClass {

    public String pushEvent (Event event) {
        //do something here
        String url = constructURL (event); //construct the website url
        String response = callWebsite (url);

        return response;
    }

    private String callWebsite (String url) {
        try {
            URL requestURL = new URL (url);
            HttpURLConnection connection = null;
            connection = (HttpURLConnection) requestURL.openConnection ();


            String responseMessage = responseParser.getResponseMessage (connection);
            return responseMessage;
        } catch (MalformedURLException e) {
            e.printStackTrace ();
            return e.getMessage ();
        } catch (IOException e) {
            e.printStackTrace ();
            return e.getMessage ();
        }
    }

}

采纳答案by duduamar

Possible solution: You can extend this class, override callWebsite (you have to make it protected for this purpose) - and the override method write some stub method implementation.

可能的解决方案:你可以扩展这个类,覆盖 callWebsite(你必须为此目的保护它) - 并且覆盖方法编写一些存根方法实现。

回答by Finglas

Stubbing

存根

You'll need a test double (stub) to allow isolated, easy, unit testing. The following is non tested, but demonstrates the idea. The use of Dependency Injectionwill allow you to inject at test time, a test version of your HttpURLConnection.

您将需要一个测试替身(存根)来进行隔离、简单的单元测试。以下内容未经测试,但演示了该想法。依赖注入的使用将允许您在测试时注入 HttpURLConnection 的测试版本。

public class MyClass() 
{
   private IHttpURLConnection httpUrlConnection;   

   public MyClass(IHttpURLConnection httpUrlConnection)
   {
       this.httpUrlConnection = httpUrlConnection;
   }

   public String pushEvent(Event event) 
   {
       String url = constructURL(event);
       String response = callWebsite(url);
       return response;
  }
}

Then you create a stub (sometimes referred to as a mock object) to be the stand in for the concrete instance.

然后创建一个存根(有时称为模拟对象)作为具体实例的代表。

class TestHttpURLConnection : IHttpURLConnection { /* Methods */ }

You'll also construct a concrete version, for your production code to use.

您还将构建一个具体版本,供您的生产代码使用。

class MyHttpURLConnection : IHttpURLConnection { /* Methods */ }

Using your test class (an adapter) you are able to specifiy what should happen during your test. A mocking framework will enable you to do this with less code, or you can manually wire this up. The end result of this for your test is that you'll set your expectations for your test, for example, in this case you may set OpenConnection to return a true boolean (This is just an example by the way). Your test will then assert that when this value is true, the return value of your PushEvent method matches some expected result. I've not touched Java properly for a while, but here are some recommended mocking frameworksas specified by StackOverflow members.

使用您的测试类(一个适配器),您可以指定在测试期间应该发生什么。模拟框架将使您能够以更少的代码完成此操作,或者您可以手动将其连接起来。测试的最终结果是您将设置您对测试的期望,例如,在这种情况下,您可以将 OpenConnection 设置为返回一个真正的布尔值(顺便说一下,这只是一个示例)。然后,您的测试将断言,当此值为 true 时,您的 PushEvent 方法的返回值与某些预期结果匹配。我有一段时间没有正确接触 Java,但这里有一些StackOverflow 成员指定的推荐模拟框架

回答by RHSeeger

Approaching things from a slightly different angle...

从稍微不同的角度看待事物......

I'd worry less about testing this specific class. The code in it is extremely simple and, while a functional test to make sure it's working with a connection would be helpful, a unit level test "may" not be necessary.

我不太担心测试这个特定的类。其中的代码非常简单,虽然确保它与连接一起工作的功能测试会有所帮助,但“可能”不需要单元级测试。

Instead, I'd focus on testing the methods it calls that appear to actually do something. Specifically...

相反,我会专注于测试它调用的看似实际执行某些操作的方法。具体来说...

I'd test constructURL method from this line:

我将从这一行测试constructURL方法:

String url = constructURL (event);

making sure that it can construct a URL properly from different Events, and throws Exceptions when it should (possibly on an invalid Event or null).

确保它可以从不同的事件正确构造一个 URL,并在它应该抛出异常时(可能在无效事件或 null 上)。

And I'd test the method from the following line:

我将从以下行测试该方法:

String responseMessage = responseParser.getResponseMessage (connection);

Possibly pulling out any "get information out of the connection" logic into one proc, and leaving only "parse said information" in the original one:

可能将任何“从连接中获取信息”逻辑提取到一个过程中,而在原始过程中只留下“解析所述信息”:

String responseMessage = responseParser.getResponseMessage(responseParser.getResponseFromConnection(connection));

or something along those lines.

或类似的规定。

The idea being to put any "must deal with external data sources" code in one method, and any code logic in separate methods that can be easily tested.

想法是将任何“必须处理外部数据源”的代码放在一个方法中,并将任何代码逻辑放在可以轻松测试的单独方法中。

回答by Chris Knight

As an alternative to Finglas's helpful answer with respect to mocking, consider a stubbed approach where we override the functionality of callWebsite(). This works quite well in the case where we aren't so interested in the logic of callWebsite as that of the other logic called within pushEvent(). One important thing to check is that callWebsite is calledwith the correct URL. So, first change is to the method signature of callWebsite() to become:

作为Finglas 在模拟方面的有用答案的替代方案,请考虑一种我们覆盖callWebsite() 功能的存根方法。在我们对 callWebsite 的逻辑不像在 pushEvent() 中调用的其他逻辑那么感兴趣的情况下,这非常有效。要检查的一件重要事情是使用正确的 URL 调用 callWebsite。所以,首先要改变 callWebsite() 的方法签名,变成:

protected String callWebsite(String url){...}

Now we create a stubbed class like this:

现在我们创建一个这样的存根类:

class MyClassStub extends MyClass {
    private String callWebsiteUrl;
    public static final String RESPONSE = "Response from callWebsite()";

    protected String callWebsite(String url) {
        //don't actually call the website, just hold onto the url it was going to use
        callWebsiteUrl = url;
        return RESPONSE;
    }
    public String getCallWebsiteUrl() { 
        return callWebsiteUrl; 
    }
}

And finally in our JUnit test:

最后在我们的 JUnit 测试中:

public class MyClassTest extends TestCase {
    private MyClass classUnderTest;
    protected void setUp() {
        classUnderTest = new MyClassStub();
    }
    public void testPushEvent() { //could do with a more descriptive name
        //create some Event object 'event' here
        String response = classUnderTest.pushEvent(event);
        //possibly have other assertions here
        assertEquals("http://some.url", 
                     (MyClassStub)classUnderTest.getCallWebsiteUrl());
        //finally, check that the response from the callWebsite() hasn't been 
        //modified before being returned back from pushEvent()
        assertEquals(MyClassStub.RESPONSE, response);
    }
 }

回答by Roman

Create an abstract class WebsiteCallerwhich would be a parent of ConcreteWebsiteCallerand WebsiteCallerStub.

创建一个抽象类WebsiteCaller,它将是ConcreteWebsiteCallerand的父类WebsiteCallerStub

This class should have one method callWebsite (String url). Move your callWebsite method from MyClassto ConcreteWebsiteCaller. And MyClasswill look like:

这个类应该有一个方法callWebsite (String url)。将您的 callWebsite 方法从MyClassConcreteWebsiteCaller。而MyClass看起来像:

class MyClass {

    private WebsiteCaller caller;

    public MyClass (WebsiteCaller caller) {
        this.caller = caller;
    }

    public String pushEvent (Event event) {
        //do something here
        String url = constructURL (event); //construct the website url
        String response = caller.callWebsite (url);

        return response;
    }
}

and implement method callWebsitein your WebsiteCallerStubin some way appropriate for testing.

和实现方法callWebsiteWebsiteCallerStub适当的测试某种方式。

Then in your unit test do something like this:

然后在你的单元测试中做这样的事情:

@Test
public void testPushEvent() {
   MyClass mc = new MyClass (new WebsiteCallerStub());
   mc.pushEvent (new Event(...));
}