Замінюємо тестування алгоритмів тестуванням внесених ефектів

Як і очікував, правило 8 про те, що не тестуємо алгоритм методів у статті "Правила впровадження TDD в старому проекті" викликало найбільше питань «як» і «навіщо». В момент складання минулого статті мені здалося це очевидно, тому не зупинився детальніше на цьому моменті. Але оскільки запитань виникло багато, хочу описати своє бачення. Тому під катом буде невеликий приклад коду і два приклади того, як його можна було б протестувати.

Щоб не змушувати вас бігати до попередньої статті, наведу правило «Не тестуємо алгоритм методів» ще раз, як воно там звучало:
Тут невдало підібрано назву правила, але кращого поки не придумав. Серед «мокистов» (це ті, хто вмокає в тестах) є ті, хто перевіряє кількість викликів певних методів, веріфіцірует сам виклик та ін Іншими словами, займається перевіркою внутрішньої роботи методів. Це так само погано, як і тестування приватних. Різниця тільки в рівні застосування такої перевірки. Такий підхід знову дає безліч тендітних тестів, з-за чого TDD деякими не сприймається нормально.
Є наступний код хендлера:

public class SomeEventHandler
{
public SomeEventHandler(IDatabaseCommands dbCommands,
IEventValidator validator,
IMessagingLogger messagingLogger)
{
// skipped
}

public HandlerResult Handle(EventPayload payload)
{
if (Validator.IsOurEvent(payload))
if (Validator.IsValid(payload))
{
var evt = Mapper.Map<Event>(payload);
try
{
using (var tran = new TransactionScope())
{
DbCommands.SaveEvt(evt);
MessagingLogger.Received(payload);
tran.Complete();
}
}
catch (Exception ex)
{
return MessageHandlerResult.Fatal;
}
}
else
{
var error = Validator.GetErrors();
MessagingLogger.InvalidEvent(payload, error);
return MessageHandlerResult.Fatal;
}
return MessageHandlerResult.Success;
}
}

Необхідно протестувати роботу методу Handle(). Питання стоїть в тому, щоб переконатися, що методи DbCommands і MessagingLogger були викликані.

Підхід «мокиста»
Він передав би в конструктор класу моки відповідних інтерфейсів, а після перевірив б зголосилися чи ні відповідні методи: SaveEvt(), Received() або InvalidEvent(). Код виглядав би приблизно так:

public void Should_save_valid_data_and_log_to_messaging_events()
{
var builder = new EventPayload {
// skipped
};

var validator = Mock.Of<IEventValidator>();
var dbCommands = new Mock<IDatabaseCommands>();
var messagingLogger = new Mock<IMessagingLogger>();
var handler = new SomeEventHandler(dbCommands, validator, messagingLogger);

var result = handler.Handle(payload);

// assertions
Assert.Equal(MessageHandlerResult.Success, result);
dbCommands.Verify(m => m.SaveEvt(It.IsAny<Event>(), Times.Once())
messagingLogger.Verify(m => m.Received(It.IsAny<EventPayload>(), Times.Once())
}

Підхід «немокиста»
Він створив би fake objects і перевірив б звершилося подію в цілому, а не виклик методу. У цьому випадку код був би приблизно таким:

public void Should_save_valid_data_and_log_to_messaging_events()
{
var builder = new EventPayload {
// skipped
};

var validator = Mock.Of<IEventValidator>();
var dbCommands = new FakeDatabaseCommands();
var messagingLogger = new FakeMessagingLogger();
var handler = new SomeEventHandler(dbCommands, validator, messagingLogger);

var result = handler.Handle(payload);

// assertions
Assert.Equal(MessageHandlerResult.Success, result);
Assert.True(dbCommands.IsEventSaved);
Assert.True(messagingLogger.IsEventRegistered);
}

А методи fake-objects виглядали б так:

public void SaveEvt(Event evt)
{
IsEventSaved = true;
}

При цьому IsEventSaved був би оголошений тільки в fake об'єкті.

Плюси і Мінуси
Перший підхід простий і швидкий, але якщо необхідно змінювати методи, викликати один замість іншого в тій же ситуації, тести необхідно було б правити.

Другий підхід призводить до створення додаткових сутностей, а виграш виходить тільки в ситуації з заміною методів. В цьому випадку, можливо, навіть нічого не доведеться змінювати ні в Fakes, ні в тестах. Ще один плюс, правда, більш ідеалістичний, в тому, що немокист робить тест так, щоб він не знав про внутрішній устрій досліджуваного методу. Тому, особисто я, якщо час дозволяє, роблю тести на fakes.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.