【发布时间】:2018-01-11 03:56:53
【问题描述】:
我正在尝试为 Spring Boot 中的服务编写一个简单的单元测试。
该服务调用存储库上的一个方法,该方法返回一个用户实例。 我正在尝试模拟存储库,因为我只想测试服务。
所以,Repository的代码:
public interface UserRepository extends MongoRepository<User, String> {
User findByEmail(String email);
}
服务接口:
public interface UserService {
@Async
CompletableFuture<User> findByEmail(String email) throws InterruptedException;
}
服务实现:
@Service
public class UserServiceImpl implements UserService {
private UserRepository userRepository;
// dependency injection
// don't need Autowire here
// https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-spring-beans-and-dependency-injection.html
public UserServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Async
public CompletableFuture<User> findByEmail(String email) throws InterruptedException {
User user = userRepository.findByEmail(email);
return CompletableFuture.completedFuture(user);
}
}
单元测试:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTest {
@InjectMocks
UserService userService;
@Mock
UserRepository mockUserRepository;
@Before
public void setUp() {
MockitoAnnotations.initMock(this);
}
@Test
public void mustReturnUser() throws InterruptedException {
String emailTest = "foo@bar.com";
User fakeUser = new User();
fakeUser.setEmail(emailTest);
when(mockUserRepository.findByEmail(emailTest)).thenReturn(fakeUser);
User user = userService.findByEmail(emailTest).join();
assertThat(user).isEqualTo(fakeUser);
verify(mockUserRepository).findByEmail(emailTest);
}
}
当我运行这个测试时,我得到了一个MockitoException:
org.mockito.exceptions.base.MockitoException:
Cannot instantiate @InjectMocks field named 'userService'.
...
Caused by: org.mockito.exceptions.base.MockitoException: the type 'UserService' is an interface.
我没有使用接口,而是尝试使用真正的实现;像这样改变测试:
@InjectMocks
UserServiceImpl userService;
现在,测试成功通过,但这似乎不对(至少对我而言)。 我喜欢测试 Spring Boot 正在使用的 UserService(假设在我的系统的新版本中,我实现了一个新的 UserServicePostgreSQLImpl - 现在我正在使用 MongoDB)。 (编辑:见问题底部的编辑)
我将单元测试更改如下:
@Autowired
@InjectMocks
UserService userService;
但现在我测试失败了:
Expected :model.User@383caf89
Actual :null
由于某种原因,当我使用 @Autowired 时,UserRepository 模拟不起作用。
如果我将emailTest 更改为在我的数据库中使用真实的电子邮件,
测试通过。
当我使用@Autowired时,
测试使用的是真正的UserRepository,而不是UserRepository 的Mock 版本。
有什么帮助吗?
编辑:查看@msfoster 和@dunni 的答案,思考得更好,我相信正确的方法是测试每个实现(在我的示例中,使用UserServiceImpl userService)。
【问题讨论】:
-
你不是在测试实际的实现,是你说的吗?重点是测试你的接口实现。
-
这正是我要说的!如果在测试中我使用
UserServiceImpl userService,我可以测试真正的实现,但是如果我创建另一个 userService 实现,我将需要编写一个新的单元测试(好吧,这也是正确的),但我喜欢只测试Spring 正在使用的实现 -
请问您为什么要这样做?似乎测试的目的不是测试服务,而是您的应用程序的一个方面。
-
您想对实现进行单元测试。如果您稍后添加
UserServicePostgreSQLImpl,您也会为该类添加另一个单元测试。如果你想编写一个集成测试,在运行时进行依赖注入,不要使用@InjectMocks,而只使用@Inject或@Autowired,将实现注入你的测试类。然后,您可以使用@MockBean将您的存储库的模拟添加到 Spring 上下文中。 -
@msfoster 和 dunni ,是的,两者都是正确的。现在想得更好,我必须测试所有实现。关于单元测试和集成测试的这件事对我来说是新的!谢谢! (对我来说,这个问题可以结束了!)
标签: java spring unit-testing