Hoe kan ik java.time.LocalDate.now() bespotten

In mijn testcase heb ik een testtijdgevoelige methode nodig, in die methode gebruiken we java 8-klasse LocalDate, het is niet Joda.

Wat kan ik doen om de tijd te veranderen als ik een test doe


Antwoord 1, autoriteit 100%

Vervang in uw code LocalDate.now()door LocalDate.now(clock);.

U kunt dan Clock.systemDefaultZone()doorgeven voor productie en een vaste klokom te testen.


Dit is een voorbeeld:

Injecteer eerst de Clock. Als je springboot gebruikt, doe dan gewoon een:

@Bean
public Clock clock() {
    return Clock.systemDefaultZone();
}

Ten tweede, bel LocalDate.now(clock)in uw code :

@Component
public class SomeClass{
    @Autowired
    private Clock clock;
    public LocalDate someMethod(){
         return LocalDate.now(clock);
    }
}

Nu, binnen je unit-testklasse:

// Some fixed date to make your tests
private final static LocalDate LOCAL_DATE = LocalDate.of(1989, 01, 13);
// mock your tested class
@InjectMocks
private SomeClass someClass;
//Mock your clock bean
@Mock
private Clock clock;
//field that will contain the fixed clock
private Clock fixedClock;
@Before
public void initMocks() {
    MockitoAnnotations.initMocks(this);
    //tell your tests to return the specified LOCAL_DATE when calling LocalDate.now(clock)
    fixedClock = Clock.fixed(LOCAL_DATE.atStartOfDay(ZoneId.systemDefault()).toInstant(), ZoneId.systemDefault());
    doReturn(fixedClock.instant()).when(clock).instant();
    doReturn(fixedClock.getZone()).when(clock).getZone();
}
@Test
public void testSomeMethod(){
    // call the method to test
    LocalDate returnedLocalDate = someClass.someMethod();
    //assert
    assertEquals(LOCAL_DATE, returnedLocalDate);
}

Antwoord 2, autoriteit 3%

Je kunt je code herstructureren om het testvriendelijk te maken, bijvoorbeeld door alle aanroepen van LocalDate.now()te vervangen door een methode van een aangepaste mockable niet-statische klasse.

Als alternatief kunt u mockStatic van PowerMockgebruiken.


Antwoord 3, autoriteit 2%

Als we statische methoden zoals now() moeten bespotten, kunnen we meerdere alternatieven gebruiken, zoals PowerMock:

@RunWith(PowerMockRunner.class)
@PrepareForTest({ LocalDateTime.class })
public class LocalDateTimeUnitTest {
    @Test
    public void givenLocalDateTimeMock_whenNow_thenGetFixedLocalDateTime() {
        Clock clock = Clock.fixed(Instant.parse("2014-12-22T10:15:30.00Z"), ZoneId.of("UTC"));
        LocalDateTime dateTime = LocalDateTime.now(clock);
        mockStatic(LocalDateTime.class);
        when(LocalDateTime.now()).thenReturn(dateTime);
        String dateTimeExpected = "2014-12-22T10:15:30";
        LocalDateTime now = LocalDateTime.now();
        assertThat(now).isEqualTo(dateTimeExpected);
    }
}

Of JMockit, inderdaad met JMockit kunnen we de MockUp-klasse gebruiken:

@Test
public void givenLocalDateTimeWithJMock_whenNow_thenGetFixedLocalDateTime() {
    Clock clock = Clock.fixed(Instant.parse("2014-12-21T10:15:30.00Z"), ZoneId.of("UTC"));
    new MockUp<LocalDateTime>() {
        @Mock
        public LocalDateTime now() {
            return LocalDateTime.now(clock);
        }
    };
    String dateTimeExpected = "2014-12-21T10:15:30";
    LocalDateTime now = LocalDateTime.now();
    assertThat(now).isEqualTo(dateTimeExpected);
}

Of de klasse Verwachtingen:

