我有以下课程:
public class MyClass {
private Apple apple;
public void myMethod() {
apple = AppleFactory.createInstance(someStringVariable);
....
....
....
}
}
测试类:
@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
@InjectMocks
MyClass myClass;
@Test
public void myMethod(){
...
...
...
}
}
如何在MyClass中将Apple实例注入模拟?
答案 0 :(得分:21)
您有3种可能性来解决这个问题:
抽象工厂:使用具体的工厂类,而不是使用静态方法:
public abstract class AppleFactory {
public Apple createInstance(final String str);
}
public class AppleFactoryImpl implements AppleFactory {
public Apple createInstance(final String str) { // Implementation }
}
在您的测试类中,模拟工厂:
@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
@Mock
private AppleFactory appleFactoryMock;
@Mock
private Apple appleMock;
@InjectMocks
MyClass myClass;
@Before
public void setup() {
when(appleFactoryMock.createInstance(Matchers.anyString()).thenReturn(appleMock);
}
@Test
public void myMethod(){
...
...
...
}
}
PowerMock :使用PowerMock创建静态方法的模拟。看my answer to a relevant question看看它是如何完成的。
可测试类:将Apple
创建包装在protected
方法中并创建一个覆盖它的测试类:
public class MyClass {
private Apple apple;
public void myMethod() {
apple = createApple();
....
....
....
}
protected Apple createApple() {
return AppleFactory.createInstance(someStringVariable);
}
}
@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
@Mock
private Apple appleMock;
@InjectMocks
MyClass myClass;
@Test
public void myMethod(){
...
...
...
}
private class TestableMyClass extends MyClass {
@Override
public void createApple() {
return appleMock;
}
}
}
当然,在测试课程中,您应该测试TestableMyClass
而不是MyClass
。
我会告诉你我对每种方法的看法:
抽象工厂方法是最好的 - 这是一个隐藏实现细节的清晰设计
可测试类 - 是需要最少更改的第二个选项
PowerMock
选项是我最不喜欢的选项 - 您可以忽略并隐藏您的问题,而不是寻求更好的设计。但这仍然是一个有效的选择。答案 1 :(得分:2)
关于Avi& amp;的第一个答案Ev0oD。抽象类只能扩展而不能实现。
public abstract class AppleFactory {
public abstract Apple createInstance(final String str);
}
public class AppleFactoryImpl extends AppleFactory {
public Apple createInstance(final String str) { // Implementation }
}
答案 2 :(得分:0)
除了Avi提出的解决方案之外,您还可以选择第四种可能性:
注入工厂: 对我来说,当你已经拥有refacrot代码时,这是最好的选择。使用此解决方案,您不必更改引用代码,只需更改工厂类和测试。
public class AppleFactory
{
private static Apple _injectedApple;
public static createInstance(String str)
{
if (_injectedApple != null)
{
var currentApple = _injectedApple;
_injectedApple = null;
return currentApple;
}
//standard implementation
}
public static setInjectedApple(Apple apple)
{
_injectedApple = apple;
}
}
现在您可以简单地使用静态工厂:
@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
@Mock
private Apple appleMock;
@InjectMocks
MyClass myClass;
@Before
public void setup() {
AppleFactory.setInjectedApple(appleMock);
}
@Test
public void myMethod(){
...
...
...
}
}