在测试中的方法内部实例化模拟对象的最佳方法是什么?

时间:2017-05-15 22:01:33

标签: java unit-testing

我有以下代码,我寻找测试它的最佳方法:

public class ClientFactory {

    private ConfigurationLoader loader;

    public ClientFactory(ConfigurationLoader loader) {
        this.loader = loader;
    }


  public IRest create(String something) {
    Configuration config = loader.load(something);

    if (magic()) {
        return new ClientType1(config); 
    }    

    return new ClientType2(config); 
  }

}  

public class ClientType1 implements IRest{

  private Configuration config;

  public ClientType1(Configuration config) {
    this.config = config;
  }

  public Something doGetRequest(Long id) {
    WebClient client = getHttpClient();

    return client.get();        
  }

  private WebClient getHttpClient() {
    WebClient client = new WebClient();

    client.setSchema(config.getSchema());
    client.setHostname(config.getHostname());
    client.setPort(config.getPort());

    // and so on ....

    return client;

  }

}

我想测试ConfigurationLoader和ClientType1.getHttpClient方法之间的交互/行为。从一方面来看,我认为这是一个好主意,测试对象之间的交互,从另一方面来看,嗯我测试setter和getter - 无聊,这里没有涉及业务logig。哪一个更真实?

当实例化配置对象时,可以轻松地将其转移到ClientType1中,模拟新的WebClient()'似乎是问题所在。我想过:

public class ClientType1 implements IRest{

  private Configuration config;
  private WebClient client; // this will be replaced by mock

  public ClientType1(Configuration config) {
    this.config = config;
    webClient =  new WebClient();
  }

  .....

  private Client getHttpClient() {       
    client.setSchema(config.getSchema());
    ....

    return client;    
  }
}

并使用PowerMock通过mock替换private WebClient client,但我不确定它是 java方式。任何指导方针/建议?

1 个答案:

答案 0 :(得分:2)

如您所见,new关键字使单元测试变得困难。我建议避免它。我认为你的问题更多的是设计问题。对象应自行配置。设计对象时,请考虑它的真正依赖关系。 IMO ClientType1的真正依赖关系是WebClientWebClient而不是Configuration。 IMO ClientFactory的真正依赖关系是Configuration而不是String

我会像这样重新设计:

interface ClientFactory {
    IRest create(Configuration config);
}

public class DefaultClientFactory implements ClientFactory {
    private final ClientFactory magicClientFactory;
    private final ClientFactory otherClientFactory;

    public DefaultClientFactory(ClientFactory magicClientFactory, ClientFactory otherClientFactory) {
        this.magicClientFactory = magicClientFactory;
        this.otherClientFactory = otherClientFactory;
    }

    public IRest create(Configuration config) {
        if (magic()) {
            return magicClientFactory.create(config);
        } else {
            return otherClientFactory.create(config);
        }
    }
}

interface WebClientFactory {
    WebClient create(Configuration config);
}

public class DefaultWebClientFactory implements WebClientFactory {
    public WebClient create(Configuration config) {
        WebClient client = new WebClient();

        client.setSchema(config.getSchema());
        client.setHostname(config.getHostname());
        client.setPort(config.getPort());
        return client;
    }
}

public class ClientType1Factory implements ClientFactory {
    private final WebClientFactory webClientFactory;

    public ClientType1Factory(WebClientFactory webClientFactory) {
        this.webClientFactory = webClientFactory;
    }

    public IRest create(Configuration config) {
        return new ClientType1(webClientFactory.create(config));
    }
}

public class ClientType1 implements IRest{

    private final WebClient webClient;

    public ClientType1(WebClient webClient) {
        this.webClient = webClient;
    }

    public Something doGetRequest(Long id) {
        return webClient.get();        
    }
}

使用此设计,您可以成功地对每个定义的类进行单元测试,而无需借助PowerMock的高级功能。您可以通过传递模拟ClientType1来对WebClient进行单元测试。您还可以通过传递不同的配置并检查创建的对象是否符合您的预期来测试您的工厂。此外,代码更少耦合,更灵活,每个类别都有一个责任。