Mockito Junit测试不起作用-@mockBean模拟的Bean为空

时间:2019-11-02 17:17:56

标签: unit-testing junit mockito

我正在尝试Mockito进行模拟和单元测试。

尝试使用@MockBean模拟自动装配的Bean。但是bean在运行时为空。

正在测试的课程。

@Service
public class UserServiceImpl  {

  @Autowired
  GenericRestClient restClient;

  @Autowired
  RequestMapper requestMapper;

  @Autowired
  ResponseMapper responseMapper;

  @Override
  public List<User> findUsers(RRequest requestBody,
      String index) throws HostException {
List<User> users= 
requestMapper.mapRequest("test");
// ...doing something
return users;
}

测试类:


import static org.junit.Assert.assertNotNull;

import java.util.ArrayList;
import java.util.List;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.boot.test.mock.mockito.MockBean;

@RunWith(MockitoJUnitRunner.class)

public class UserServiceImplTest {

    @MockBean
    GenericRestClient restClient;

    @MockBean
    RequestMapper requestMapper;

    @MockBean
    ResponseMapper responseMapper;

    @InjectMocks
    UserServiceImpl userService;

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

    }

    @Test
    public void testFindUsers() {
List<Users> users = null;
        Mockito.when(requestMapper.mapRequest("test"))
        .thenReturn(users); 
        assertNull(users);      
    }
}

错误:

java.lang.NullPointerException
    at test.my.code.UserServiceImplTest.testFindUsers(UserServiceImplTest.java:10)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:79)
    at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:85)
    at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
    at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:163)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)


我调试过,NullPointerException是由于requestMapper(使用MockBean照​​原样null模拟)。

此代码有什么问题?

3 个答案:

答案 0 :(得分:1)

与@abinmichael不同,我不建议使用Spring Runner来运行此测试。您的测试是单元测试,而运行Spring(创建应用程序上下文)是非常昂贵的操作,更适合于集成测试。

我不是模拟专家,但是我相信,您应该稍微重构UserServiceImpl以便可以看到依赖关系:

@Service
public class UserServiceImpl  {


 private final GenericRestClient restClient;
 private final RequestMapper requestMapper;
 private final ResponseMapper responseMapper;

 @Autowired // in recent spring version, this annotation can be omitted 
 public UserServiceImpl(GenericRestClient restClient, RequestMapper requestMapper, ResponseMapper responseMapper) {
    this.restClient = restClient;
    this.requestMapper = requestMapper;
    this.responseMapper = responseMapper;    
 }
 ...

采用这种方法,@InjectMocks中不再需要:

@RunWith(MockitoJUnitRunner.class)

public class UserServiceImplTest {

@Mock
GenericRestClient restClient;

@Mock
RequestMapper requestMapper;

@Mock
ResponseMapper responseMapper;


UserServiceImpl userService;

@Before 
public void init() {
  userService = new UserServiceImpl(restClient, requestMapper, responseMapper);
}

....
}

如果您坚持使用字段注入,请阅读The accepted answer以获取有关@InjectMocks的工作方式的更多信息。也许您没有“专用”访问修饰符,也许您在构造函数注入和字段注入之间有些混用,@InjectMocks不同时支持两者

答案 1 :(得分:0)

更改为

后,您可以尝试运行测试吗?
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceImplTest {

javadoc https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/mock/mockito/MockBean.html 状态

 @Target(value={TYPE,FIELD})
 @Retention(value=RUNTIME)
 @Documented
 @Repeatable(value=MockBeans.class)
public @interface MockBean

可用于将模拟添加到Spring ApplicationContext的注释。可以用作类级别的注释,也可以用作@Configuration类或@strongWith SpringRunner的测试类中的字段。

答案 2 :(得分:0)

我建议您可以尝试这种方法,将 @InjectMocks 用于 test target,将 @Mock 用于该服务内的注入类。

无需使用 @Before,因为您使用了字段注入。

@RunWith(SpringRunner.class)
@SpringBootTest(classes = YourMainClass.class)
public class UserServiceImplTest {

    @Mock
    GenericRestClient restClient;

    @Mock
    RequestMapper requestMapper;

    @Mock
    ResponseMapper responseMapper;

    @InjectMocks
    UserServiceImpl userService

    @Test
    public void testFindUsers() {
             
    }
}