@InjectMocks具有null依赖项

时间:2018-05-11 06:15:06

标签: java spring junit mocking mockito

我们目前正在使用JUnit和Mockito来测试我们的代码。

我们有一个类似于下面的服务接口。

public interface ServiceA {
     void doSomething();
}

它的实现类如下所示。

@Service
@Transactional
public class ServiceAImpl implements ServiceA {

     @Inject
     private RepositoryA repA;

     @Inject
     private ShareServiceA sharedServA;

     public void doSomething(){
     }
}

现在,我想模拟ServiceAImpl类的repA依赖。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:test-context.xml" })
@DirtiesContext(classMode= DirtiesContext.ClassMode.AFTER_CLASS)
public class ServiceAImplTest {

   @Mock
   RepositoryA repA;

   @InjectMocks
   ServiceA servA = new ServiceAImpl();

   @Before
   public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        ........
   }
}

调用initMocks后,只启动了ServiceImpl的repA依赖项,并且sharedServA保持为null,因此当被测试的类调用sharedServA方法时会导致null异常。

根据我在互联网和书籍上阅读的一些参考资料,只有在被测试的类具有声明参数的构造函数时才会发生这种情况。但是,对于我上面的例子,我没有声明任何构造函数。这是正确的行为还是我错过了什么?

4 个答案:

答案 0 :(得分:2)

你错过了什么,这就是对服务的嘲弄:

@Mock
private ShareServiceA sharedServA;

在您的测试课程中。在

@Mock
RepositoryA repA;

是注入模拟存储库的原因。

编辑:

既然你“不想模仿你的sharedServA”,也许你想要

@Spy
private ServiceAImpl sharedServA;

但这通常是不好的测试风格 - Mockito docs state that

  

应该谨慎使用真正的间谍,例如在处理遗留代码时。

答案 1 :(得分:2)

由于你想模拟一个bean但想拥有另一个bean的真实实例,你需要加载整个spring上下文,以便正确注入真正的bean,并且你必须将mocked bean注册到spring上下文中,这样它会被注入。

因此,在您的一个测试配置类中声明了一个RepositoryA的模拟bean。

@Bean
public void mockedRepositoryA() {
   return mock(RepositoryA.class);
}

test-context.xml中如下

<bean id="idOfServiceAHere" class="org.mockito.Mockito" factory-method="mock">
</bean>

然后通过@Inject注释注入它,就好像它是真正的bean一样。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:test-context.xml" })
@DirtiesContext(classMode= DirtiesContext.ClassMode.AFTER_CLASS)
public class ServiceAImplTest {

   // internally, you will have mocked instance of RepositoryA and
   // real instance of ShareServiceA.
   @Inject
   private ServiceA serviceA;

   @Before
   public void setUp() throws Exception {
        ....
   }
}

答案 2 :(得分:1)

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:test-context.xml" })
@DirtiesContext(classMode= DirtiesContext.ClassMode.AFTER_CLASS)
public class ServiceAImplTest {

   @Mock // not @mock
   RepositoryA repA;

   // need to mock each dependency of class under test
   @Mock
   ShareServiceA sharedServA;

   // no need to initialize via new
   @InjectMocks
   ServiceA servA;

   @Before
   public void setUp() throws Exception {
        // no need for this,
        // @RunWith(SpringJUnit4ClassRunner.class) will be enough
        // MockitoAnnotations.initMocks(this);
        ........
   }
}

查看代码中的评论。

如果您不想模拟ShareServiceA,您仍然可以模拟它并调用实际方法而不是返回模拟结果。

Mockito.when(sharedServA.someMethod(any())).thenCallRealMethod();

答案 3 :(得分:0)

您应该为ShareServiceA sharedServA创建@Mock 因为java中的任何对象都会创建构造函数默认值。

如果可以的话。请提供有关ShareServiceA的代码。