@Test
public void givenLocalDateTimeWithExpectations_whenNow_thenGetFixedLocalDateTime() {
    Clock clock = Clock.fixed(Instant.parse("2014-12-23T10:15:30.00Z"), ZoneId.of("UTC"));
    LocalDateTime dateTimeExpected = LocalDateTime.now(clock);
    new Expectations(LocalDateTime.class) {
        {
            LocalDateTime.now();
            result = dateTimeExpected;
        }
    };
    LocalDateTime now = LocalDateTime.now();
    assertThat(now).isEqualTo(dateTimeExpected);
}

We kunnen hiermeer voorbeelden vinden.

Een ander eenvoudig alternatief is het gebruik van de now()methode met een vaste Clock-instantie. Zeker, de meeste klassen in het java.time-pakket hebben een now()-methode met een Clock-parameter:

@Test
public void givenFixedClock_whenNow_thenGetFixedLocalDateTime() {
    Clock clock = Clock.fixed(Instant.parse("2014-12-22T10:15:30.00Z"), ZoneId.of("UTC"));
    String dateTimeExpected = "2014-12-22T10:15:30";
    LocalDateTime dateTime = LocalDateTime.now(clock);
    assertThat(dateTime).isEqualTo(dateTimeExpected);
}

Antwoord 4

Misschien wilt u ook een vaste klok in productie doorgeven (waarvan de waarde wordt vastgesteld aan het begin van een transactie) om te voorkomen dat u inconsistent “nu” gebruikt in verschillende entiteiten en verzoeken.
Zie deze vraagvoor details.


Antwoord 5

We moeten hier een statische methode bespotten. Ik gebruik de volgende afhankelijkheid. Onthoud dat al onze testcode in het try-blok moet staan. Zodra we LocalDate.now() of LocalDate

. aanroepen

<!-- https://mvnrepository.com/artifact/org.mockito/mockito-inline -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-inline</artifactId>
    <version>3.11.0</version>
    <scope>test</scope>
</dependency>

De code:

@Test
    void verifykNonLeapYear() {
        LocalDate currentLocalDate = LocalDate.of(2010, 2, 13);
        try (MockedStatic<LocalDate> topDateTimeUtilMock = Mockito.mockStatic(LocalDate.class)) {
            topDateTimeUtilMock.when(() -> LocalDate.now()).thenReturn(currentLocalDate);
            assertThat(TopDateTimeUtil.numberOfDaysInCurrentYear(), is(365));
        }
    }

Antwoord 6

Lente gebruiken:

ClockConfiguration-klasse:

@Configuration
public class ClockConfiguration {
    private final static LocalDate LOCAL_DATE = LocalDate.of(2019, 12, 17);
    @Bean
    @ConditionalOnMissingBean
    Clock getSystemDefaultZoneClock() {
        return Clock.systemDefaultZone();
    }
    @Bean
    @Profile("test")
    @Primary
    Clock getFixedClock() {
        return Clock.fixed(LOCAL_DATE.atStartOfDay(ZoneId.systemDefault()).toInstant(), ZoneId.systemDefault());
    }
}

SomeService-klasse:

@Service
@RequiredArgsConstructor
public class SomeService {
    private final Clock clock;
    public void someMethod(){
        ...
        LocalDateTime.now(clock)
        LocalDate.now(clock)
        ...
    }
}

U moet een actief “test”-profiel hebben in de test:

SomeServiceTest-klasse:

@ActiveProfiles("test")
@EnableConfigurationProperties
@SpringBootTest(classes = [YourAppMainClass])
class SomeServiceTest {
    ...
}

Antwoord 7

Je kunt de leverancier binnen je klas die je aan het testen bent, gebruiken om de huidige tijd te halen, waar de datum-tijd ook wordt gebruikt.

public Supplier<LocalDateTime> localDateTime = () -> LocalDateTime.now();

en in de testmethode overschrijf je gewoon de waarde zoals :

myClassObj.localDateTime = () -> LocalDateTime.parse("2020-11-24T23:59:59.999");

Antwoord 8

Je kunt de huidige datum gewoon als functieparameter doorgeven.

fun doSmthng(now: LocalDate = LocalDate.now()) {
    testAnotherService.doSmthng1(TestObj("my message!", now))
}

En in de test kun je een specifieke datum doorgeven en erop beweren. Het idee is om de afhankelijkheden te injecteren in plaats van expliciet te creëren in de hoofdtekst van de functie.

Other episodes