我想模拟的类:
测试类.java
public class testClass(){
public String getDescription(String input){
String value = this.getDetails(input); // i am not going to change this line, hence want to mock this.
//below this i have some complexity logic, which i would like to fix cyclomatic complexity issue
}
private String getDetails(String input){
return "More details for the "+input;
}
}
我的问题是我如何模拟"this.getDetails(input)"以返回一些用于测试目的的字符串?
如果你有一个足够大和复杂的类,你需要模拟其中的一小部分,把它当作一个暗示,你违反了单一责任原则,并适当地划分类。如果使用依赖项注入,则可以提供所需的任何实现。
public class TestClass {
/**
* Computes a detail string based on an input. Supply this in the constructor
* for full DI, relax visibility, or add a setter.
*/
private final Function<String, String> detailFunction;
public String getDescription(String input){
String value = detailFunction.apply(input);
// ...
}
}
作为轻量级替代方案,您可以测试实际类的覆盖或间谍。
@Test public void testTestClassWithOverride() {
TestClass instanceUnderTest = new TestClass() {
@Override public String getDescription(String input) {
return "Predictable value";
}
};
// test your instanceUnderTest here
}
@Test public void testTestClassWithSpy() {
TestClass spyUnderTest = Mockito.spy(new TestClass());
doReturn("Predictable value").when(spyUnderTest).getDescription(anyString());
// test your spyUnderTest here
}
请记住,虽然这是您的一个选项,但它不应该是您的第一个选择:您不是测试您的实际类,而是测试它的一次性变体,并且您已经做到了其他消费者也可以子类化您的 TestClass。如果可能,将您需要的灵活性写入类本身,并将您的测试视为遵循相同规则的使用者。
首先,制作所谓的"部分模拟"是一种不好的做法。这说明您的代码不遵循单一责任原则,从而导致您的代码无法(或很难)测试。
我建议你从你的类中提取getDescription
方法,并通过依赖反转或更具体的依赖注入(例如通过使用 Spring 框架)间接使用它:
public class TestClass() {
private DetailsServiceProvider detailsServiceProvider;
public TestClass(DetailsServiceProvider detailsServiceProvider) {
this.detailsServiceProvider = detailsServiceProvider;
}
public String getDescription(String input) {
String value = detailsServiceProvider.getDetails(input); // i am not going to change this line, hence want to mock this.
//below this i have some complexity logic, which i would like to fix cyclomatic complexity issue
}
}
public interface DetailsServiceProvider {
String getDetails(String input);
}
public class DetailsServiceProviderImpl implements DetailsServiceProvider{
@Override
public String getDetails(String input) {
return "More details for the "+input;
}
}
然后在测试中,您可以简单地:
@Test
public void test() {
DetailsServiceProvider mockedProvider = Mockito.mock(DetailsServiceProvider.class);
//TODO: add scenarios for the mocked object
TestClass target = new TestClass(mockedProvider);
String description = target.getDescription();
//TODO: add assertions
}
如果您不想为首选方法而苦苦挣扎,则可以在Mockito中使用@Spy。这将创建您想要的 - 对象的部分模拟,其中部分方法将是真实的,另一部分 - 模拟:
@Test
public void test() {
TestClass partialMockedObject = Mockito.spy(new TestClass());
Mockito.doReturn("test details").when(partialMockedObject).getDetails();
String description = partialMockedObject.getDescription();
//TODO: add assertions
}
同样,此方法不是必需的,但如果没有给出其他选项,则可以使用此方法。请注意,这要求getDetails()
在测试中可见,这意味着private
修饰符在此处不起作用。