Модульное тестирование частных методов в Акке
Я новичок в Акке, и я пытаюсь Акка на Java. Я хотел бы понять модульное тестирование бизнес-логики в актерах. Я читаю документацию, и единственный пример изолированной бизнес-логики внутри актера:
static class MyActor extends UntypedActor {
public void onReceive(Object o) throws Exception {
if (o.equals("say42")) {
getSender().tell(42, getSelf());
} else if (o instanceof Exception) {
throw (Exception) o;
}
}
public boolean testMe() { return true; }
}
@Test
public void demonstrateTestActorRef() {
final Props props = Props.create(MyActor.class);
final TestActorRef<MyActor> ref = TestActorRef.create(system, props, "testA");
final MyActor actor = ref.underlyingActor();
assertTrue(actor.testMe());
}
Хотя это просто, это означает, что метод, который я хочу протестировать, является общедоступным. Тем не менее, учитывая, что актеры должны общаться только через сообщения, я понимаю, что нет никаких причин использовать публичные методы, поэтому я сделал свой метод приватным. Как в примере ниже:
public class LogRowParser extends AbstractActor {
private final Logger logger = LoggerFactory.getLogger(LogRowParser.class);
public LogRowParser() {
receive(ReceiveBuilder.
match(LogRow.class, lr -> {
ParsedLog log = parse(lr.rowText);
final ActorRef logWriter = getContext().actorOf(Props.create(LogWriter.class));
logWriter.tell(log, self());
}).
matchAny(o -> logger.info("Unknown message")).build()
);
}
private ParsedLog parse(String rowText) {
// Log parsing logic
}
}
Итак, чтобы проверить метод parse
Я либо:
- нужно сделать пакет приватным
- Или проверить открытый интерфейс актера, то есть следующего актера
LogWriter
получил правильное проанализированное сообщение от моего актераLogRowParser
Мои вопросы:
- Есть ли недостатки у варианта № 1? Предполагая, что субъекты, общающиеся только через сообщения, инкапсуляция и чистые открытые интерфейсы, менее важны?
- В случае, если я пытаюсь использовать опцию № 2, есть ли способ перехватить сообщения, отправленные от актера в тесте downstream (тестирование
LogRowParser
и ловить вLogWriter
)? Я рассмотрел различные примеры наJavaTestKit
но все они отлавливают сообщения, которые являются ответами назад отправителю, и ни одно из них не показывает, как перехватить сообщение, отправленное новому субъекту. - Есть ли другой вариант, который мне не хватает?
Спасибо!
UPD: забыл упомянуть, что я также рассматривал варианты как:
- Перемещение логики из актеров полностью в вспомогательные классы. Это обычная практика с аккой?
- Powermock... но я пытаюсь избежать этого, если возможен редизайн
2 ответа
Там действительно нет веских причин, чтобы сделать этот метод частным. Как правило, метод класса становится закрытым, чтобы тот, кто имеет прямую ссылку на экземпляр этого класса, не вызывал этот метод. С экземпляром актера никто не будет иметь прямой ссылки на экземпляр этого класса актера. Все, что вы можете получить для связи с экземпляром этого актерского класса, это ActorRef
это легкий прокси-сервер, который позволяет вам общаться только путем отправки сообщений для обработки onReceive
через почтовый ящик. ActorRef
не раскрывает никакого внутреннего состояния или методов этого класса актера. Это своего рода один из главных пунктов продажи системы актеров. Экземпляр субъекта полностью инкапсулирует свое внутреннее состояние и методы, защищая их от внешнего мира и позволяет только тем внутренним вещам изменяться в ответ на получение сообщений. Вот почему нет необходимости отмечать этот метод как закрытый.
редактировать
Модульное тестирование актера, IMO, всегда должно проходить через receive
функциональность. Если у вас есть некоторые внутренние методы, которые затем вызываются обработкой в receive
Вы не должны сосредоточиваться на тестировании этих методов в отдельности, а вместо этого убедиться, что пути, которые приводят к их вызову, правильно выполняются с помощью сообщений, которые вы передаете во время тестовых сценариев.
В вашем конкретном примере, parse
производит ParsedLog
сообщение, которое затем отправляется на logWriter
детский актер. Для меня, зная, что parse
работает, как ожидалось, означает, что logWriter
получил правильное сообщение. Для этого я бы позволил создание ребенка logWriter
переопределить, а затем сделать это в тестовом коде и заменить создание актера TestProbe
, Затем вы можете использовать expectMsg
на этом зонде, чтобы убедиться, что он получил ожидаемый ParsedLog
Таким образом, сообщение также проверяет функциональность в parse
,
Что касается вашего другого комментария о том, как перевести реальный бизнес для актера в отдельный и более тестируемый класс, а затем вызвать его из актера, некоторые люди делают это, так что это не случайно. Лично я нет, но это только я. Если такой подход работает для вас, я не вижу каких-либо серьезных проблем с ним.
У меня была такая же проблема 3 года назад, когда я имел дело с актерами: лучший подход, который я нашел, состоял в том, чтобы иметь минимальную ответственность за ответную реакцию актера. Актер получит сообщение и выберет метод объекта для вызова или сообщение для отправки или исключение, чтобы бросить и все. Таким образом, будет очень легко смоделировать либо сервисы, вызываемые субъектом, и входные данные для этих сервисов.