我有以下代码,我正在寻找测试它的最佳方法:
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方法之间的交互/行为。一方面,我认为这是个好主意,测试对象之间的交互,另一方面,嗯,我测试了设置者和获取者 - 无聊,这里不涉及业务逻辑。哪一个更真实?
配置对象的模拟在实例化时可以很容易地转移到 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将private WebClient client
替换为模拟,但我不确定这是Java方式。有什么指导方针/建议吗?
正如你所发现的,new
关键字使单元测试变得困难。我建议避免它。我认为你在这里的问题更多的是设计问题。对象不应自行配置。设计对象时,请考虑它的真正依赖项是什么。IMO ClientType1
的真正依赖关系是WebClient
或WebClient
池,而不是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
单元测试。您还可以通过传入不同的配置并检查创建的对象是否符合预期来测试工厂。此外,代码的耦合更少,更灵活,每个类都有一个单一的责任。