Spring 中的服务未使用 Junit 设置值
Service in Spring not setting values with Junit
我正在使用 Junit 测试我的服务,但结果不是预期的。
当我保存我的实体时,服务中未设置 return 日期。
测试:
@Test
@DisplayName("Should set determined time for return date")
public void shouldSetReturnDate() {
ClientDTORequest dto = createNewDTOClient();
Client client = createNewClient();
Mockito.when(clientRepository.save(Mockito.any())).thenReturn(client);
Client saved = clientService.save(dto);
Assertions.assertEquals(dateTimeNow.plusMinutes(30), saved.getReturnDate());
}
我的 createNewClient():
private Client createNewClient() {
//the null param is the return date
return new Client(1L, "name", null);
}
我的服务:
public Client save(ClientDTORequest dto) {
Client client = mapper.map(dto, Client.class);
client.setReturnDate(dateTimeNow.plusMinutes(30));
Client savedClient = clientRepository.save(client);
return savedClient;
}
而当测试结果:
org.opentest4j.AssertionFailedError:
Expected :2022-04-04T01:17:25.715895900
Actual :null
结果没有被服务传递给模拟,这是我的镜头,但我不知道为什么。
谢谢!
如果你用 @Autowired 注入你的 clientRepository 那么它不会模拟。试试@SpyBean
(@Autowired ClientRepository clientRepository 不会模拟;@SpyBean ClientRepository clientRepository 应该模拟)
问题是你与“现在”耦合,所以服务总是在它运行的那一刻有时间。
处理时间的最佳方法之一是对概念 Clock
或 TimeProvider
进行建模并将其注入到服务中。然后你可以模拟它来断言测试中的时间。
class Clock {
LocalDateTime now() {
return LocalDateTime.now().plusMinutes(30); // <-- as you needs
}
}
class Service {
private Clock clock;
Service(Clock clock) {
this.clock = clock;
}
void save(MyEntity entity) {
entity.setCreatedDateTime(clock.now());
//repositoty.save(entity);
}
}
@Getter
class MyEntity {
private LocalDateTime createdDateTime;
public void setCreatedDateTime(LocalDateTime createdDateTime) {
//assing it to a field
this.createdDateTime = createdDateTime;
}
}
class ServiceTest {
@Mock
private Clock clock;
private Service service;
@Test
void testSave() {
LocalDateTime fixedDateTimeNow = LocalDateTime.of(2022, 4, 3, 18, 0, 0);
Mockito.when(clock.now()).thenReturn(fixedDateTimeNow);
MyEntity entity = new MyEntity();
service.save(entity);
Assertions.assertEquals(fixedDateTimeNow, entity.getCreatedDateTime());
}
}
注意:在您的服务中保持状态时要小心,因为它不是线程安全的。因此,当多个服务调用“同时”发生时,您最终会遇到并发问题。
经过几个小时的测试,我发现了问题:
我的服务正在更改数据,但被我的模拟覆盖:
Mockito.when(clientRepository.save(Mockito.any())).thenReturn(client); <-- mock overridden the changed data from service
Client saved = clientService.save(dto);
所以我找到了 ArgumentCaptor,在那里我可以从方法调用中获取对象:
宣布俘虏:
@Captor
ArgumentCaptor<Client> clientCaptor;
使用测试方法:
Mockito.when(clientRepository.save(clientCaptor.capture())).thenReturn(client); //<-- capturing the result
clientService.save(dto);
Client saved = clientCaptor.getValue() //getting object
Assertions.assertEquals(dto.getReturnDate().plusMinutes(30), saved.getReturnDate()); //assertion
我正在使用 Junit 测试我的服务,但结果不是预期的。 当我保存我的实体时,服务中未设置 return 日期。
测试:
@Test
@DisplayName("Should set determined time for return date")
public void shouldSetReturnDate() {
ClientDTORequest dto = createNewDTOClient();
Client client = createNewClient();
Mockito.when(clientRepository.save(Mockito.any())).thenReturn(client);
Client saved = clientService.save(dto);
Assertions.assertEquals(dateTimeNow.plusMinutes(30), saved.getReturnDate());
}
我的 createNewClient():
private Client createNewClient() {
//the null param is the return date
return new Client(1L, "name", null);
}
我的服务:
public Client save(ClientDTORequest dto) {
Client client = mapper.map(dto, Client.class);
client.setReturnDate(dateTimeNow.plusMinutes(30));
Client savedClient = clientRepository.save(client);
return savedClient;
}
而当测试结果:
org.opentest4j.AssertionFailedError:
Expected :2022-04-04T01:17:25.715895900
Actual :null
结果没有被服务传递给模拟,这是我的镜头,但我不知道为什么。
谢谢!
如果你用 @Autowired 注入你的 clientRepository 那么它不会模拟。试试@SpyBean (@Autowired ClientRepository clientRepository 不会模拟;@SpyBean ClientRepository clientRepository 应该模拟)
问题是你与“现在”耦合,所以服务总是在它运行的那一刻有时间。
处理时间的最佳方法之一是对概念 Clock
或 TimeProvider
进行建模并将其注入到服务中。然后你可以模拟它来断言测试中的时间。
class Clock {
LocalDateTime now() {
return LocalDateTime.now().plusMinutes(30); // <-- as you needs
}
}
class Service {
private Clock clock;
Service(Clock clock) {
this.clock = clock;
}
void save(MyEntity entity) {
entity.setCreatedDateTime(clock.now());
//repositoty.save(entity);
}
}
@Getter
class MyEntity {
private LocalDateTime createdDateTime;
public void setCreatedDateTime(LocalDateTime createdDateTime) {
//assing it to a field
this.createdDateTime = createdDateTime;
}
}
class ServiceTest {
@Mock
private Clock clock;
private Service service;
@Test
void testSave() {
LocalDateTime fixedDateTimeNow = LocalDateTime.of(2022, 4, 3, 18, 0, 0);
Mockito.when(clock.now()).thenReturn(fixedDateTimeNow);
MyEntity entity = new MyEntity();
service.save(entity);
Assertions.assertEquals(fixedDateTimeNow, entity.getCreatedDateTime());
}
}
注意:在您的服务中保持状态时要小心,因为它不是线程安全的。因此,当多个服务调用“同时”发生时,您最终会遇到并发问题。
经过几个小时的测试,我发现了问题:
我的服务正在更改数据,但被我的模拟覆盖:
Mockito.when(clientRepository.save(Mockito.any())).thenReturn(client); <-- mock overridden the changed data from service
Client saved = clientService.save(dto);
所以我找到了 ArgumentCaptor,在那里我可以从方法调用中获取对象:
宣布俘虏:
@Captor
ArgumentCaptor<Client> clientCaptor;
使用测试方法:
Mockito.when(clientRepository.save(clientCaptor.capture())).thenReturn(client); //<-- capturing the result
clientService.save(dto);
Client saved = clientCaptor.getValue() //getting object
Assertions.assertEquals(dto.getReturnDate().plusMinutes(30), saved.getReturnDate()); //assertion