Unit Testing Framework for .Net encourages developers to test their .Net code, whether it complies with specified requirements & designs and performs as expected before handing over to the QA testing team. This will help detect problems at the early phase of software development. When considering Unit testing .Net application, there are several .Net Unit testing frameworks available in the industry with NUnit, xUnit, MS test as most popular frameworks. Hence, choosing the right Unit testing framework will always be a difficult task. However, xUnit earns points over other frameworks as it has addressed some shortcomings and mistakes of its predecessors. Here are some of the reasons why you would need to use xUnit over other Unit testing frameworks.
xUnit is a free, open source Unit testing framework for .Net developed by Brad Wilson and Jim Newkirk, inventor of NUnit. The framework is built with .Net Framework 2.0 and it doesn’t require any installation (such as XCopy), which makes it ideal for storing control. On top of that, it works well with ReSharper, Xamarin, TestDriven.Net and a Console runner for accomplishing the Unit test. It includes the excellent extensibility of test classes and test methods. xUnit test is the best Unit testing framework for .Net programming languages like C#, VB.Net, and F#. xUnit derives its structure and functionality from SUnit of Smalltalk.
Top 5 Enhancements with xUnit
xUnit includes some attractive features to support developers writing a clean test – a test which has high readability, simplicity, clarity, and density of expression. The inventors of this framework intended to create some rules instead of repeating boring guidance about “do X” and “Don’t do Y”. Let us explore the bad practices and shortcomings with other .Net Unit testing frameworks as well as improvements with xUnit:
1. Extensibility with Fact and Theory
xUnit is far more flexible and extensible than the other .Net Unit test frameworks. It allows you to create new attributes to control your tests. It ensures custom functionality with the possibility of extending the Asset class’s Contains, DoesNotContain Equal, NotEqual, InRange, & NotInRange. xUnit also allows you to inherit from Fact, Theory, and other attributes.
xUnit supports two kinds of Unit tests like Facts and Theories. It uses the [Fact] attribute in place of [Test] attribute. The Fact attribute abides Ignore attribute with the new attribute called Skip. Fact tests invariant conditions and is typically used when there is a need to have a Unit test, which includes no method arguments. Here is an example of [Fact] attribute.
As a part of enforcing cleaner code, xUnit requires specifying the reason for skipping a certain test, and it is not mandatory with the former Ignore attribute.
Theory attribute is meant to support data-driven tests that only work with a certain set of values passed as arguments to the test methods. Here is an example:
Theory attribute is a good example of the extensibility feature of xUnit. If you code a single Unit test method, the [Theory] attribute allows you to execute the method multiple times. For example, let us explore the same code with multiple inputs:
Here the test will run thrice in the test explorer - executing once for each set of specified input.
Benefit: This feature proves the xUnit is far more extensible and allows you to run your test methods multiple times.
2. Single Object Instance per Test Method
In xUnit, test methods are isolated, while they interfere with each other in the remaining .Net Unit testing frameworks. Consider the following examples:
Though both Unit test code appears almost the same, you can find the difference by observing their result update:
You can find that the second NUnit test has failed. This is because NUnit executes the entire tests in the same class using the same instance. On the other hand, with xUnit, the test class is instantiated – it runs the single test method and discards the class. It recreates the class again to run the next test method.
Benefit: This feature allows to isolates the test method, which ensures that you can run any blend of tests and in any order without letting one test method affect the other test’s result. Thereby, the xUnit test eliminates the risk of test method dependencies.
3. No [SetUp] or [TearDown]
SetUp and TearDown are two attributes on methods commonly implemented in the TestFixture of NUnit and JUnit to perform initialization and destruction. However, most programmers complain that using [SetUP] and [TearDown] cause code duplication. The example below depicts the NUnit Unit test with these attributes:
When executing this code, you will get the following result:
The result proves that using [SetUp] and [TearDown] attributes are a bad practice when it comes to reducing code duplication. xUnit test performs initialization and destruction with test class’ constructor & an IDisposable interface.
Benefit: Eliminating these features encourages the .Net developers to write cleaner Unit tests with xUnit. Thereby xUnit promises the simple design by removing code duplication. Here is an example of how xUnit handles [TearDown] by implementing IDisposable.
4. No [ExceptedException]
xUnit employs Assert.Throws instead of using [ExpectedException] - an attribute to show that the execution of your Unit test will throw a specific exception. With [ExpectedException], there is a chance to conceal real errors when the exception occurs in the wrong place of the code. ExceptedException attribute is recommended only when there is a single line of code in the test method. Since ExceptedException evaluates only one of the lines in code and throws the exception. In case, another line of the code throws an exception, this attribute could easily hide errors.
As a result, xUnit test implements Assert.Throws in place of expected exceptions. Though it requires quite more code, it is very straightforward in which line of code you’re expecting to throw a specific exception regardless of the number of lines in the code.
Let us have an example for testing exceptions with xUnit test. The following code uses the ExpectedException to specify that test will pass when an exception called authentication with invalid credential is raised:
However, imagine a situation that you are throwing a SecurityException rather than AuthenticatedException. Here the .Net Unit test framework will throw a security exception without caring about who or where the exception is raised.
xUnit rescues your Unit test with a much cleaner tactic as shown below:
As you can see, the xUnit test code uses Assert.Throws construct instead of ExpectedException. Hence, if you use SecurityException in the creation of AuthenticationServices, the Unit test framework will throw a security exception and your test fails.
Benefit: When your exception becomes too generic, Assert.Throws allows you to verify as well as assert conditions after your exception is thrown.
5. Reduced number of custom attributes
The excessive use of custom attributes sometimes deviate you away from the original language. xUnit test has removed some of these attributes from the .Net Unit test framework. Here is the list of attributes removed from the framework:
- [Setup] and [TearDown] are replaced with Constructors & IDisposable.
- [TestFixture] is removed
- [Ignore] is absorbed with Skip = parameter on [Fact]
- [ExceptedException] is replaced with Throws
- [TestFixtureSetup] & [TestFixtureTearDown] are eliminated
Moreover, to ensure the xUnit run into a situation where test classes are continually being recreated, the inventor has eliminated some of the functionalities like ClassCleanup, ClassInitialize, TestCleanup, and TestInitialize.
Lesson Learned: These omissions reflect that using these attributes are not good practices in unit testing.
xUnit is really a great and lightweight framework. With all these improvements and fine-tuning, xUnit becomes the most preferable choice of Unit testing framework for .Net developers. xUnit test helps to ensure your code is working properly. It also reduces the load on manual testing by making single code block run within expected parameters.