@RunWith(SpringRunner.class) 与@RunWith(MockitoJUnitRunner.class)

@RunWith(SpringRunner.class) vs @RunWith(MockitoJUnitRunner.class)

我在使用 mockito 进行 junit 测试时使用 @RunWith(MockitoJUnitRunner.class)。但现在我正在使用 spring 引导应用程序并尝试使用 @RunWith(SpringRunner.class) 。使用 @RunWith(SpringRunner.class) 是否比使用 @RunWith(MockitoJUnitRunner.class) 有任何优势?我还能使用 @Injectmock@Mock@Spy@RunWith(SpringRunner.class)

等功能吗

使用SpringRunner.class时,Spring提供对应的注解:

  • @MockBean
  • @SpyBean

模拟通过 @Autowired 注释注入到被测对象。要启用此功能测试必须用

注释
  • @SpringBootTest

  • @TestExecutionListeners(MockitoTestExecutionListener.class)

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

SpringRunner 支持加载 Spring ApplicationContext 并将 bean @Autowired 加载到您的测试实例中。 它实际上做的远不止这些(在 Spring 参考手册中介绍),但这是基本思想。

然而,MockitoJUnitRunner 支持使用 Mockito 创建模拟和间谍。

但是,对于 JUnit 4,您一次只能使用一个 Runner

因此,如果您想同时使用 Spring 和 Mockito 的支持,您只能选择 一个 的跑步者。

但你很幸运,因为除了 runners 之外,Spring 和 Mockito 还提供 rules

例如,您可以使用 Spring runner 和 Mockito 规则,如下所示。

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

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

    @Mock
    MyService myService;

    // ...
}

不过,通常情况下,如果您正在使用 Spring 启动并且需要从 Spring ApplicationContext 中模拟一个 bean,您会然后使用 Spring Boot 的 @MockBean 支持而不是简单地 @Mock.

根据 JavaDoc:

SpringRunner is an alias for the SpringJUnit4ClassRunner. To use this class, simply annotate a JUnit 4 based test class with @RunWith(SpringRunner.class). If you would like to use the Spring TestContext Framework with a runner other than this one, use org.springframework.test.context.junit4.rules.SpringClassRule and org.springframework.test.context.junit4.rules.SpringMethodRule.

以及 TestContext 的 JavaDoc:

TestContext encapsulates the context in which a test is executed, agnostic of the actual testing framework in use.

方法getApplicationContext():

Get the application context for this test context, possibly cached. Implementations of this method are responsible for loading the application context if the corresponding context has not already been loaded, potentially caching the context as well.

因此,SpringRunner 确实加载上下文并负责维护它。例如,如果你想将数据持久化到嵌入式数据库中,比如 H2 内存数据库,你必须使用 SpringRunner.class;并且,要清理表格以删除每次测试后插入的记录,您可以使用 @DirtiesContext 注释测试以告诉 Spring 清理它。

但是,这已经是一个集成或组件测试。如果你的测试是纯单元测试,你不必加载数据库,或者你只是想验证某个依赖的某个方法是否被调用,MockitoJUnit4Runner 就足够了。您只需使用 @MockMockito.verify(...) 即可通过测试。而且速度快了很多。

测试应该很快。尽可能快地。因此,只要有可能,请使用 MockitoJUnit4Runner 来加快速度。

您完全可以将 SpringRunner 用于单元测试和集成测试。 SpringRunner Tutorial

2019 年 11 月场景:spring-引导:2.1.1.RELEASE

  • 你有一个spring启动api休息
  • 您需要测试名为 MySuperSpringService
  • 的服务
  • 但是,在 MySuperSpringService 内部,还需要两个自动装配。一个执行 sql select (MyJpaRepository) 另一个调用外部 api rest (MyExternalApiRest)
@Service
public class MySuperSpringService {

  @Autowired
  private MyRepository innerComponent1;

  @Autowired
  private MyExternalApiRest innerComponent2;

  public SomeResponse doSomething(){}
}

如何测试 MySuperSpringService 模拟数据库和外部 api 休息?

因此,为了测试需要另一个 spring beans 的服务 MySuperSpringService:MyJpaRepository 和 MyExternalApiRest,您需要使用 @ 模拟它们MockBean 并根据需要创建结果。

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
  }

}