你好,我是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.UserDetailsServiceImplTest中 [错误] testLoadUserByUsername经过的时间:0.111 s <<<错误! java.lang.NullPointerException 在com.example.kanban.UserDetailsServiceImplTest.testLoadUserByUsername(UserDetailsServiceImplTest.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)
来使错误消失(我的尝试已被注释掉),但是我无法处理此问题。
答案 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进行测试,但我不建议这样做。
除了我的回答,我也对您有一些建议:
全面了解您的框架。它们不只是魔术。了解spring的工作原理以及内部的Autowired注释。
如果您是依赖注入的新手,我强烈建议this blog post来解释基础知识。
您已经收到了一些注释,可以提示您解决方案。我相信他们不仅通过提供答案,还试图帮助您自己找到问题,以帮助您。
如果您需要使用Mockito测试Spring Boot项目单元的示例,则可以检查this repository。
我希望这会有所帮助。