When you need to unit test a method, you should check happy path (for example, the method returns a resulting value) and sad path (the method throws exception). Here I describe a basic usage of unit testing with Visual Studio 2017 and xUnit version 2.2.
The code being tested:
1 2 3 4 5 6 7 8 9 |
public int GetNumber(int number) { if (number < 0) { throw new ArgumentException(); } return number; } |
1. Check successful result (happy path) – it’s very straightforward.
1 2 3 4 5 6 7 8 9 10 |
[Fact] public void GetNumber_Success() { // arrange var sut = new SystemUnderTest(); // act int actual = sut.GetNumber(5); // assert Assert.Equal(5, actual); } |
2. Check fail when the method throws an exception (sad path)
If you have faced with MSTest, you might remember [ExpectedException] attribute. In this case MSTest waits for a particular exception would be thrown in a whole unit test method, but not in a specific line of code. Modern unit test frameworks have more graceful capabilities to catch the exception.
2.1. Using Throws<>
1 2 3 4 5 6 7 8 |
[Fact] public void GetNumber_Fail_Throws() { // arrange var sut = new SystemUnderTest(); // act & assert Assert.Throws<ArgumentException>(() => sut.GetNumber(-1)); } |
But this approach combines Act and Assert phases of unit test in one line of code. Richard Banks suggested a better way in his article Stop Using Assert.Throws in Your BDD Unit Tests.
2.2. Using Record.Exception
1 2 3 4 5 6 7 8 9 10 11 |
[Fact] public void GetNumber_Fail_Record() { // arrange var sut = new SystemUnderTest(); // act var exception = Record.Exception(() => sut.GetNumber(-1)); // assert Assert.NotNull(exception); Assert.IsType<ArgumentException>(exception); } |
At first, I check that the exception was really caught, then check the type of that exception.
Next time I’ll tell about unit testing the asynchronous methods.