Лучший подход к применению шаблона Arrange-Act-Assert при ожидании исключения
Я пытаюсь следовать шаблону Arrange-Act-Assert при написании модульного теста, и я попал в точку, где я не уверен, какой подход будет лучше. Я использую xUnit, и мой первый подход к проблеме был:
//Arrange
int key = 1;
string value = "X";
//Act
board.Add(key, value);
var result = Assert.Throws<ArgumentException>(() => board.Add(key, value));
//Assert
Assert.IsType<ArgumentException>(result);
и мой второй подход:
int key = 1;
string value = "X";
board.Add(key, value);
Assert.Throws<ArgumentException>(() => board.Add(key, value));
какой подход лучше?
Редактировать: в блоге об этом на wp.me/p4f69l-3z
5 ответов
Ты первый .Add
звонок должен быть действительно частью аранжировки. Рассматривайте это как предварительное условие / настройку для действия. Помимо этого вы можете обернуть акт в Action
чтобы это читалось лучше:
//Arrange
int key = 1;
string value = "X";
board.Add(key, value);
//Act
Action addingSameKeySecondTime = () => board.Add(key, value);
//Assert
Assert.Throws<ArgumentException>(addingSameKeySecondTime)
Библиотека FluentAssertions, упомянутая в комментариях, делает такие утверждения еще более похожими на предложения:
int key = 1;
string value = "X";
board.Add(key, value);
Action addingSameKeySecondTime = () => board.Add(key, value);
addingSameKeySecondTime.ShouldThrow<ArgumentException>();
Я обычно придерживаюсь следующего подхода
// Arrange
int key = 1;
string value = "X";
board.Add(key, value);
// Act & Assert
Assert.Throws<ArgumentException>(() => board.Add(key, value));
этот подход используется в ASP.NET MVC (например, https://aspnetwebstack.codeplex.com/SourceControl/latest).
Для меня исходный код должен быть самоописательным, поэтому комментарии AAA больше похожи на обходной путь и могут не обеспечивать достаточной гибкости. Проверьте эту библиотеку: Heleonix.Testing Вы можете писать тесты как в стилях Jasmine/Jest JavaScript в формах Given/When/Then и AAA.
На самом деле нет хорошего ответа на утверждение исключения... Это похоже на тестирование событий, за исключением того, что они прерывают поток кода. Допустим, вы протестируете событие добавления (я использую NUnit):
// Arrange
int key = 1;
var eventFired = false;
board.Added += (boardItem) => {
eventFired = boardItem.key == key;
};
// Act
board.Add(key, "X");
// Assert
Assert.That(eventFired, Is.True);
Это то же самое, когда вы тестируете исключение:
// Arrange
int key = 1;
var exceptionRaised = false;
board.Add(key, "X");
// Act
try {
board.Add(key, "X");
}
catch(InvalidOperationException ex) {
exceptionRaised = true;
}
// Assert
Assert.That(exceptionRaised, Is.True);
Поэтому Assert.Throws можно использовать для удобства, но он не соответствует стилю AAA. Но помните, что при написании хороших тестов ААА не обязательно, главное, чтобы ваши тесты были легкими для понимания.
Я бы сказал, что ваш второй пример лучше. Assert.Throws пропустит / провалит тест, поэтому нет причин получать его результат и утверждать его. Когда я пишу тесты "будет выбрасывать", я держу их в одной или двух строках:
[Test]
public void SomeMethod_NullSomething_ShouldThrow() {
var something = MakeTarget();
Assert.Throws<ArgumentNullException>(() => something.SomeMethod(null));
}