【问题标题】:Null Pointer Exception JUnit Mockito空指针异常 JUnit Mockito
【发布时间】:2021-04-03 09:23:55
【问题描述】:

我是 JUnit 和 Mockitio 的新手。当我运行下面的代码时,我得到, java.lang.NullPointerException:无法调用“org.springframework.http.ResponseEntity.getStatusCodeValue()”,因为“com.learnit.testing.MyController.getUser(java.lang.Long)”的返回值为 null

@Controller
@RequestMapping("/api")
public class MyController {

    @Autowired
    private MyService service;

    @GetMapping("/")
    @ResponseBody
    public ResponseEntity<Object> getUser(Long id) throws Exception {
        return service.myResponse(id);
    }
}

@Service
public class MyService {
    
    @Autowired
    private MyRepository repository;

    public ResponseEntity<Object> myResponse(Long id) throws Exception{

        MyData data=repository.findById(id).orElse(null);
        if(data!=null)
            return new ResponseEntity<>(HttpStatus.OK);
        return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);

    }
}

测试

@ExtendWith(MockitoExtension.class)
public class MyTest {

    @InjectMocks
    private MyController controller;
    
    @Mock
    private MyRepository repo;
    
    @Mock
    private MyService service;
    
    @Test
    public void checkService() throws Exception {
        when(repo.findById((long)1)).thenReturn(null);
        assertEquals(controller.getUser((long)1).getStatusCodeValue(), 500);
        
    }
}

【问题讨论】:

  • 请注意,我认为让服务返回ResponseEntity 是非常糟糕的风格。我建议让服务返回一个 Optional&lt;MyData&gt; 并让控制器将其转换为 ResponseEntity,具体取决于它是否为空 (404) 或不 (OK)。

标签: java spring-boot junit mockito


【解决方案1】:

有几个问题:

  1. 您没有存根MyService#myResponse 方法的行为。您需要模拟 MyService 实例,因为这是 MyController 实例的直接依赖项。因此,MyRepository 不需要在此测试用例中进行模拟(除非您出于某种原因想要对其方法进行存根,但我在当前代码中没有这样做)。我建议您替换以下内容:
        when(repo.findById((long)1)).thenReturn(null);

MyService 模拟实例的存根行为:

        when(service.myResponse(1L)).thenReturn(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));

执行此操作时,可以删除 MyRepository 测试套件模拟实例,因为它不再需要。

  1. 检查你的导入,基于@ExtendWith注解的使用我假设你在这里使用JUnit 5。确保您实际使用来自org.junit.jupiter.api 的其他类(例如@TestAssertions)。默认情况下,JUnit 不支持与 JUnit 4 的向后兼容性。不过,我想保持兼容性,您应该在测试运行时路径中包含 junit-vintage-engine 工件。

P.S 你需要考虑你真正想要测试的类,它决定了哪些实例应该被模拟。

【讨论】:

    【解决方案2】:

    恕我直言,您需要在测试类中添加一个 @RunWith(some.mockito.TestSuiteIDontRememberName.class) 注释。 @Mock 注解需要一个测试套件来搜索注解并收集信息以在其中注入值。

    如果我是你,我会首先尝试在 @Before 方法中初始化模拟实例并查看测试是否运行......然后,我会阅读 Mockito(对不起,但我通常使用 @987654325 @) 库文档,并检查 @Mock 注释是如何使用的(我可以告诉你你有机会成为问题)

    您的测试代码中存在问题...您在调用方法 findById() 时告诉模拟实例返回 null,所以很可能如果发生这种情况,您将在 main 中获得 NPE代码,尝试使用.orelse() 方法时。可能您不知道要返回什么,或者您需要返回一个同样模拟的对象实例......但因此,如果不实现与您的意图完全不同的东西,就不可能修复您的代码。

    无论如何,您已经在代码中消除了足够多的东西使其无法测试,而在此过程中您根本没有包括如何调用要测试的方法。

    请阅读页面 How to create a Minimal, Reproducible Example 并编辑您的问题以包含可测试的代码,显示 NPE,这样我们至少可以看到它是如何以及在何处发生的。

    【讨论】:

    • JUnit5 使用ExtendWith 注释而不是RunWith
    • 还有模拟实例注入到带有@AutowiredSpring注解的测试类中吗?恕我直言,他还有许多其他问题,例如告诉模拟对象将null 返回给调用者,并且他正在获得一个NPE....是与他获得的null 值相关的NPE。代码是不可测试的……所以用我们掌握的信息很难说什么。
    • 是的,@InjectMocks 应该创建被测主题,并将标有@Mock 的适用字段“自动连接”到其中。
    猜你喜欢
    • 2019-05-24
    • 2021-06-15
    • 2016-08-19
    • 2021-06-30
    • 1970-01-01
    • 2015-10-07
    • 1970-01-01
    • 2019-04-09
    • 1970-01-01
    相关资源
    最近更新 更多