【问题标题】:Mockito junit testing not working- beans mocked by @mockBean are nullMockito junit 测试不起作用-@mockBean 模拟的 bean 为空
【发布时间】:2020-02-28 14:07:30
【问题描述】:

我正在尝试使用 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。)

这段代码有什么问题?

【问题讨论】:

  • @MockBean 是 spring 注释,@InjectMocks and @Mock 是 mockito 注释。 Mockito 本身对 spring 一无所知,为什么你的注释对象是null

标签: unit-testing junit mockito


【解决方案1】:

我建议您可以尝试这种方法,将@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() {
             
    }
}

【讨论】:

    【解决方案2】:

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

    我不是 mockito 专家,但我相信,您应该稍微重构 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 不能同时支持两者

    【讨论】:

    • 谢谢你,马克。虽然我更喜欢使用现场注入。我用过@Mock,它对我有用。
    【解决方案3】:

    改成之后可以试试运行测试吗

    @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 类中的字段,或@RunWith SpringRunner 的测试类。

    【讨论】:

    • 它对我不起作用。它给了我错误
    • 不知何故对我不起作用。它给了我这个错误:java.lang.Exception: No tests found matching [{ExactMatcher:fDisplayName=testFindResourceAvailability], {ExactMatcher:fDisplayName=testFindResourceAvailability(testmy.code.serivce. UserServiceImplTest)], {LeadingIdentifierMatcher:fClassName=UserServiceImplTest,fLeadingIdentifier=testFindUsers]]
    • 您的示例代码 sn-p 是否不完整。我没有看到测试 testFindResourceAvailability。我尝试了 testFindUsers 的示例测试用例,并且 requestMapper 不为空,而是模拟对象。
    • ```` java.lang.Exception: 没有找到匹配的测试 [{ExactMatcher:fDisplayName=testFindUsers], {ExactMatcher:fDisplayName=testFindUsers(testmy.code.serivce.UserServiceImplTest)], {LeadingIdentifierMatcher :fClassName=UserServiceImplTest,fLeadingIdentifier=testFindUsers]]```` - 抱歉不正确的部分。此时我得到空指针
    • 我观察到使用@ Mock 代替@ MockBean 对我有用。所有模拟实例都被创建并注入到被测类中。
    猜你喜欢
    • 2021-06-17
    • 1970-01-01
    • 2019-02-03
    • 1970-01-01
    • 2016-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多