Лучший подход к применению шаблона 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));
}
Другие вопросы по тегам