繁体   English   中英

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

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

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

}

打桩

您需要一个测试双(存根)以允许隔离,简单,单元测试。 以下是未经测试,但演示了这个想法。 使用依赖注入将允许您在测试时注入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时,PushEvent方法的返回值与某些预期结果匹配。 我暂时没有正确地触及Java,但是这里有一些建议的模拟框架 ,由StackOverflow成员指定。

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

从略微不同的角度接近事物......

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

相反,我专注于测试它所调用的实际上做某事的方法。 特别...

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

String url = constructURL (event);

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

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

String responseMessage = responseParser.getResponseMessage (connection);

可能将任何“从连接中获取信息”逻辑拉出到一个proc中,并且只留下“解析所述信息”的原始信息:

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

或类似的规定。

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

作为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);
    }
 }

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

这个类应该有一个方法callWebsite (String url) 将您的callWebsite方法从MyClass移动到ConcreteWebsiteCaller 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;
    }
}

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM