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 应该模拟)

问题是你与“现在”耦合,所以服务总是在它运行的那一刻有时间。 处理时间的最佳方法之一是对概念 ClockTimeProvider 进行建模并将其注入到服务中。然后你可以模拟它来断言测试中的时间。

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