Release Notes: March 16, 2015

Hello, v2 2.0!

The day is finally here: we shipped 2.0 RTM today.

If you don't want to read a bunch of release notes and just want to get started, this documentation is for you.

Our 2½ year journey included 20 alphas, 5 betas, and 4 release candidates:

January 2012
July 2012
August 2012
August 2013
March 2014
January 2015
March 2015
Shipped 1.9
Shipped 1.9.1
First public alpha of 2.0
Shipped 1.9.2
First public beta of 2.0
First release candidate of 2.0
Shipped 2.0

Thank You!

The community drove this release as much as anything else, and that feedback—whether it came through using the product, reporting issues, or contributing code—was directly responsible for its success. There are a few people that we want to call out:

What's New

Unlike traditional release notes (which cover incremental builds from previous drops), these will cover the major design shift from v1 to v2, and some discussion about what motivated some of these changes. The first thing to make clear: we will continue to support 1.9.2 through our new 2.0 runners, so your upgrade path is incremental. We have no plans to release any fixes for 1.9.2 unless they are complete showstoppers, but given its age, we don't expect that to ever happen.

One of the reasons that this process took so long is that there is almost no code in the framework itself that remains from the v1 timeframe, other than some of the assertion library. From an internal implementation standpoint (and an extensibility standpoint), this is an entirely new product.


The #1 feature for v2 was to improve consumability. To us, that falls into several separate but related buckets of functionality:

More Platforms. 1.x only directly supported writing and running unit tests on the desktop CLR. For 2.0, we have added support for Portable Class Libraries, Universal Applications (those that target Windows Phone 8.1, Windows 8.1, and Windows 10), Modern Windows 8 Applications, Windows Phone 8 Silverlight Applications, MonoAndroid Applications, MonoTouch Applications, and iOS Unified Applications. In addition, in 2.1 we are shipping support for ASP.NET 5 projects running on Windows, Linux, and OS X.

Separated Assertions. A common theme among users was that they wanted the ability to choose assertion libraries other than ours. In 2.0, we have separated the core framework from the assertion library, and we offer the asserts in either binary or source form. By bringing in assertions as source, you can extend the Assert class itself with your own assertion methods.

No more installers. We moved to a NuGet based distribution model in 1.7, and with 2.0, we've finally cut ties with all other distribution mechanisms. Even support for running tests inside of Visual Studio is shipped as NuGet package!

More Visual Studio. We regularly test our code with Visual Studio 2012, 2013, and the CTP builds of 2015. (Earlier versions of Visual Studio are no longer supported, since the minimum .NET version required for unit test projects is .NET 4.5.)

Lots of Samples. Our samples project has samples ranging from simple assertion enhancement all the way up to writing your own replacement unit testing framework.

Integrated Theory Support

One of the very first things we did for 2.0 was to remove the xunit.extensions project, and integrate the theory support directly into core. It did help us to have this as an extension when we were driving the v1 design, but we heard from many (if not most) of our users that they were all using theories.

The rest of the code in xunit.extensions was moved primarily into sample projects. We may ship some of these samples as source-based NuGet packages in the near future, based on developer demand.

For more information on migrating v1 tests that use xunit.extensions, please see Upgrading xunit.extensions.


One of the premiere new features in 2.0 is support for parallelism. Back in the old v1 days, people would occasionally hack together MSBuild scripts to run multiple assemblies in parallel; this desire to get tests running faster drove us to make parallelism a part of the framework itself as well as the runners.

The first and most visible part of this decision is the new default behavior of to run your tests in parallel. Tests in the same test class will not be run in parallel against each other, but tests in different test classes will. We call the delineation of tests which can run in parallel against each other test collections. In order to prevent tests from running in parallel against each other, you put them in the same test collection. The default behavior in 2.0 (in the absence of any other configuration) is create a test collection for each test class.

This will undoubtedly be the most surprising change for users moving from v1 (or other test frameworks). We decided to make this the default behavior because we believe that unit tests should be small, self-contained, and fast. By running them in parallel, we can better utilize modern computers, which typcially have somewhere between 2 and 12 virtual CPUs sitting at the ready.

Of course, we provide a way to influence parallelism. For more information on how you can tweak these settings, please see Running Tests in Parallel.

One of the "victims" of the new parallelism system is that we've removed the Timeout property from the [Fact] attribute. In a world where many things are running at once, and the system cannot reliably determine how much time a given set of code has been running, we needed to remove this feature. In addition, if you want test timings to be accurate, you will need to turn off all parallelism; otherwise, the test timing numbers will be unreliable.

Finally, while 1.x half-heartedly supported asynchronous tests, this has been embraced fully by 2.0. This means many of the extensibility points in the system are now async, and we have full support for async code in the assertion library. You can even decorate your test methods with async void instead of async Task, and we'll still be able to wait for your tests to finish. (Just to be clear: returning Task still works just fine; the async void feature is something you can choose to use or not.)

Test Collections

We mentioned test collections in the section above on parallelism, but it's worth mentioning it separately as a feature, since it has more utility value than just setting parallelism boundaries.

The old IUseFixture<T> interface in v1 has been replaced by two new interfaces: IClassFixture<T> and ICollectionFixture<T>. In addition, the mechanism for injecting fixture values into your tests has changed from property setters to constructor arguments. Class fixtures are created once and shared amongst all tests in the same class (much like the old IUseFixture). Collection fixtures work the same way, except that the single instance is shared amongst all tests in the same test collection.

For more information on using test collections, please see Shared Context between Tests.

Test Output

In v1, we captured output written to standard output, standard error, the debug channel, and the trace channel, and we surfaced this output up to the test runners (which displayed them in a way that was appropriate to the test). In addition, the output was placed into the XML output, if you saved the result of your test runs.

With v2, we have stopped capturing these sources. The primary reason is that these are shared resources, and it's impossible to know which of the many unit tests running in parallel might be writing to the console or the tracing system. Instead, tests can accept ITestOutputHelper as a constructor argument, and use that to provide output that is reported up to the runners (and placed into the XML results). It can be used from the constructor, from within the test, and from within IDisposable.Dispose() (if it's defined).

For more information on using test output, please see the TestOutputExample sample project.

New Extensibility Model

One of the more common reasons people chose 1.x was because it was extremely easy to extend. Those extensibility points offered some measure of control over the test framework, but we repeatedly heard from our users about use cases they weren't able to accomplish with our extension points.

The re-design of the internal engine of 2.0 was driven primarily around our desire to dramatically improve the extensibility of the core testing framework itself. We are aware that people write extensions for a large number of reasons ranging from simply adding new assertions and assert comparers all the way up to writing new test frameworks that can leverage the runner infrastructure provided by We wanted to ensure that these users could not only do what they already did, but given them many more options for extending

The Samples project on GitHub contains several levels of extensibility samples. Over the next few weeks and months, we will be writing documentation (on this site) to go along with these samples, to help developers understand how to best exploit the power of the new v2 engine.

Move to GitHub got its start not just on CodePlex, but inside of it. Jim and Brad developed the early versions of while they were on the CodePlex team. Our decision to move from CodePlex wasn't easy, but our community was pushing us to make the switch. It was good for us, because we saw an immediate and dramatic uptick in community contribution.

The organization is home to almost all of the projects related to They include:

With all these projects come a lot of NuGet packages. If you want to know what's in each package and where to start, please see What NuGet Packages Should I Use?.

Thank You! (Again)

Seriously, thank you to everybody who uses this framework. Happy coding!

- Jim & Brad

Copyright © .NET Foundation. Contributions welcomed at