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

发布于 2024-08-31 04:36:20 字数 979 浏览 4 评论 0原文

我想知道使用 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 ();
        }
    }

}

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?

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 ();
        }
    }

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

无言温柔 2024-09-07 04:36:20

存根

您需要一个测试替身(存根)来允许隔离、简单的单元测试。以下未经测试,但演示了该想法。使用 依赖注入 将允许您在测试时注入 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;
  }
}

然后,您创建一个存根(有时称为模拟对象)来代替具体实例。

class TestHttpURLConnection : IHttpURLConnection { /* Methods */ }

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

class MyHttpURLConnection : IHttpURLConnection { /* Methods */ }

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

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 Injection will allow you to inject at test time, a test version of your 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 frameworks as specified by StackOverflow members.

彼岸花ソ最美的依靠 2024-09-07 04:36:20

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

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.

土豪我们做朋友吧 2024-09-07 04:36:20

从稍微不同的角度处理事情......

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

相反,我会专注于测试它调用的、看起来确实能做某事的方法。具体来说...

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

String url = constructURL (event);

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

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

String responseMessage = responseParser.getResponseMessage (connection);

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

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

或类似的内容。

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

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:

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).

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.

稚然 2024-09-07 04:36:20

作为 Finglas 关于模拟的有用答案的替代方案,请考虑使用存根方法,在该方法中我们重写 callWebsite() 的功能。当我们对 callWebsite 的逻辑不如对 pushEvent() 中调用的其他逻辑感兴趣时,这种方法非常有效。要检查的一件重要事情是使用正确的 URL 调用 callWebsite。因此,第一个更改是将 callWebsite() 的方法签名更改为:

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

现在我们创建一个如下所示的存根类:

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; 
    }
}

最后在 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);
    }
 }

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:

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:

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);
    }
 }
等风来 2024-09-07 04:36:20

创建一个抽象类 WebsiteCaller,它是 ConcreteWebsiteCallerWebsiteCallerStub 的父类。

该类应该有一个方法callWebsite (String url)。将 callWebsite 方法从 MyClass 移至 ConcreteWebsiteCallerMyClass 看起来像:

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;
    }
}

并以某种适合测试的方式在您的 WebsiteCallerStub 中实现方法 callWebsite

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

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

Create an abstract class WebsiteCaller which would be a parent of ConcreteWebsiteCaller and WebsiteCallerStub.

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

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 callWebsite in your WebsiteCallerStub in some way appropriate for testing.

Then in your unit test do something like this:

@Test
public void testPushEvent() {
   MyClass mc = new MyClass (new WebsiteCallerStub());
   mc.pushEvent (new Event(...));
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文