For C# developers, it's no problem! Roslyn, the C# compiler, is also a library that can parse C# code and expose the syntax tree (and more!). When combined with LINQ, this makes it easy to automate changes across a codebase.
Here at Jetabroad we've used Roslyn several times to make large codebase refactors. In this post, we'll use Roslyn to make a basic NUnit test to xUnit test converter. In the interest of keeping this post short, we won't get into NUnit vs xUnit, but there are plenty of comparisons on the web. We'll also be assuming some basic knowledge of Roslyn.
Building our NUnit to xUnit Converter
There are many differences between NUnit and xUnit, and not all of them are easily automatable. However, we can save a lot of time by automating the following changes:
- NUnit test methods have a
[Test]
attribute. xUnit test methods use a[Fact]
attribute. - NUnit test classes have a
[TestFixture]
attribute. xUnit test classes don't need an attribute. - NUnit has
[SetUp]
for test initialization and[TearDown]
for cleanup. xUnit uses the the constructor for initialization andIDisposable
for cleanup.
The CSharpSyntaxRewriter is built into Roslyn. It's an implementation of the Visitor pattern, and it provides an elegant way to traverse and "update" our immutable syntax tree. Here's what a sample syntax tree looks like for an NUnit test class:
Let's take the easy case first, and replace
[Test]
with [Fact]
:We subclass the
CSharpSyntaxRewriter
, and override one of the many methods of the base class. In this case, we want to make a change to an attribute so we override VisitAttribute
, and return an xUnit Fact
attribute to replace the NUnit Test
attribute. If it's not an attribute we're interested in, we call the base method to continue processing other nodes of the tree.Pretty straight-forward, right? But so far, a Find-Replace would do the same thing with less effort. Let's move on to something more interesting: deleting
[TestFixture]
. In this case, we need to override VisitAttributeList
, because if we remove TestFixture
from the attribute list [TestFixture]
we'd end up with the empty attribute list []
. So we need to remove the entire attribute list in that case:Finally, let's do something a bit more difficult, replacing the method with the
SetUp
attribute with a constructor:Easy! We now have a basic CSharpSyntaxRewriter implementation. Here's how we might call it on a project:
Future Work
For the next steps in our xUnit converter, we need to do the following:
- Converting
TearDown
toIDisposable
- Adding
using Xunit;
to the top of each file. - Converting
TestCaseSource
to xUnit'sMemberData