Unit Testing
Unit Testing
Having automated tests is a great way to ensure a software application does what its
authors intend it to do. There are multiple types of tests for software applications.
These include integration tests, web tests, load tests, and others. Unit tests test
individual software components and methods. Unit tests should only test code within
the developer’s control. They should not test infrastructure concerns. Infrastructure
concerns include databases, file systems, and network resources.
Also, keep in mind there are best practices for writing tests. For example, Test Driven
Development (TDD) is when a unit test is written before the code it is meant to check. TDD is
like creating an outline for a book before we write it. It is meant to help developers write
simpler, more readable, and efficient code.
Arrange, Act, Assert is a common pattern when unit testing. As the name implies, it
consists of three main actions:
Why?
Clearly separates what is being tested from the arrange and assert steps.
Less chance to intermix assertions with "Act" code.
Bad:
[Fact]
public void Add_EmptyString_ReturnsZero()
{
// Arrange
var stringCalculator = new StringCalculator();
// Assert
Assert.Equal(0, stringCalculator.Add(""));
}
Better:
[Fact]
public void Add_EmptyString_ReturnsZero()
{
// Arrange
var stringCalculator = new StringCalculator();
// Act
var actual = stringCalculator.Add("");
// Assert
Assert.Equal(0, actual);
}
Here, we will create four different test methods to test four different methods. And each
method will be gone through with three processes -- one arranges the data for testing, the
second performs the operation with required values and the last checks if the expected value
is equal to the actual value or not.
Fluent Assertions is a very extensive set of extension methods that allows you to more
naturally specify the expected outcome of a TDD or BDD-style unit tests. Targets .NET
Framework 4.5 and 4.7, as well as .NET Core 2.0, .NET Standard 1.3, 1.6 and 2.0.
We need to keep three things while writing the Unit Test Cases and these
are Arranging the data, Performing the action and Matching the output
(Arrange, Act, Assert).
A Working Theory
XUnit also has a Theory attribute, which represents a test that should succeed for certain input
data. In practice, most code has a different behavior depending on inputs (such as a different
result based on validation), and I find that I use Theory to create parameterized tests much more
often than Fact. There are 3 basic ways to create Theory based tests, and these ways will be
covered below.
[Theory]
[InlineData(2,3)]
[InlineData(4,5)]
[InlineData(5,11)]
public void CanAddNumbersFromInlineDataInput(int x, int y)
{
// arrange
Abacus abacus = new Abacus(Math.Max(x, y), x + y);
// act
int result = abacus.Add(x, y);
// assert
Assert.True(result > 0);
Assert.Equal(x + y, result);
}
I tend to use this form when the number of parameterized cases is pretty small.
[Theory]
[MemberData("AddPositiveNumberData")]
public void CanAddNumbersFromMemberDataInput(int x, int y)
{
// arrange
Abacus abacus = new Abacus(Math.Max(x, y), x + y);
// act
int result = abacus.Add(x, y);
// assert
Assert.True(result > 0);
Assert.Equal(x + y, result);
}
xUnit.net
n/a Fail n/a alternative: Assert.True(f
alse, "message")
xUnit.net
Is.GreaterThan n/a n/a alternative: Assert.True(x
> y)
Is.AssignableF IsAssigna
n/a
rom bleFrom
Is.InstanceOf< IsInstanceO
IsType<T>
T> fType
xUnit.net
Is.NaN n/a n/a alternative: Assert.True(d
ouble.IsNaN(x))
xUnit.net
Is.Not.Assigna alternative: Assert.False(
n/a n/a
bleFrom<T> obj is Type)
xUnit.net
Is.LessThan n/a n/a alternative: Assert.True(x
< y)
Attribute Notes
Note 1: Long-term use of [ExpectedException] has uncovered various problems with
it. First, it doesn’t specifically say which line of code should throw the exception, which allows
subtle and difficult-to-track failures that show up as passing tests. Second, it doesn’t offer the
opportunity to fully inspect details of the exception itself, since the handling is outside the
normal code flow of the test. Assert.Throws allows you to test a specific set of code for
throwing an exception, and returns the exception during success so you can write further
asserts against the exception instance itself.
Note 2: The xUnit.net team feels that per-test setup and teardown creates difficult-to-follow
and debug testing code, often causing unnecessary code to run before every single test is
run. For more information, see http://jamesnewkirk.typepad.com/posts/2007/09/why-you-
should-.html.
Note 3: xUnit.net provides a new way to think about per-fixture data with the use of
the IClassFixture<T> and ICollectionFixture<T> interfaces. The runner will
create a single instance of the fixture data and pass it through to your constructor before
running each test. All the tests share the same instance of fixture data. After all the tests have
run, the runner will dispose of the fixture data, if it implements IDisposable. For more
information, see Shared Context.
Note 4: xUnit.net ships with support for data-driven tests call Theories. Mark your test with
the [Theory] attribute (instead of [Fact]), then decorate it with one or
more [XxxData] attributes, including [InlineData] and [MemberData]. For more
information, see Getting Started.
Attributes
NUnit 3.x MSTest 15.x xUnit.net 2.x Comments
The difference is that unit tests are designed to test every functional code block in your project in
an isolated state, whereas integration tests test against the controller actions. This different
approach is highly suitable for web projects, as the tests are effectively emulating user action on
views.