java - 如何防止 Spring 在模拟中注入(inject) @Autowired 引用?

标签 java spring unit-testing junit mockito

我想使用 Spring + JUnit + Mockito 测试一个类,但我无法让它正常工作。

假设我的类引用了一个服务:

@Controller
public class MyController 
{

    @Autowired
    private MyService service;

    @PostConstruct
    public void init() {
        service.whatever();
    }

    public void doSomething() {
        service.create();
    }
}

该服务引用了一个存储库:
@Service
public class MyService {

    @Autowired
    private MyRepository repository;

    public void whatever() {}

    public void create() {
        repository.save();
    }
}

在测试 MyController 类时,我希望模拟该服务。问题是:即使服务被模拟,Spring 也会尝试将存储库注入(inject)模拟 .

这就是我所做的。测试类:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { MyControllerTestConfiguration.class })
public class MyControllerTest {

    @Autowired
    private MyController myController;

    @Test
    public void testDoSomething() {
        myController.doSomething();
    }

}

配置类:
@Configuration
public class MyControllerTestConfiguration {

    @Bean
    public MyController myController() {
        return new MyController();
    }

    @Bean
    public MyService myService() {
        return Mockito.mock(MyService.class);
    }

}

我得到的错误是:org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [test.test.MyRepository] found for dependency
我尝试使用 Mockito 的 @InjectMocks 初始化模拟。注释,但这失败了,因为 @PostConstruct在模拟注入(inject)之前调用方法,生成 NullPointerException .

而且我不能简单地模拟存储库,因为在现实生活中这会让我模拟很多类......

谁可以帮我这个事?

最佳答案

使用构造函数而不是字段注入(inject)。这使测试变得容易得多。

@Service
public class MyService {

    private final MyRepository repository;

    @Autowired
    public MyService(MyRepository repository) {
        this.repository = repository;
    }

    public void whatever() {}

    public void create() {
        repository.save();
    }
}

-
@Controller
public class MyController {

    private final MyService service;

    @Autowired
    public MyController(MyService service) {
        this.service = service;
    }

    @PostConstruct
    public void init() {
        service.whatever();
    }

    public void doSomething() {
        service.create();
    }
}

这有几个优点:
  • 您的测试中不需要 Spring。这可以让你做正确的单位测试。它还使测试非常快(从几秒到几毫秒)。
  • 您不能意外地创建一个没有依赖关系的类的实例,这会导致 NullPointerException .
  • 正如@NamshubWriter 指出的那样:

    [The instance fields for the dependencies] can be final, so 1) they cannot be accidentally modified, and 2) any thread reading the field will read the same value.


  • 丢弃@Configuration上课并写一个这样的测试:
    @RunWith(MockitoJUnitRunner.class)
    public class MyControllerTest {
    
        @Mock
        private MyRepository repository;
    
        @InjectMocks
        private MyService service;
    
        @Test
        public void testDoSomething() {
            MyController myController = new MyController(service);
            myController.doSomething();
        }
    
    }
    

    关于java - 如何防止 Spring 在模拟中注入(inject) @Autowired 引用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31347477/

    相关文章:

    java - 在运行时检索上下文配置

    java - Spring Rest Hibernate 一对多/多对一

    java - 一对多关系的问题

    java - 如何处理与微服务无关的类对象?

    javascript - VueJS 单元测试 "What to Test`“,属性内容?

    javascript - 测试一个简单的指令

    java - 我是否以正确的方式使用模拟?

    java - Codility - 功能改变

    java - RxJava 2 & Retrofit 2 顺序独立调用

    java - JUnit5 - @CsvSource - 将源代码转换为 POJO