@RunWith(SpringRunner.class)与@RunWith(MockitoJUnitRunner.class)的区别

57
我之前在使用mockito进行junit测试时,使用了@RunWith(MockitoJUnitRunner.class)。但现在我正在开发一个spring boot应用程序,想要使用@RunWith(SpringRunner.class)。使用@RunWith(SpringRunner.class)相对于@RunWith(MockitoJUnitRunner.class)是否有任何优势?我还能否在使用@RunWith(SpringRunner.class)的情况下使用@Injectmock@Mock@Spy等功能呢?
5个回答

56

SpringRunner 支持加载 Spring ApplicationContext 并将 @Autowired 的 bean 注入到测试实例中。它实际上做了更多的事情(在 Spring 参考手册中有介绍),但这是基本的想法。

MockitoJUnitRunner 则提供支持,用于使用 Mockito 创建 mocks 和 spies。

然而,在 JUnit 4 中,你一次只能使用一个 Runner

因此,如果您想同时使用 Spring 和 Mockito 的支持,则只能选择其中的一个 Runner。

但是现在很幸运,因为除了runner之外,Spring 和 Mockito 都提供了rule

例如,您可以按以下方式将 Spring runner 与 Mockito rule 结合使用。

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {

    @Rule
    public MockitoRule rule = MockitoJUnit.rule();

    @Mock
    MyService myService;

    // ...
}

通常情况下,如果您正在使用Spring Boot并需要模拟Spring ApplicationContext中的bean,则应使用Spring Boot的@MockBean支持而不是简单地使用@Mock


如何将 @RunWith(SpringRunner.class) 替换为类似 MockitoJUnit 的 Rule? - GOXR3PLUS
3
使用SpringClassRuleSpringMethodRule:https://docs.spring.io/spring/docs/5.1.2.RELEASE/spring-framework-reference/testing.html#testcontext-junit4-rules - Sam Brannen
还有一件事 :) 我可以将其与PowerMock规则结合使用或与PowerMock同步运行吗 :) ? - GOXR3PLUS
1
我不使用PowerMock,所以很遗憾我无法回答那个问题。 - Sam Brannen
例如,在测试Spring-Data接口时,我使用@MockBean,但在测试JPA查询实现类时,我使用@Mock - djangofan

15

当使用SpringRunner.class时,Spring提供了相应的注释:

  • @MockBean
  • @SpyBean

通过@Autowired注释将Mocks注入到测试对象中。 要启用此功能,测试必须带有以下注释之一:

  • @SpringBootTest

或者

  • @TestExecutionListeners(MockitoTestExecutionListener.class)

更多详细信息和示例可以在官方文档中找到:Mocking and Spying Beans


1
模拟对象无法使用Autowired进行注入,您应该使用MockBean。 - voipp
2
@voipp模拟对象可以使用Autowired注入到被测试的对象中,但是MockBean用于定义要注入的模拟对象。 - VaL

5
根据JavaDoc的说明:
SpringRunner是SpringJUnit4ClassRunner的别名。要使用这个类,只需在基于JUnit 4的测试类上注释@RunWith(SpringRunner.class)即可。如果你想使用Spring TestContext框架与其他运行器一起使用,可以使用org.springframework.test.context.junit4.rules.SpringClassRule和org.springframework.test.context.junit4.rules.SpringMethodRule。
而TestContext的JavaDoc如下:
TestContext封装了测试执行的上下文,与实际使用的测试框架无关。
getApplicationContext()方法的JavaDoc如下:
获取此测试上下文的应用程序上下文,可能会被缓存。该方法的实现负责加载应用程序上下文(如果相应的上下文尚未加载),并可能缓存上下文。
因此,SpringRunner确实加载上下文并负责维护它。例如,如果您想将数据持久化到嵌入式数据库中,比如H2内存数据库,您必须使用SpringRunner.class;为了在每个测试后清除表格以摆脱插入的记录,您需要使用@DirtiesContext注释测试,以告诉Spring进行清理。
但是,这已经是一个集成或组件测试了。如果您的测试是纯单元测试,或者只想验证依赖项的某个方法是否被调用,那么MockitoJUnit4Runner就足够了。您只需随意使用@Mock和Mockito.verify(...),测试就会通过。而且速度更快。
测试应该快速。尽可能快。因此,只要可能,就使用MockitoJUnit4Runner来加速它。

1
SpringRunner does not load any context it just enable spring boot features like @Autowire, @MockBean, etc.. during junit testing. If you want to load the context you'd rather use @SpringBootTest - Hamza Belmellouki

1

场景 2019年11月:Spring Boot:2.1.1.RELEASE

  • 您拥有一个Spring Boot API Rest
  • 您需要测试名为MySuperSpringService的服务
  • 但是,在MySuperSpringService内部,需要两个更多的自动装配。一个用于执行SQL选择(MyJpaRepository),另一个用于调用外部API Rest(MyExternalApiRest
@Service
public class MySuperSpringService {

  @Autowired
  private MyRepository innerComponent1;

  @Autowired
  private MyExternalApiRest innerComponent2;

  public SomeResponse doSomething(){}
}

如何测试 MySuperSpringService 中模拟数据库和外部 API Rest?

为了测试这个服务 MySuperSpringService,需要使用 @MockBean 来模拟另外两个 Spring Beans: MyJpaRepository 和 MyExternalApiRest,并根据需要创建结果。

import static org.mockito.Mockito.when;
import java.io.IOException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;

import junit.framework.TestCase;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = your.package.Application.class)
public class MySuperSpringServiceTest extends TestCase {

  @Autowired
  private MySuperSpringService serviceToTest;

  @MockBean
  private MyRepository myRepository;

  @MockBean
  private MyExternalApiRest myExternalApiRest;

  @Before
  public void setUp()  {

    Object myRepositoryResult = new Object();
    //populate myRepositoryResult as you need
    when(myRepository.findByClientId("test.apps.googleusercontent.com"))
        .thenReturn(myRepositoryResult);

    Object myExternalApiRestResult = new Object();
    //populate myExternalApiRestResult as you need
    when(myExternalApiRest.listUserRoles("john@doe.com")).thenReturn(myExternalApiRestResult);

  }

  @Test
  public void testGenerateTokenByGrantTypeNoDatabaseNoGoogleNoSecurityV1(){
    SomeResponse response = serviceToTest.doSomething();
    //put your asserts here
  }

}

几个问题...需要使用 @SpringBootTest 吗?我们是否需要使用 @MockBean?这个可以简单地使用 @Mock 来完成吗? - satish marathe
@SpringBootTest对于测试非常重要!@Mock适用于简单的Java类。如果目标类是一个带有多个@Autowired注入的Spring组件,则@MockBean是您唯一的选择! - JRichardsz
@JRichardsz 我不这么认为。你可以模拟Spring组件的依赖关系。 - Sudip Bolakhe

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接