Java 如何在测试下模拟方法调用的方法
我正在处理遗留代码,并希望增加它的测试覆盖率 我有一门课,像:Java 如何在测试下模拟方法调用的方法,java,unit-testing,mocking,mockito,Java,Unit Testing,Mocking,Mockito,我正在处理遗留代码,并希望增加它的测试覆盖率 我有一门课,像: public class MyStrategy implements SomeStrategy { TimeAndDateUtils timeAndDateUtils = new TimeAndDateUtils(); @Override public boolean shouldBeExtracted(Argument argument) { Date currentDate = tim
public class MyStrategy implements SomeStrategy {
TimeAndDateUtils timeAndDateUtils = new TimeAndDateUtils();
@Override
public boolean shouldBeExtracted(Argument argument) {
Date currentDate = timeAndDateUtils.getCurrentDate();
return currentDate.isBefore(argument.getDate());
}
}
我想测试shouldBeExtracted方法,模拟调用timeAndDateUtils.getCurrentDate()
,以便它返回一些固定值
所以我想做的是:
Date currentDate = %some fixed date%
TimeAndDateUtils timeAndDateUtils = Mockito.mock(TimeAndDateUtils.class);
Mockito.when(timeAndDateUtils.getCurrentDate()).thenReturn(currentDate);
Assert.assertTrue(myStrategy.shouldBeExtracted(argument))
如何强制MyStrategy类使用模拟对象而不是创建自己的对象?您可以使用反射将模拟放入MyStrategy对象中。它看起来是这样的:
MyStrategy myStrategy = new MyStrategy(); // I don't know if you are using DI
MyStrategy.class.getDeclaredField("timeAndDateUtils").set(myStrategy, timeAndDateUtilsMock);
您可以使用反射将mock放入MyStrategy对象中。它看起来是这样的:
MyStrategy myStrategy = new MyStrategy(); // I don't know if you are using DI
MyStrategy.class.getDeclaredField("timeAndDateUtils").set(myStrategy, timeAndDateUtilsMock);
timeAndDateUtils
是在类本身中创建的,这使得测试访问变得困难。通过构造函数注入依赖项,以便可以创建模拟
public class MyStrategy implements SomeStrategy {
TimeAndDateUtils timeAndDateUtils;
public MyStrategy(TimeAndDateUtils timeAndDateUtils) {
this.timeAndDateUtils = timeAndDateUtils;
}
@Override
public boolean shouldBeExtracted(Argument argument) {
Date currentDate = timeAndDateUtils.getCurrentDate();
return currentDate.isBefore(argument.getDate());
}
}
试验
timeAndDateUtils
是在类本身中创建的,这使得测试访问变得困难。通过构造函数注入依赖项,以便可以创建模拟
public class MyStrategy implements SomeStrategy {
TimeAndDateUtils timeAndDateUtils;
public MyStrategy(TimeAndDateUtils timeAndDateUtils) {
this.timeAndDateUtils = timeAndDateUtils;
}
@Override
public boolean shouldBeExtracted(Argument argument) {
Date currentDate = timeAndDateUtils.getCurrentDate();
return currentDate.isBefore(argument.getDate());
}
}
试验
Mockito有一个很好的类用于重写类内的私有对象。它被称为白盒。它是这样工作的
MyStrategy myStrategy = new MyStrategy();
TimeAndDateUtils timeAndDateUtils = Mockito.mock(TimeAndDateUtils.class);
Whitebox.setInternalState(myStartegy, "timeAndDateUtils", timeAndDateUtilsMock);
这将更改您的模拟的TimeAndDateUtilsMockito有一个很好的类用于重写类内的私有对象。它被称为白盒。它是这样工作的
MyStrategy myStrategy = new MyStrategy();
TimeAndDateUtils timeAndDateUtils = Mockito.mock(TimeAndDateUtils.class);
Whitebox.setInternalState(myStartegy, "timeAndDateUtils", timeAndDateUtilsMock);
这将更改模拟的TimeAndDateUtils假设无法重写现有代码以使其更易于测试,这是注释的典型用例,允许将模拟或间谍字段自动注入测试对象
@RunWith(MockitoJUnitRunner.class)
public class MyStrategyTest {
@Mock
private TimeAndDateUtils timeAndDateUtils;
@InjectMocks
private MyStrategy myStrategy;
@Test
public void testShouldBeExtracted() {
...
Mockito.when(timeAndDateUtils.getCurrentDate()).thenReturn(currentDate);
Assert.assertTrue(myStrategy.shouldBeExtracted(argument));
}
}
假设您无法重写现有代码以使其更易于测试,这是注释的典型用例,允许将模拟或间谍字段自动注入测试对象
@RunWith(MockitoJUnitRunner.class)
public class MyStrategyTest {
@Mock
private TimeAndDateUtils timeAndDateUtils;
@InjectMocks
private MyStrategy myStrategy;
@Test
public void testShouldBeExtracted() {
...
Mockito.when(timeAndDateUtils.getCurrentDate()).thenReturn(currentDate);
Assert.assertTrue(myStrategy.shouldBeExtracted(argument));
}
}
这是一个旧的JavaSE应用程序,没有任何DIHello@Nicolas,我使用的是Mockito 1.9.x。我在其他项目中广泛使用InjectMock。甚至在询问堆栈溢出之前,我也在本例中尝试过它。由于某些原因,它不起作用,所以我想,这与这个项目中完全缺乏DI有关。但由于你的解释,它实际上应该是有效的。我再试试看。Thx,Iryna。我刚刚用Mockito
1.9.5
测试了完全相同的代码,它工作得很好。请确保用@RunWith(MockitoJUnitRunner.class)
Hello@Nicolas在用@InjectMocks注释myStrategy之后,我不能再模拟方法调用了。然后我得到一条错误消息:when()需要一个参数,必须是“模拟的方法调用”。
这是一个没有任何DIHello@Nicolas的旧Java SE应用程序,我使用的是Mockito 1.9.x。我在其他项目中广泛使用InjectMock。甚至在询问堆栈溢出之前,我也在本例中尝试过它。由于某些原因,它不起作用,所以我想,这与这个项目中完全缺乏DI有关。但由于你的解释,它实际上应该是有效的。我再试试看。Thx,Iryna。我刚刚用Mockito1.9.5
测试了完全相同的代码,它工作得很好。请确保用@RunWith(MockitoJUnitRunner.class)
Hello@Nicolas在用@InjectMocks注释myStrategy之后,我不能再模拟方法调用了。然后我得到一条错误消息:when()需要一个参数,该参数必须是“模拟的方法调用”。
该类是公共接口“SomeStrategy”的实现之一。我不喜欢只为其中一个更改构造函数。该类是公共接口“SomeStrategy”的实现之一。我不喜欢只为其中一个更改构造函数。这使得代码的可读性更差。谢谢你的建议!我发现@kimi82的解决方案更优雅了一点。谢谢你的建议!我发现@kimi82的解决方案更优雅了一点。虽然这会起作用,但这是一个糟糕的解决方案。相反,要测试的方法可以很容易地通过两个简单的测试来覆盖,传递一个参数
,该参数包含当前日期之前/之后的日期。不需要模拟。这样做,您将在同一测试中测试2段遗留代码。除非TimeAndDateUtils是已知框架/库的一部分,否则我不会依赖TimeAndDateUtils类,我会为这两个类创建互不依赖的测试。十年的经验(以及创建的数千个测试)告诉我,集成测试总是比独立的单元测试更好。两者,TDD和BDD用于确保质量从未使用过BDD,只有TDD。(BDD要求在非技术利益相关者的直接参与下创建测试,这在实践中几乎是不可能实现的。)虽然这会起作用,但这是一个糟糕的解决方案。相反,要测试的方法可以很容易地通过两个简单的测试来覆盖,传递一个参数
,该参数包含当前日期之前/之后的日期。不需要模拟。这样做,您将在同一测试中测试2段遗留代码。除非TimeAndDateUtils是已知框架/库的一部分,否则我不会依赖TimeAndDateUtils类,我会为这两个类创建互不依赖的测试。十年的经验(以及创建的数千个测试)告诉我,集成测试总是比独立的单元测试更好。两者,TDD和BDD用于确保质量从未使用过BDD,只有TDD。(BDD要求在非技术利益相关者的直接参与下创建测试,这在实践中几乎不可能实现。)