尝试在测试中使用方法时出现NullPointer错误

时间:2020-04-17 18:42:32

标签: java spring spring-boot junit

你好,我是Junit的新手,我目前正在使用Junit 5为该课程编写单元测试:

<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="myApp">
  <div ng-controller="AppCtrl">
    <select ng-options="t.id as t.label for t in currentOptions | orderBy:updateOptionOrder" class="form-control" style="width:50%" ng-model="field"></select>
    <hr>
    <p>{{field}}</p>
  </div>
</div>

下面是一个测试类:

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
        Optional<User> user = userRepository.findByEmail(email);

        user.orElseThrow(() -> new UsernameNotFoundException("Not found: " + email));

        return user.map(UserDetailsImpl::new).get();
    }
}

我得到:

[错误]测试运行:1,失败:0,错误:1,跳过:0,经过的时间:5.108 s <<<失败! -在com.example.kanban.UserDetailsS​​erviceImplTest中 [错误] testLoadUserByUsername经过的时间:0.111 s <<<错误! java.lang.NullPointerException 在com.example.kanban.UserDetailsS​​erviceImplTest.testLoadUserByUsername(UserDetailsS​​erviceImplTest.java:55)

class UserDetailsServiceImplTest {

    private static UserDetailsServiceImpl userDetailsServiceImpl;

    String email;
    /*User mockedUser;
    UserRepository userRepository;
    UserDetails userDetails;*/

    @BeforeAll
    static void setup() {
        userDetailsServiceImpl = new UserDetailsServiceImpl();
    }

    @Test
    void testLoadUserByUsername() {
        /*userRepository = mock(UserRepository.class);
        mockedUser = mock(User.class);

        Optional<User> user = Optional.of(mockedUser);

        when(userRepository.findByEmail(email)).thenReturn(user);
        assertEquals(userRepository.findByEmail(email), user);*/

        UserDetails userDetails = userDetailsServiceImpl.loadUserByUsername(email);
        assertTrue(userDetails instanceof UserDetails);
    }
}

我试图使 [INFO] Running com.example.kanban.WebSecurityConfigTest [INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0 s - in com.example.kanban.WebSecurityConfigTest 2020-04-18 13:36:56.910 INFO 9180 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor' 2020-04-18 13:36:56.910 INFO 9180 --- [extShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default' 2020-04-18 13:36:56.918 INFO 9180 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated... 2020-04-18 13:36:56.934 INFO 9180 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed. [INFO] [INFO] Results: [INFO] [ERROR] Errors: [ERROR] UserDetailsServiceImplTest.testLoadUserByUsername:55 » NullPointer [INFO] [ERROR] Tests run: 4, Failures: 0, Errors: 1, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD FAILURE [INFO] ------------------------------------------------------------------------ [INFO] Total time: 8.708 s [INFO] Finished at: 2020-04-18T13:36:57+02:00 [INFO] ------------------------------------------------------------------------ [ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.22.2:test (default-test) on project kanban: There are test failures. 方法返回userRepository.findByEmail(email)来使错误消失(我的尝试已被注释掉),但是我无法处理此问题。

2 个答案:

答案 0 :(得分:3)

如果您自己创建UserDetailsServiceImpl的实例,并且不让Spring进行,则不会注入任何内容。这就是您在@BeforeAll方法中所做的事情,因为您使用的是基于字段的注入,因此看不到。

如果要继续使用这种注射方法,则需要在测试中让弹簧注射UserDetailsServiceImpl

要这样做,您需要向测试班级中添加@SpringBootTest

@SpringBootTest
class UserDetailsServiceImplTest {

    @Autowired
    private UserDetailsServiceImpl userDetailsServiceImpl;
}

但是,似乎您想模拟UserRespository(对于单元测试,这是最佳选择),因此最简单,最干净的方法是更新注入方式和使用构造函数注入(有关更多信息,请参见documentation

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    private final UserRepository userRepository;
    public UserDetailsServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository
    }
    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
        Optional<User> user = userRepository.findByEmail(email);

        user.orElseThrow(() -> new UsernameNotFoundException("Not found: " + email));

        return user.map(UserDetailsImpl::new).get();
    }
}

并像简单的单元测试一样更改您的测试:

class UserDetailsServiceImplTest {

    private UserDetailsServiceImpl userDetailsServiceImpl;
    private UserRepository userRepository;

    @BeforeEach
    void setup() {
        userRepository = mock(UserRepository.class)
        userDetailsServiceImpl = new UserDetailsServiceImpl(userRepository);
    }

    @Test
    void testLoadUserByUsername() {
        mockedUser = mock(User.class);

        Optional<User> user = Optional.of(mockedUser);

        when(userRepository.findByEmail(email)).thenReturn(user);
        assertEquals(userRepository.findByEmail(email), user);

        UserDetails userDetails = userDetailsServiceImpl.loadUserByUsername(email);
        assertTrue(userDetails instanceof UserDetails);
    }
}

就像@bhdrkn一样,您通常需要阅读有关框架和依​​赖项注入的更多信息。还有单元测试。

答案 1 :(得分:1)

欢迎使用Stackoverflow。

UserRepository中的

UserDetailsServiceImpl不会在单元测试中自动接线/注入,因为没有初始化spring上下文。您可以使用模拟来测试这种依赖性。您可以对此进行检查。另外,您也可以尝试初始化Spring Context进行测试,但我不建议这样做。

除了我的回答,我也对您有一些建议:

  1. 全面了解您的框架。它们不只是魔术。了解spring的工作原理以及内部的Autowired注释。

  2. 如果您是依赖注入的新手,我强烈建议this blog post来解释基础知识。

  3. 您已经收到了一些注释,可以提示您解决方案。我相信他们不仅通过提供答案,还试图帮助您自己找到问题,以帮助您。

  4. 如果您需要使用Mockito测试Spring Boot项目单元的示例,则可以检查this repository

我希望这会有所帮助。