[英]Mockito mock returning Null value from repository
我正在尝试使用JUNIT5
测试我的服务实现。 我不太明白出了什么问题,但我的UserRepository
似乎没有返回值。
我已经测试过 UserRepository 是否被用于: verify(repository, times(1));
响应是“需要但未调用......实际上,与此模拟的交互为零”
这是测试 Class:
@ExtendWith(MockitoExtension.class)
public class UserServiceTest {
@Mock
UserRepository repository;
@InjectMocks
UserServiceImpl userServiceImpl;
UserModel inputUserModel;
@BeforeEach
public void setUp() throws Exception {
inputUserModel = new UserModel();
inputUserModel.setEmail("testemail@gmail.com");
inputUserModel.setFirstName("john");
inputUserModel.setLastName("doe");
inputUserModel.setPassword("test");
inputUserModel.setMatchPassword("test");
User inputUser = User.builder()
.email(inputUserModel.getEmail())
.firstName(inputUserModel.getFirstName())
.lastName(inputUserModel.getLastName())
.password(inputUserModel.getPassword())
.timeCreated(Timestamp.valueOf(LocalDateTime.now(ZoneId.systemDefault()))).userID(1)
.build();
User outputUser = User.builder().
email(inputUserModel.getFirstName()).
password(inputUserModel.getLastName()).
lastName(inputUserModel.getFirstName())
.firstName(inputUserModel.getFirstName())
.timeCreated(Timestamp.valueOf(LocalDateTime.now(ZoneId.systemDefault()))).userID(1).build();
Mockito.when(repository.save(inputUser)).thenReturn(outputUser);
}
@Test
public void whenSaveUser_ThenUserHasID(){
Assertions.assertEquals(1, userServiceImpl.saveUser(inputUserModel).getUserID());
}
}
我得到的错误:
org.mockito.exceptions.misusing.PotentialStubbingProblem:
Strict stubbing argument mismatch. Please check:
- this invocation of 'save' method:
repository.save(
User(userID=0, email=testemail@gmail.com, timeCreated=2022-12-14 03:18:24.0435578, password=test, firstName=john, lastName=doe)
);
-> at com.jschwery.securitydemo.service.Implementations.UserServiceImpl.saveUser(UserServiceImpl.java:37)
- has following stubbing(s) with different arguments:
1. repository.save(
User(userID=1, email=testemail@gmail.com, timeCreated=2022-12-14 03:18:24.0235563, password=test, firstName=john, lastName=doe)
);
我正在为其创建测试的服务 Class:
public class UserServiceImpl implements UserService {
UserRepository userRepository;
@Autowired
public UserServiceImpl(UserRepository repository){
this.userRepository = repository;
}
@Override
public User saveUser(UserModel userModel) {
if(!Objects.equals(userModel.getPassword(), userModel.getMatchPassword())){
throw new UserException("Passwords do not match");
}
User user = User.builder().
email(userModel.getEmail()).
firstName(userModel.getFirstName()).
lastName(userModel.getLastName()).
password(userModel.getPassword()).
timeCreated(Timestamp.valueOf(LocalDateTime.now(ZoneId.systemDefault()))).build();
User returnedUser = userRepository.save(user);
System.out.println(returnedUser.getEmail());
System.out.println("userID" + returnedUser.getUserID());
return returnedUser;
}
}
谢谢阅读: :)
我猜您的User
class 在userID
字段或所有字段上定义了一个equals
/ hashCode
。 当您在setUp
方法中模拟您的存储库时,您定义了当使用您指定的确切object 调用该方法时应该执行的操作,将给定的 object 与通过调用equals
/ hashCode
方法在模拟中指定的 object 进行比较。
您在setUp
方法中将User
userID
定义为具有用户 ID 1
,但在您的UserServiceImpl
中, userID
从未设置过(因为它是由持久层生成的)。 因此,永远不会调用equals
/ hashCode
检查以确定 Mockito 是否应该执行存根逻辑,因为UserServiceImpl
传递的 object 永远不会等于您在模拟中定义的那个。
有几种方法可以解决这个问题。
我的建议是将您的 Unit-Test 转换为@SpringBootTest
。 测试模拟行为没有任何价值。 如果此测试是集成测试,您将测试存储库是否将User
插入数据库并使用您的断言生成 ID。
如果你想模拟存储库,你应该为输入参数使用any()
匹配器,然后做存储库会做的事情,即设置 id:
when(repository.save(any(User.class))).thenAnswer(invocation -> {
final User entity = invocation.getArgument(0);
ReflectionTestUtils.setField(entity, "userID", RandomUtils.nextLong());
return entity;
});
然后我会断言, userID
不是 null,因为userID
将像在生产中一样随机生成:
@ExtendWith(MockitoExtension.class)
public class UserServiceTest {
@Mock
private UserRepository repository;
@InjectMocks
private UserServiceImpl userServiceImpl;
@Test
void saveUser_userHasID(){
// Arrange
final UserModel inputUserModel = new UserModel();
inputUserModel.setEmail("testemail@gmail.com");
inputUserModel.setFirstName("john");
inputUserModel.setLastName("doe");
inputUserModel.setPassword("test");
inputUserModel.setMatchPassword("test");
when(repository.save(any(User.class))).thenAnswer(invocation -> {
final User entity = invocation.getArgument(0);
ReflectionTestUtils.setField(entity, "userID", RandomUtils.nextLong());
return entity;
});
// Act
final User user = userServiceImpl.saveUser(inputUserModel);
// Assert
assertThat(user.getUserID()).isNotNull();
}
}
我强烈建议您使用 AssertJ 作为您的断言,因为它提供了流畅的断言和比 JUnit 更多的断言方法。
Spring 数据无法通过带有模拟的简单单元测试生成用户 ID,您将需要在集成测试中运行 spring 上下文。
有两个字段导致您遇到错误: UserID
和timeCreated
。
timeCreated
字段:在您的测试中,您创建了一个用于模拟的inputUser
object。 问题是这个 object inputUser
不是你的方法在测试saveUser(UserModel userModel)
下使用的那个你可以清楚地看到时间戳在两个对象之间的错误中不同。UserID
字段:您再次期望模拟使用inputUser
object。此 object 的 userID = 1。但在实际方法saveUser(UserModel userModel)
中,此字段未明确设置,因此默认值将设置为0
您也可以在错误中看到这一点。 现在,由于您的测试用例whenSaveUser_ThenUserHasID
旨在验证保存的记录是否具有 ID,因此您无法使用模拟来完成。 该 ID 由您正在使用的 Spring 数据生成,并且让它运行,您应该使用和集成测试在您的测试中运行您的 spring 上下文,以便您的 spring 数据能够为您的用户生成一个 ID, 这里是之前的一个问题将对您有所帮助。 有了这个,您既不需要测试中的inputUser
也不需要模拟。 (您可以使用来自@DataJpaTest
5 的 @DataJpaTest 或@SpringBootTest
...)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.