【问题标题】:NullPointer Error when trying to use method in test尝试在测试中使用方法时出现 NullPointer 错误
【发布时间】:2020-07-31 08:40:29
【问题描述】:

您好,我是 Junit 新手,我目前正在使用 Junit 5 为此类编写单元测试:

@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();
    }
}

下面是一个测试类:

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);
    }
}

我明白了:

[错误] 测试运行:1,失败:0,错误:1,跳过:0,经过时间:5.108 秒

    [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) 方法返回Optional&lt;User&gt; user 以使错误消失(我的尝试已被注释掉),但我无法处理这个问题。

【问题讨论】:

  • 我看不出你到底在哪里出错,但你可能无法很好地自动装配 userRepository。你能查一下stackoverflow.com/questions/45470877/…
  • 您确定userRepository 正在自动连线吗?您能否提供更多的堆栈跟踪信息?
  • 你至少需要@RunWith(SpringRunner.class) @SpringBootTest(classes = {....}) 你在你的测试类中使用了这些注解吗?
  • @Shoshi 我已经添加了这些注释,但我仍然得到同样的错误
  • @Ascalonian 当我运行应用程序时它工作正常,所以我确定 userRepository 正在自动装配

标签: java spring spring-boot junit


【解决方案1】:

如果您自己创建UserDetailsServiceImpl 的实例并且不要让Spring 做它- 不会注入任何东西。这就是您在 @BeforeAll 方法中所做的,因为您使用的是基于字段的注入,您看不到这一点。

如果你想继续使用这种注入方法,你需要让 spring 在你的测试中注入 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 建议的那样,您需要阅读有关框架和依​​赖注入的更多信息。还有关于单元测试。

【讨论】:

    【解决方案2】:

    欢迎使用 Stackoverflow。

    UserDetailsServiceImpl 中的UserRepository 不会在单元测试中自动装配/注入,因为没有初始化弹簧上下文。您可以使用模拟测试这种依赖关系。你可以检查mockito。或者,您也可以尝试初始化 Spring Context 以进行测试,但我不建议这样做。

    除了我的回答之外,我还有几个建议:

    1. 彻底了解您的框架。它们不仅仅是魔法。了解 spring 的工作原理以及 Autowired 注释的工作原理。

    2. 如果您不熟悉依赖注入,我强烈推荐this blog post,它解释了基础知识。

    3. 您已经获得了一些提示您解决方案的 cmets。我相信他们不仅通过提供答案来帮助您,而且还通过帮助您自己找到问题来帮助您。

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

    我希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-08-27
      • 2014-04-30
      • 1970-01-01
      • 2015-12-10
      • 2021-07-27
      • 2019-11-24
      • 1970-01-01
      相关资源
      最近更新 更多