Spring mvc 使用Mockito时如何清理spring测试中的mock
我对莫基托很陌生,在清理方面遇到了一些麻烦 我曾经使用JMock2进行单元测试。据我所知,JMock2将期望和其他模拟信息保存在一个上下文中,该上下文将为每个测试方法重建。因此,每种试验方法都不会受到其他方法的干扰 在使用JMock2时,我对spring测试采用了相同的策略,我发现我在中使用的策略存在一个潜在问题:为每个测试方法重建应用程序上下文,因此会减慢整个测试过程 我注意到许多文章推荐在spring测试中使用Mockito,我想尝试一下。在我在一个测试用例中编写两个测试方法之前,它工作得很好。每个测试方法在单独运行时都通过了测试,如果它们一起运行,其中一个将失败。我推测这是因为模拟信息保存在模拟本身中(“因为我在JMock中没有看到任何类似的上下文对象”),并且模拟(和应用程序上下文)在两种测试方法中都是共享的 我通过在@Before方法中添加reset()解决了这个问题。我的问题是,处理这种情况的最佳实践是什么(reset()的javadoc说,如果需要reset(),代码是有味道的)?任何想法都将不胜感激,提前感谢Spring mvc 使用Mockito时如何清理spring测试中的mock,spring-mvc,mockito,spring-test,Spring Mvc,Mockito,Spring Test,我对莫基托很陌生,在清理方面遇到了一些麻烦 我曾经使用JMock2进行单元测试。据我所知,JMock2将期望和其他模拟信息保存在一个上下文中,该上下文将为每个测试方法重建。因此,每种试验方法都不会受到其他方法的干扰 在使用JMock2时,我对spring测试采用了相同的策略,我发现我在中使用的策略存在一个潜在问题:为每个测试方法重建应用程序上下文,因此会减慢整个测试过程 我注意到许多文章推荐在spring测试中使用Mockito,我想尝试一下。在我在一个测试用例中编写两个测试方法之前,它工作得很
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
"file:src/main/webapp/WEB-INF/booking-servlet.xml",
"classpath:test-booking-servlet.xml" })
@WebAppConfiguration
public class PlaceOrderControllerIntegrationTests implements IntegrationTests {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Autowired
private PlaceOrderService placeOrderService;
@Before
public void setup() {
this.mockMvc = webAppContextSetup(this.wac).build();
reset(placeOrderService);// reset mock
}
@Test
public void fowardsToFoodSelectionViewAfterPendingOrderIsPlaced()
throws Exception {
final Address deliveryAddress = new AddressFixture().build();
final String deliveryTime = twoHoursLater();
final PendingOrder pendingOrder = new PendingOrderFixture()
.with(deliveryAddress).at(with(deliveryTime)).build();
when(placeOrderService.placeOrder(deliveryAddress, with(deliveryTime)))
.thenReturn(pendingOrder);
mockMvc.perform(...);
}
@Test
public void returnsToPlaceOrderViewWhenFailsToPlaceOrder() throws Exception {
final Address deliveryAddress = new AddressFixture().build();
final String deliveryTime = twoHoursLater();
final PendingOrder pendingOrder = new PendingOrderFixture()
.with(deliveryAddress).at(with(deliveryTime)).build();
NoAvailableRestaurantException noAvailableRestaurantException = new NoAvailableRestaurantException(
deliveryAddress, with(deliveryTime));
when(placeOrderService.placeOrder(deliveryAddress, with(deliveryTime)))
.thenThrow(noAvailableRestaurantException);
mockMvc.perform(...);
}
您不应该注入
placeOrderService
对象,而应该在每次测试之前让Mockito将其初始化为@Mock
,方法如下:
@Mock private PlaceOrderService placeOrderService;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
正如Javadoc中推荐的:
您甚至可以将@Before
方法放在一个超类中,只需为使用@Mock
对象的每个测试用例类扩展它
@DirtiesContext(classMode=classMode.AFTER\u每个测试方法)
。这会导致测试速度变慢,内存消耗增加,但这是我们最好的选择。使用此技巧,您不必重置Mockito mock旧答案: 我不太喜欢在spring conxtext中使用Mockito Mock。但是,您是否可能正在寻找类似于:
@After public void reset_mocks() {
Mockito.reset(placeOrderService);
}
基于Spring的测试很难做到快速和独立(如@Brice)。下面是一个用于重置所有模拟的litle实用程序方法(您必须在每个
@Before
方法中手动调用它):
正如您看到的,所有bean都有一个迭代,检查bean是否为mock,并重置mock。我特别关注callAopUtils.isAopProxy
和((建议)bean).getTargetSource().getTarget()
。如果您的bean包含一个@Transactional
注释,那么这个bean的模拟总是由spring包装到代理对象中,以便重置或验证
import org.mockito.Mockito;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.ApplicationContext;
public class MyTest {
public void resetAll(ApplicationContext applicationContext) throws Exception {
for (String name : applicationContext.getBeanDefinitionNames()) {
Object bean = applicationContext.getBean(name);
if (AopUtils.isAopProxy(bean) && bean instanceof Advised) {
bean = ((Advised)bean).getTargetSource().getTarget();
}
if (Mockito.mockingDetails(bean).isMock()) {
Mockito.reset(bean);
}
}
}
}
@MockBean
private PlaceOrderService placeOrderService;
@SpringBootTest
public class MyServiceTest {
@MockInBean(MyService.class)
private ServiceToMock serviceToMock;
@Autowired
private MyService myService;
@Test
public void test() {
Mockito.when(serviceToMock.returnSomething()).thenReturn(new Object());
myService.doSomething();
}
}
public static void resetMocks(ApplicationContext context) {
for ( String name : context.getBeanDefinitionNames() ) {
Object bean = context.getBean( name );
if (new MockUtil().isMock( bean )) {
Mockito.reset( bean );
}
}
}