Best approach towards applying the Arrange-Act-Assert pattern when expecting exception

6.4k views Asked by At

I'm trying to follow the Arrange-Act-Assert pattern when writing unit test and I got to a point where I'm confused on which approach will be better. I'm using xUnit and my first approach to the problem was:

//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);

and my second approach is:

int key = 1;
string value = "X";

board.Add(key, value);

Assert.Throws<ArgumentException>(() => board.Add(key, value));

which is a better approach?

Edit: Blogged about this on wp.me/p4f69l-3z

5

There are 5 answers

0
Neil Smith On

I'd say your second example is better. Assert.Throws will pass/fail the test so there's no reason to get it's result and assert on that. When I'm writing 'will throw' tests I keep it on one or two lines:

[Test]
public void SomeMethod_NullSomething_ShouldThrow() {
    var something = MakeTarget();

    Assert.Throws<ArgumentNullException>(() => something.SomeMethod(null));
}
11
Elias Platek On

Actually there is no good answer on asserting exception... It's like testing events, except that they interrupt the code flow. Let's say you would test the add event (I'm using 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);

It's the same thing when you test an exception:

// 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);

So the Assert.Throws can be used for convenience but it doesn't fit with AAA style. But remember AAA is not mandatory to write good tests, the most important is to have your tests easy to understand.

4
k.m On

Your first .Add call should be really part of arrange. Treat it as precondition/setup for act. Other than that you can wrap act in Action to make it read better:

//Arrange
int key = 1;
string value = "X";
board.Add(key, value);

//Act
Action addingSameKeySecondTime = () => board.Add(key, value);

//Assert
Assert.Throws<ArgumentException>(addingSameKeySecondTime)

FluentAssertions library mentioned in comments makes such asserts even more sentence-alike:

int key = 1;
string value = "X";
board.Add(key, value);

Action addingSameKeySecondTime = () => board.Add(key, value);

addingSameKeySecondTime.ShouldThrow<ArgumentException>();
0
Patrick McDonald On

I usually take the following approach

// Arrange
int key = 1;
string value = "X";
board.Add(key, value);

// Act & Assert
Assert.Throws<ArgumentException>(() => board.Add(key, value));

this is the approach taken in ASP.NET MVC (e.g. https://aspnetwebstack.codeplex.com/SourceControl/latest#test/Common/PrefixContainerTest.cs)

0
Hennadii On

For me, source code should be self-descriptive, so AAA comments are more like workaround and might not provide enough flexibility. Check this library: Heleonix.Testing You can write tests as in JavaScript Jasmine/Jest styles in both Given/When/Then and AAA forms.