如何防止Spring在模拟中注入@Autowired引用?

Tho*_*ulé 2 java junit spring unit-testing mockito

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

假设我的班级引用了一项服务:

@Controller
public class MyController 
{

    @Autowired
    private MyService service;

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

    public void doSomething() {
        service.create();
    }
}
Run Code Online (Sandbox Code Playgroud)

此服务引用了一个存储库:

@Service
public class MyService {

    @Autowired
    private MyRepository repository;

    public void whatever() {}

    public void create() {
        repository.save();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

这就是我做的.测试类:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { MyControllerTestConfiguration.class })
public class MyControllerTest {

    @Autowired
    private MyController myController;

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

}
Run Code Online (Sandbox Code Playgroud)

配置类:

@Configuration
public class MyControllerTestConfiguration {

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

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

}
Run Code Online (Sandbox Code Playgroud)

我得到的错误: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [test.test.MyRepository] found for dependency

我尝试使用Mockito的@InjectMocks注释来初始化模拟但是这会失败,因为在模拟@PostConstruct注入之前调用该方法,生成一个NullPointerException.

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

谁可以帮我这个事?

hzp*_*zpz 6

使用构造函数而不是字段注入.这使得测试变得更加容易.

@Service
public class MyService {

    private final MyRepository repository;

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

    public void whatever() {}

    public void create() {
        repository.save();
    }
}
Run Code Online (Sandbox Code Playgroud)

-

@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();
    }
}
Run Code Online (Sandbox Code Playgroud)

这有几个好处:

  • 你的测试中不需要Spring.这允许您进行适当的单元测试.它还使测试速度极快(从几秒到几毫秒).
  • 你不能意外地创建一个没有依赖关系的类的实例,这会产生一个NullPointerException.
  • 正如@NamshubWriter指出:

    [依赖项的实例字段]可以是final,因此1)它们不会被意外修改,2)任何读取该字段的线程都将读取相同的值.

丢弃@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();
    }

}
Run Code Online (Sandbox Code Playgroud)