xUnit.net RC2 Released
UPDATE: xUnit.NET RC2 New Drop includes ASP.NET MVC support and better GUI runner. Details here.
UPDATE: Added Static Methods mention and F# - Thanks to DevHawk!
I've been a big fan of such testing frameworks as NUnit and MbUnit, but recently I've found myself getting pulled more towards xUnit.net at least to play around with for any of my code samples that I write for this blog and on my own time. I'm not really a fan of MSTest and many I think would agree about its deficiencies. I won't go as far as say Jay Flowers and wear the shirt though...
Another Release?
Recently, Brad Wilson and Jim Newkirk recently announced the release of xUnit.net RC2 on CodePlex. I'd encourage you download the latest bits here. For those wondering what changes happened between RC1 and RC2, Brad has a good writeup on his blog here. What's interesting about this is the removal of the Assert class methods which take a user defined message should it fail. I was never really a fan of those in the first place though.
Another interesting added features was the IUserFixture<T> which allows you to have a startup and teardown for your fixtures in a separate class and therefore reusable, unlike the current way of using no parameter constructors as your startup and the IDisposable.Dispose for your teardown. See the tests in the FixtureExample for details. But here's a snipped version of that code:
public class DatabaseFixture : IDisposable
{
SqlConnection connection;
int fooUserID;
public DatabaseFixture()
{
string connectionString = ConfigurationManager.ConnectionStrings["DatabaseFixture"].ConnectionString;
connection = new SqlConnection(connectionString);
connection.Open();
string sql = @"INSERT INTO Users VALUES ('foo', 'bar'); SELECT SCOPE_IDENTITY();";
using (SqlCommand cmd = new SqlCommand(sql, connection))
fooUserID = Convert.ToInt32(cmd.ExecuteScalar());
}
public SqlConnection Connection
{
get { return connection; }
}
public int FooUserID
{
get { return fooUserID; }
}
public void Dispose()
{
string sql = @"DELETE FROM Users WHERE ID = @id;";
using (SqlCommand cmd = new SqlCommand(sql, connection))
{
cmd.Parameters.AddWithValue("@id", fooUserID);
cmd.ExecuteNonQuery();
}
connection.Close();
}
}
What the above code allows us to do is to define a class that holds the data from the initialization of the first test, to the cleanup after the last test. Our state is therefore maintained in a reusable manner. As you will note, the startup logic resides in the default no parameter constructor and all teardown logic is in the IDisposable.Dispose method.
public class FixtureTests : IUseFixture<DatabaseFixture>
{
DatabaseFixture database;
public void SetFixture(DatabaseFixture data)
{
database = data;
}
[Fact]
public void FooUserWasInserted()
{
string sql = "SELECT COUNT(*) FROM Users WHERE ID = @id;";
using (SqlCommand cmd = new SqlCommand(sql, database.Connection))
{
cmd.Parameters.AddWithValue("@id", database.FooUserID);
int rowCount = Convert.ToInt32(cmd.ExecuteScalar());
Assert.Equal(1, rowCount);
}
}
}
Then we can go ahead with our tests, while using the SqlConnection as defined on our DatabaseFixture. After we're done with our test, it goes ahead and calls Dispose on the fixture. I tend to like this approach and it's definitely growing on me.
Why xUnit.net?
For those new to xUnit.net, there are some decent links to help you along. Some of the more interesting ones can be found here:
- Comparison to MbUnit, NUnit and MSTest
- Running Resharper with xUnit.net
- An introduction to xUnit.net
- An introduction to xUnit.net extensions
- Create your own xUnit.net extension
To use this, just simply use the Assert.Throws<TException>(Assert.ThrowsDelegate) which I've found to be very helpful. Let's look at a quick test of that being used.
[Fact]
public void PopEmptyStack()
{
Stack<string> stack = new Stack<string>();
Assert.Throws<InvalidOperationException>(() => stack.Pop());
}
As you can see, we're pretty explicit about what line will throw the exception, and that's really the key to this scenario. There are a good number of samples provided on the releases page that you should check out. As always with most products that I talk about, I highly recommend reading the tests to really fully understand what's going on underneath the covers. Not only does it help you understand the intent of the program, but you can learn about good coding techniques, design patterns, testing patterns and so on.
Another point that xUnit.net separates itself from the pack is the ability to decorate static methods as facts. This frees you from having to create an instance of your test class in order to call them. Harry Pierson, aka DevHawk, demonstrates its use with regards to F# and testing the parse buffer here. It definitely opened my eyes and a few more avenues as I pursue more F# related work items in the future. Here's just a quick and dirty sample of showing how you can use xUnit.net with F# quite easily, just as Harry's post did.
#light
#R @"E:\Tools\xunit-build-1223-samples\Samples\xunit\xunit.dll"
open System
open System.Collections.Generic
open Xunit
type Stack<'t> = class
val elements : LinkedList<'t>
new() = { elements = new LinkedList<'t>() }
member x.IsEmpty
with get() = x.elements.Count = 0
member x.Push element =
x.elements.AddFirst(element:'t)
member x.Top
with get() =
if x.elements.Count = 0 then
raise (InvalidOperationException("cannot top an empty stack"))
x.elements.First.Value
member x.Pop =
let top = x.Top
x.elements.RemoveFirst()
top
end
[<Fact>]
let NoElementsShouldBeEmpty () =
let stack = new Stack<string>()
Assert.True(stack.IsEmpty)
If you notice, the FactAttribute is placed on a static method called NoElementsShouldBeEmpty and sure enough it works like a champ through xUnit.net. I like this approach instead of the pomp and circumstance required for creating classes as shown above with my Stack class. Note the use of the empty parans which forces it to be a void method with no values passed either. But if you run it through the xunit.console sure enough it succeeds like a champ.
What are we missing though? Well, I'm in favor of having a standalone GUI Test Runner much like NUnit and MbUnit have. In fact, Brad has started this and you can get these features from the latest commits here. Mind you I haven't gotten it to work just right yet, but it's a work in progress.
Conclusion
There is a lot to like about xUnit.net and takes a lot of lessons learned from the use of NUnit, MbUnit and others and I think they're doing a good job incorporating issues. This project isn't as active as MbUnit and NUnit, but it's definitely one to keep an eye on. Recent releases of NUnit and Gallio Automation Platform will probably be also covered in the short while as well as they have a lot to offer. Until next time...