Practical .NET

Introducing the .NET Core Unit Testing Framework (or: Why xUnit?)

Let me be clear about the new .NET Core testing framework, xUnit: It's an alternative, not a replacement for MSTest, the .NET Core version of the framework you're familiar with from earlier versions of the .NET Framework. Not only is xUnit not a replacement, unlike MSTest, migrating existing .NET Framework test code to xUnit probably isn't an option. Enough syntax is different that migrating existing code to xUnit is probably more work than you'd want to do (a lot of syntax is different).

But, having said that, there are lots of good reasons why you'd want to use xUnit when you create your first new testing project in .NET Core.

Changes and Improvements
Some of the changes that come with xUnit are obvious. As a relatively trivial example, you no longer need the TestClass attribute to flag classes that contain test code: the xUnit framework will find test methods wherever you put them.

Many changes are less benign if you're considering porting existing test code to xUnit. In xUnit, for example, you will usually flag test methods with the Fact attribute rather than TestMethod. That Fact attribute also now absorbs the Ignore attribute, which is now a property called Skip on Fact. As part of enforcing best practices, using the Skip property requires that you provide a reason for skipping a test, which was optional with the Ignore attribute.

The equivalent toTestCategory is an attribute called Trait that requires two parameters instead of one. As I've discussed elsewhere, the legacy ExpectedException attribute isn't a great way to test for exceptions. As a result, xUnit doesn't have ExpectedException but, instead, has Assert.Throws, which is a better solution.

Overall, you'll find some common tests that were obscure or hard to write before are simpler and more obvious in xUnit. To begin with, in general, the Assert class's method names are more to the point (for example, AreEqual is replaced by Equal). That Equal method also now includes a version that will compare two collections to see if they're equivalent. There's also a ThrowsAny method for checking for any Excpetion being thrown, which provides a more obvious test than the equivalent code in MSTest (and xUnit also has DoesNotThrow method to check that a test case doesn't throw some exception). xUnit's Assert class includes an IsAssignableFrom method for those cases where yout want to check if you have the right class or a class that it inherits from.

A Different Architecture
Under the hood, the xUnit architecture is also far more extensible than the other Visual Studio test frameworks. You can create new attributes to control tests or extend the Assert class's Equal, NotEqual, Contains, DoesNotContain, InRange and NotInRange methods to provide custom functionality.

That extensibility has already made possible an alternative to the Fact attribute that flags test methods: the Theory attribute. The Theory attribute supports data-driven testing for methods that only work with a specific set of values: You write a single test method and the Theory attribute lets you run the method multiple times, once for every data value for which the test is applicable.

The major architectural change isn't as obvious, however. With Visual Studio Test, the infrastructure would instantiate your test class and then run all of your test methods. With xUnit, your test class is instantiated, a single test method is run and the class is discarded. The class is then recreated before running the next method. The primary benefit is that your tests are far more isolated than they were before, which is a good thing. Isolating tests ensures that you can run any combination of tests and run them in any order you want without one test affecting the results of another test.

However, it also means that some functionality that Visual Studio Test supports had to be eliminated or rewritten to work in an environment where test classes are constantly being recreated. Specifically, ClassInitialize, ClassCleanup, TestInitialize and TestCleanup are gone in xUnit. Their omission also reflects xUnit authors' belief that using these attributes was, if not bad practices, certainly "not very good" ones.

Obviously, if you agree that those attributes (and ExpectedException) are problematic, you don't have to give up Visual Studio Test/MSTest to get the "benefit" of not using them. However, if you're running an IT department (as I did, once upon a time) you'll feel safer if your team is using a tool that prevents bad practices rather than having to go around to your developers to remind them to "Do this" and "Don't do that."

Still, there may be times when you'd like to share a single context among all your tests (the rationale for ClassInitialize/Cleanup) or create a new test context for each test (the rationale for TestInitialize/Cleanup). I'm not here to say you're wrong.

In fact, I feel your pain: While I was never a fan of ClassInitialize and ClassCleanup, I've used TestInitialize and TestCleanup occasionally. The good news is there are replacements, as I'll discuss in a later column.

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus

Featured

  • Using Local AI to Cut Copilot Usage-Based Billing Shock

    After being gobsmacked by the new billing plan using almost all my monthly credits in one or two days, I tried pushing some Copilot-style coding work onto local models in VS Code. What I found was less "free AI" and more "pick your pain": cloud charges on one side, heavy local resource use and long waits on the other.

  • .NET 11 Preview 5 Focuses on Performance, Productivity and Safer Code

    .NET 11 Preview 5 focuses on under-the-hood runtime performance gains, streamlined APIs and language features that reduce boilerplate, plus built‑in security checks and incremental ASP.NET Core and EF Core improvements aimed at everyday developer productivity.

  • VS Code 1.124 Focuses on Agent Autonomy and Parallel Sessions

    Microsoft's June 2026 VS Code update turns on Autopilot by default and adds background sending for agent sessions.

  • Developing Agentic Systems in .NET: From Concept to Code

    ZioNet founder Alon Fliess previews his Visual Studio Live! San Diego session on building true agentic systems in .NET -- covering the cognitive loop, MCP tool integration, multi-agent orchestration and enterprise hosting and governance with the Microsoft Agent Framework.

Subscribe on YouTube