Mock LocalDate.now (часы) с помощью Clock.fixed () - PullRequest
2 голосов
/ 09 июля 2020

Я изо всех сил пытаюсь протестировать свою конечную точку, когда устанавливаю конкретную c дату.

Я не хочу использовать PowerMock для имитации метода c stati, вместо этого я решил изменить реализацию моей службы и использовать реализацию LocalDate.now (Clock clock), чтобы ее было легче протестировать.

Я добавил в свой класс SpringBootApplication:

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

и автоматически подключил его к my Service

@Autowired
private Clock clock;

и использовал его в своей реализации следующим образом:

LocalDateTime localDate = LocalDateTime.now(clock);

На тестовой стороне я издевался над Clock

private final static LocalDate WEEKEND = LocalDate.of(2020, 07, 05);

@Mock
private Clock clock;
private Clock fixedClock;

и использовал его как что:

MockitoAnnotations.initMocks(this);

//tell your tests to return the specified LOCAL_DATE when calling LocalDate.now(clock)
fixedClock = Clock.fixed(WEEKEND.atTime(9, 5).toInstant(ZoneOffset.UTC), ZoneId.of("CET"));
doReturn(fixedClock.instant()).when(clock).instant();
doReturn(fixedClock.getZone()).when(clock).getZone();

ResponseEntity<String> response = restTemplate.postForEntity(base.toString(), request, String.class);

Когда я его отлаживал, fixedClock имеет значение, которое я ожидал FixedClock[2020-07-05T09:05:00Z,CET]. Вместо этого, если я поставлю точку останова на реализацию службы, переменная localDate будет иметь значение 2020-07-09 - .now().

Моя проблема заключается в следующем: почему переменная localDate не имеет значения of fixedClock переменной?

Большое спасибо за ваше время!

Дальнейшее редактирование:

Вот конструктор Сервиса:

@Autowired
  public SavingAccountService(
      SavingAccountRepository savingAccountRepository, UserRepository userRepository, Clock clock) {
    this.savingAccountRepository = savingAccountRepository;
    this.userRepository = userRepository;
    this.clock = clock;
  }

Аннотации в моем TestClass:

RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = RANDOM_PORT, classes = ChallengeApplication.class)
@ActiveProfiles("test")
public class SavingAccountTest {

    @Mock
    private Clock clock;
    private Clock fixedClock;

    @InjectMocks
    private SavingAccountService savingAccountService;

    @Autowired
    private TestRestTemplate restTemplate;
    private URL base;

    @LocalServerPort
    int port;

Я также хочу упомянуть, что из моего теста я вызываю контроллер, а не службу.

private final SavingAccountService savingAccountService;   

public SavingAccountRestController(SavingAccountService savingAccountService) {
    this.savingAccountService = savingAccountService;
}   

@Override   
@PostMapping   
public ResponseEntity<?> newSavingAccount(@RequestBody SavingAccount savingAccount) {
     EntityModel<SavingAccount> newSavingAccount = savingAccountService.newSavingAccount(savingAccount);
     return new ResponseEntity<>(newSavingAccount, HttpStatus.CREATED);
}

1 Ответ

2 голосов
/ 10 июля 2020

Проблема

В своем тесте вы создали службу SavingAccountService, которая использует внедренные имитаторы.

@InjectMocks
private SavingAccountService savingAccountService;

Проблема в том, что это не та служба, которую использует ваш контроллер. Spring boot test создает bean-компоненты, определенные в контексте приложения, автоматически подключает их и успешно игнорирует существование службы, определенной в test.

Решение

Вы должны сделать Spring с учетом загрузки bean-компонента Clock с фиксированным временем

Вариант 1: Mock bean

Вы определяете

@MockBean
private Clock clock;
private Clock fixedClock;

, и вам должно быть хорошо go.

Я все еще считаю этот метод запутанным, я бы хотел передать фиксированные часы как bean-компонент в контекст Spring Boot, вместо того, чтобы создавать макеты.

Вариант 2: Укажите классы компонентов, которые будут использоваться для загрузки ApplicationContext.

Создайте новый класс конфигурации в ваших тестовых каталогах

@Configuration
public class FakeClockConfig {

    private final static LocalDate WEEKEND = LocalDate.of(2020, 07, 05);

    @Bean
    public Clock clock() {
        return Clock.fixed(WEEKEND.atTime(9, 5).toInstant(ZoneOffset.UTC), ZoneId.of("CET"));
    }
}

Сообщите Spring Boot test об этой дополнительной конфигурации

@SpringBootTest(webEnvironment = RANDOM_PORT, 
                classes = {ChallengeApplication.class, FakeClockConfig.class})

Я считаю этот метод предпочтительным, вы уже сами указываете один класс компонента.

Часы с постоянным временем заменят ваши оригинальные часы

Вариант 3: @ TestConfiguration

* 104 4 * См. Spring boot - @ TestConfiguration

@ TestConfiguration - это специализированная форма @Configuration, которая может использоваться для определения дополнительных компонентов или настроек для теста.

При весенней загрузке любые bean-компоненты, настроенные в классе верхнего уровня с аннотацией @TestConfiguration, не будут обнаружены при сканировании компонентов. Мы должны явно зарегистрировать класс @TestConfiguration в классе, который содержит тестовые примеры.

Есть два способа включить эту дополнительную тестовую конфигурацию для тестов:

1.1. Аннотация @Import

1.2. Stati c вложенные классы

Давайте go с последним подходом:

@SpringBootTest(properties = "spring.main.allow-bean-definition-overriding=true")
public class ProjetRepositoryTest {

    private static final LocalDate WEEKEND = LocalDate.of(2020, 07, 05);

    @TestConfiguration
    static class FakeClockConfig {

        @Bean
        public Clock clock() {
            return Clock.fixed(WEEKEND.atTime(9, 5).toInstant(ZoneOffset.UTC), ZoneId.of("CET"));
        }
    }
}

Обратите внимание, что этот метод создает дополнительные bean-компоненты, поэтому мне нужно было разрешить переопределение bean.

См. Spring-Boot 2.1.x и определение переопределения bean-компонента

Другие примечания

Похоже, вы используете TestRestTemplate для бэкэнда тесты. Вместо этого вы можете использовать Mock Mvc.

См. Разница между Mock Mvc и RestTemplate в интеграционных тестах

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...