如何使用singleton类转向依赖注入

时间:2017-01-13 01:05:44

标签: java junit dependency-injection

我正在努力学习用Java编写Testable代码。我已经看到很多链接说如果我想进行单元测试,单例类的实现并不好。所以这就是我想要解决的问题。我有一个发送HTTP请求(HTTPRequest类)的类。我希望这个类担心使用哪个协议(TLSv1.2等)。我有多个其他类只调用带有body,URL等的HTTPRequest类。

我接近它的方式是HTTPRequest可以是单例类,因为不需要创建同一事物的多个实例。整个项目的协议将是相同的,只需要设置一次。

这就是我的HTTPRequest的样子。

public class HTTPRequest {
    private CloseableHttpClient httpClient;
    private HttpGet httpGet;
    private HttpPut httpPut;
    private HttpPost httpPost;

    HTTPRequest(CloseableHttpClient a, HttpGet b, HttpPut c, HttpPost d) {
        httpClient = a;
        httpGet = b;
        httpPut = c;
        httpPost = d;
    }

    public HTTPResponse getRequest(url) {
        //Do some processing with httpClient & httpGet to send the request
    }
}

现在从需要HTTPRequest的类中有一种方法可以调用getRequest,但只使用一个对象吗?我读了一些关于依赖注入的内容,但我真的无法弄清楚应该怎么做。

我应该如何修改代码以使其可以测试?

3 个答案:

答案 0 :(得分:1)

您可以做的是删除HTTPRequest课程中的构造函数,然后创建名为protectedgetHttpGet()等的getHttpPut方法。

public class HTTPRequest {

    public HTTPResponse getRequest(url) {
        //Do some processing with httpClient & httpGet to send the request
      getHttpGet(url).call();
    }

    protected CloseableHttpClient getHttpClient() {
      return new CloseableHttpClient();
    }

    protected HttpGet getHttpGet(String url) {
      return new HttpGet(url);
    }

    protected HttpPost getHttpPost(String url) {
      return new HttpPost(url);
    }

    ...
}

然后,您可以创建一个继承自HttpRequestMock并覆盖HttpRequest方法的getHttp*类。

public class HTTPRequestMock extends HttpRequest {

    @Override
    protected CloseableHttpClient getHttpClient() {
      //return mock
      return new CloseableHttpClientMock();
    }

    @Override
    protected HttpGet getHttpGet(String url) {
      //return mock
      return new HttpGetMock(url);
    }

    @Override
    protected HttpPost getHttpPost(String url) {
      //return mock
      return new HttpPostMock(url);
    }

    ...
}

最后,您可以在单元测试中相应地注入HttpRequestHttpRequestMock

class HTTPRequestClient {

    private HTTPRequest httpRequest = HttpRequestFactory.getHttpRequest();

    public void callServer(){
        httpRequest.getRequest("http://someurl.com/");
    }

    void setHttpRequest(HTTPRequest httpRequest){
        this.httpRequest = httpRequest;
     }
  }

class HTTPRequestFactory {
   private static final HttpRequest httpRequest = new HttpRequest();

   public static HTTPRequest getHttpRequest() {
     return httpRequest;
   }
}

class HTTPRequestClientTest {
     HTTPRequestClient  httpRequestClient;

       @Before
       public void setUp(){
           httpRequestClient = new HTTPRequestClient();
           httpRequestClient.setHttpRequest(new HttpRequestMock());
        }

        @Test
        ...
      }

答案 1 :(得分:0)

依赖注入(没有注入框架)的简单实现可以使用setter

class SomeOtherClass {

    private HTTPRequest httpRequest;
    public void setHttpRequest(HTTPRequest httpRequest){

    }

    public void someAction(){
        httpRequest.getRequest(...);
    }
}

...
SomeOtherClass someOtherClass = new SomeOtherClass(httpRequest);

在上面的示例中,HttpRequest(依赖项)被注入SomeOtherClass。通过这种方式,单元测试可以创建HttpRequest的模拟,将其提供给SomeOtherClass并测试SomeOtherClass,而不需要外部依赖项。

这有意义吗?

现在,关于单身人士:让HttpRequest成为单身人士会让 Mock 变得更难(使用像Mockito这样的模拟框架)。另外,像SomeOtherClass这样的类只能执行HttpRequest.getInstance(),所以你的测试无法做任何事情。

答案 2 :(得分:0)

  • 将名为beans.xml的空文件放入WEB-INF目录。
  • 使用HttpRequest
  • 为您的@ApplicationScoped课程添加注释
  • 使用它向所有类添加本地字段,并使用@Inject注释它们。