繁体   English   中英

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

[英]How to prevent Spring from injecting @Autowired references inside a mock?

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

假设我的班级引用了Service:

@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也会尝试将存储库注入模拟中

这是我所做的。 测试类别:

@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方法,生成了NullPointerException

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

谁可以帮我这个事?

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

@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指出的那样:

    [依赖项的实例字段]可以是最终值,因此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();
    }

}

使用接口,尤其是在使用某种AOP(事务,安全性等)的情况下,例如,您将拥有接口MyService和类MyServiceImpl

在配置中,您将具有:

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

您应该将@InjectMocks批注放入控制器中,并将@Mock放入服务中,如下所示:

@Autowired
@InjectMocks
private MyController myController;
@Autowired
@Mock
private MyService myService;

@Before
public void setup() {
    MockitoAnnotations.initMocks(this);
}

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM