testing

.NET Musings

Wandering thoughts of a developer, architect, speaker, and trainer

NAVIGATION - SEARCH

My Favorite MbUnit Attributes

I presented a "Lap Around Gallio/MbUnit 3" last night for the Dayton .Net Developers' Group, and while I was going through all of the new/updated Attributes and Assertions, FallenRogue asked me which ones I use most often.  Sounded like good fodder for a post, so here are my favorite Attributes with samples to back them up.  (btw, Jeff Brown et al have done a fantastic job with the newest versions of Gallio and MbUnit - download here.)

Attributes

 

Row

The [RowTest] capability is by far my favorite feature, and one of the two reasons I moved to MbUnit from nUnit (The other being Rollback).  The [RowTest] attribute has been merged with [Test] in version 3, so there is no need for different test decorators.  Adding one or more Row attributes converts your test into a data driven test, and allows you to increase Use Case coverage without adding additional lines of code, or worse yet, repeating code. 

When add the [Row] attribute, the signature for your test changes to take the items in each row as parameters. 

By adding the ExpectedException parameter, you can have both happy and unhappy path in your rows (some, including me, would argue that you should refactor those into separate tests)

In the following example, I am testing a calculator's Add function.  If a negative addend is supplied, the test is validating that an ArgumentOutOfRangeException is thrown.  The Rows without negative addends test various Use Cases in the happy path.

[Test]
[Row(2,2,4)]
[Row(20,22,42)]
[Row(0,22,22)]
[Row(20,0,20)]
[Row(-1,1,0,
     ExpectedException = typeof(ArgumentOutOfRangeException))]

[Row(1,-2,-1,
     ExpectedException = typeof(ArgumentOutOfRangeException))]

public void Should_Add_Two_Integers(
    int first, int second, int sum)
{
    sut = CreateSUT();
    Assert.AreEqual(sum, sut.Add(first,second));
}

RollBack


Rollback is a close second in my favorite attribute category, and the other reason I moved to MbUnit from nUnit.  Rollback enlists your test code into a transaction (via the System.Transaction .Net API), so you don't have to explicitly code up the transaction logic yourself.  This works with anything that accepts transactions (such as SQL Server).  According to some traffic in the groups, Log4Net evidently does not enlist it's operations in a transaction kicked off by the Rollback attribute in MbUnit.  So, please test your system under test first.

[Test]
[Rollback]
public void Should_Add_A_Product_List()
{
   var dal = new MyDataAccessLibrary();
   var countOfRecords = dal.GetRecordCount(); 
   dal.InsertNewRecord();
   Assert.AreEqual(countOfRecords+1,dal.GetRecordCount());
}

ExpectedException


This indicates that an exception is expected to be thrown, and if it isn't, it fails the test.  Also, the thrown exception must match the typeof the exception registered in the attribute.

[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void Test_Exception_Handling1()
{
    throw new ArgumentNullException();

}

I am moving away from this syntax because I find the new Assert.Throws syntax to be more readable.  It also returns the exception thrown so you can check the message or other properties (such as the inner exception).

[Test]
public void Should_Test_For_Exception_Thrown()
{
   var ex = Assert.Throws<ArgumentNullException>(
       () => { throw new ArgumentNullException(exMessage); });
}

Repeat, ThreadedRepeat


Repeat will run the test n times.  This can be useful for testing load (if you don't have a true loadtesting tool), but more significant is ThreadedRepeat. This will spin up n number of threads, and execute the test on each of these different thread.


Test Factories [DynamicTestFactory]


There are Static and Dynamic Test Factories available.  If I am using Test Factories, I prefer to use Dynamic.  This capability allows me to dynamically  create test based on data in the database, generated code, user stories, etc.


The signature of your test method changes to return IEnumerable<Test> instead of void.  You test code then must return new test cases.  The simplest way to do this is through the yield return statement as in the following code snippet

[DynamicTestFactory]
public IEnumerable<Test> Should_Create_And_Execute_Dynamic_Tests()
{
   IEnumerable<int> list = new[] {1, 2, 3, 4, 5};
   foreach (int i in list) 
   { 
      yield return new TestCase(string.Format("Test {0}",i), 
         () => { Assert.IsTrue(MyFunction(i)); });
   }
}

Timeout(int seconds)


Setting this attribute will fail the test if the test takes longer than the number of seconds specified.  This is helpful when you have specific performance requirements or tests that can take an inordinate amount of time.  I use it mostly for my DAL tests, where if they start failing on my dev machine due to timeouts, it's time for a reboot.


MultipleAsserts


This will force all Asserts to run in a test even if there is a failure.  The standard process is that code will halt in a test when the first exception fails.  The MultipleAsserts attribute will run all the other Asserts and report all failures.


For me, this is mostly used in speaking engagements and sample code, as I believe in the one Assert per test paradigm.


CsvData


I don't use this, but it is a nice feature to have in the toolkit.  This allows referencing comma delimited data as a source for the test.


Importance


This one I don't use eiter, but had to mention.  This decorates the test with one of the values from the Importance enum, which include Critical, Severe, Serious, Default, NoOneReallyCaresAbout.  It's the last value the got it mentioned in my list. :-)


Summary


In addition to the listed, there are the standard Category, TestsOn, Author, etc that are prevalent in all of the testing frameworks.


In my next post, I will go through my favorite Assertions in MbUnit3.


Happy Coding!

Managed Windows Shared Hosting by OrcsWeb