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

标签 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 - 无聊,这里不涉及业务逻辑。哪一个更真实呢?

配置对象的模拟在实例化时可以很容易地转移到 ClientType1 中,模拟“new 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将私有(private)WebClient客户端替换为mock,但我不确定它是java方式。有什么指导/建议吗?

最佳答案

正如您所发现的,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 的高级功能。您可以通过传入模拟 WebClient 来对 ClientType1 进行单元测试。您还可以通过传递不同的配置并检查创建的对象是否是您期望的来测试您的工厂。此外,代码耦合更少、更灵活,并且每个类都有单一职责。

关于java - 在测试方法中模拟实例化对象的最佳方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43989480/

相关文章:

unit-testing - Xcode 4 : Application Tests in iOS Simulator

unit-testing - 自分流测试模式是否违反单一职责原则?

java - 二维数组和

java - FragmentActivity 的 ActionBar 出现空指针异常

java - 使Java JScrollpane仅垂直滚动

java - 如何更改 JTable 中已编辑单元格的背景颜色?

java - 从 AlertDialog 按钮切换 Activity

scala - 如何在 Scala 单元测试中创建临时目录

django - 如何从所有应用程序加载 Django 装置?

c# - 在 Visual Studio 的单元测试中出现错误 "Cannot start more than one local run"