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

时间:2015-07-10 18:06:48

标签: 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也会尝试在模拟中注入存储库

这就是我所做的。考试类:

@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

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

有人可以帮我吗?

3 个答案:

答案 0 :(得分: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();
    }
}

-

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

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

}

答案 1 :(得分:1)

使用接口,特别是如果您使用某种AOP(交易,安全性等),即您将拥有接口MyService和类MyServiceImpl

在配置中,您将拥有:

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

答案 2 :(得分:-1)

你应该把@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();
}