我正在尝试为Spring Boot中的服务编写一个简单的单元测试。
该服务调用存储库上的方法,该存储库返回User的实例。 我正在尝试模拟存储库,因为我只想测试服务。
所以,存储库的代码:
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
,而不是Mock
版本的UserRepository
。
任何帮助?
编辑:查看@msfoster和@dunni的答案,并且思考得更好,我相信正确的方法是测试每个实现(在我的示例中,使用UserServiceImpl userService
)。
答案 0 :(得分:3)
为了让UserServiceImpl在使用@InjectMocks注释时自动装配,那么它需要注册为Spring bean本身。您可以通过使用@Service注释UserServiceImpl类来最简单地完成此任务。
这将确保在Spring启动配置中通过组件扫描获取它。 (只要扫描包含您的服务类所在的包!)
答案 1 :(得分:1)
您正在使用SpringRunner
运行测试,但对于模拟,您并不需要弹簧上下文。请尝试以下代码
// Using mockito runner
@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {
@Mock
UserRepository mockUserRepository;
// Mockito will auto inject mockUserRepository mock to userService via constructor injection
@InjectMocks
UserService userService;
@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);
}
}
答案 2 :(得分:0)
这只是@Yogesh Badke答案的变体。
虽然你在运行时使用spring, 在单元测试期间不需要使用弹簧。 代替, 您可以模拟所有依赖项并在测试设置期间将它们设置为模拟 (使用反射或设置器,如果你有它们)。
以下是一些示例代码:
import org.springframework.test.util.ReflectionTestUtils;
public class TestUserService
{
private static final String VALUE_EMAIL = "test email value";
private UserService classToTest;
@Mock
private User mockUser;
@Mock
private UserRepository mockUserRepository;
@Before
public void beforeTest()
{
MockitoAnnotations.initMock(this);
classToTest = new UserService();
doReturn(mockUser).when(mockUserRepository).findByEmail(VALUE_EMAIL);
ReflectionTestUtils.setField(
classToTest,
"userRepository",
mockUserRepository);
}
@Test
public void findByEmail_goodEmailInput_returnsCorrectUser()
{
final User actualResult;
actualResult = classToTest.findByEmail(VALUE_EMAIL);
assertSame(
mockUser,
actualResult);
}
}
答案 3 :(得分:0)
如果接口由多个类实现,则使用Junit beans.xml文件中的限定符名称(下面的示例)来运行相应的Junit测试用例。
示例:
@Autowired
@Qualifier("animal")
private Animal animals;
在Junit beans.xml中
<bean id="animal" class="com.example.abc.Lion"/>
其中Lion是Interface Animal的实现类。
答案 4 :(得分:0)
您需要为实现类@InjectMocks
。不是接口类。
示例:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTest {
@Mock
UserRepository mockUserRepository;
@InjectMocks
UserServiceImpl userServiceImpl; ------> This is important
}