Как я могу проверить агрегат, если идентификатор генерируется случайным образом? - PullRequest
0 голосов
/ 02 февраля 2020

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

однако я не вижу никакого очевидного способа сделать anyString совпадение для одного поля события в среде TestFixture.

Является ли "плохой практикой" генерирование идентификаторов в агрегате при создании? Должны ли идентификаторы генерироваться вне совокупности?

@AggregateMember(eventForwardingMode = ForwardMatchingInstances.class)
private List<TimeCardEntry> timeCardEntries = new ArrayList<>();

data class ClockInCommand(@TargetAggregateIdentifier val employeeName: String)

@CommandHandler
public TimeCard(ClockInCommand cmd) {
  apply(new ClockInEvent(cmd.getEmployeeName(),
      GenericEventMessage.clock.instant(),
      UUID.randomUUID().toString()));


@EventSourcingHandler
public void on(ClockInEvent event) {
  this.employeeName = event.getEmployeeName();
  timeCardEntries.add(new TimeCardEntry(event.getTimeCardEntryId(), event.getTime()));
}

@Data
public class TimeCardEntry {

  @EntityId
  private final String timeCardEntryId;
  private final Instant clockInTime;
  private Instant clockOutTime;

  @EventSourcingHandler
  public void on(ClockOutEvent event) {
    this.clockOutTime = event.getTime();
  }

  private boolean isClockedIn() {
    return clockOutTime != null;
  }
}

@ParameterizedTest
@MethodSource(value = "randomEmployeeName")
void testClockInCommand(String employeeName) {
    testFixture.givenNoPriorActivity()
            .when(new ClockInCommand(employeeName))
            .expectEvents(new ClockInEvent(employeeName, testFixture.currentTime(), "Any-String-Works"));
}

1 Ответ

4 голосов
/ 03 февраля 2020

Является ли "плохой практикой" генерация идентификаторов в совокупности при ее создании? Должны ли идентификаторы генерироваться вне агрегата?

Случайные числа во многом похожи на часы - они являются формой общего изменяемого состояния. Иными словами, они относятся к императивной оболочке , а не к функциональному ядру.

Что обычно означает для вашей доменной модели то, что случайность передается в качестве аргумента, а не производится самим агрегатом. Это может означать передачу генератора идентификаторов в модель предметной области или даже создание идентификатора в приложении и передачу сгенерированного идентификатора в качестве значения .

Таким образом, в нашем модульном тесте мы замените генератор случайных чисел, предоставленный целевым приложением, на «случайный» генератор, предоставленный тестом - поскольку тест управляет генератором, используемый идентификатор становится детерминированным c, и, следовательно, вы можете легче его обойти.

В тех случаях, когда вы не довольны тем, что генератор случайных чисел является частью API вашей модели домена, другой вариант - выставить его как часть тестового интерфейса.

// We don't necessarily worry about testing this version, it is "too simple to break"
void doSomethingCool(...) {
    doSomethingCool(ID.new, ...);
}

// Unit tests measure this function instead, which is easier to test and has
// all of the complicated logic
void doSomethingCool(ID id, ...) {
    // ...
}
...