NUnit and the RowTest extension

Started by Thorin, March 27, 2009, 08:57:59 PM

Previous topic - Next topic

Thorin

Not sure how many people here use NUnit (given that it's for .NET development), but there's a really neat extension that's been made for it, called [RowTest].  This lets you create one test and then specify all the different ways to call that test, passing in sets of test data.  Before [RowTest] existed, you used to have to create a different test for each set of test data.  Thus, you'd have four or five tests just do the extents tests.

Have a look-see: http://madcoderspeak.blogspot.com/2009/01/nunit-rowtest-extension-running-same.html

This attribute concept was rabidly stolen from MbUnit, where they also came up with the [Rollback] attribute (automatically rolls back database changes without you having to write code to do that) and some kind of Pair-Wise concept.  Pair-Wise lets you specify an array of possible values, and every possible combination of those values is sent to the test.  So if there's three possible values in the array, then the test will get called nine times.  And you only write one test.

These seem like such simple concepts, yet it seems like forever before someone thinks of them...
Prayin' for a 20!

gcc thorin.c -pedantic -o Thorin
compile successful

Mr. Analog

I really, really, really want to get my team into unit testing by August this year (that's my deadline). Right now their idea of application testing is to kinda poke at the app with a loosely defined script (that might as wall say "do stuff, like, 'in' the application... for a while").

Thanks for the article... bookmark'd!
By Grabthar's Hammer

Thorin

Quote from: Mr. Analog on April 08, 2009, 09:27:24 PM
I really, really, really want to get my team into unit testing by August this year (that's my deadline). Right now their idea of application testing is to kinda poke at the app with a loosely defined script (that might as wall say "do stuff, like, 'in' the application... for a while").

Thanks for the article... bookmark'd!

Ow!  That hurt to think about.  Also, you're welcome :)  I tried it out and it makes nunit test code much, much cleaner.
Prayin' for a 20!

gcc thorin.c -pedantic -o Thorin
compile successful

Mr. Analog

Quote from: Thorin on April 09, 2009, 01:01:32 PM
Quote from: Mr. Analog on April 08, 2009, 09:27:24 PM
I really, really, really want to get my team into unit testing by August this year (that's my deadline). Right now their idea of application testing is to kinda poke at the app with a loosely defined script (that might as wall say "do stuff, like, 'in' the application... for a while").

Thanks for the article... bookmark'd!

Ow!  That hurt to think about.  Also, you're welcome :)  I tried it out and it makes nunit test code much, much cleaner.

Have you taken a look at NCover?

I wanna get code coverage reports running too at some point in the mists of the future.
By Grabthar's Hammer

Thorin

Yes, we're using NCover and NCoverExplorer (both old versions, I think the new NCover 3 includes some kind of updated explorer).

Code coverage is a delicate subject, as you have to determine what kind of coverage is adequate.  If you aim for 100% coverage, you'll either be disappointed or be busy for a long time writing tests that touch those last bits of code.  If you aim for less than 100% coverage, then you'll have to determine what code doesn't get touched and whether that's important to you.

To give you an example, I wrote the following test code recently:


[RowTest]
[Row("Account")]
[Row("AccountType")]
public void CheckSqlCreation(string tableName)
{
string sql = GetData.GetSql(tableName);
Assert.That(sql.ToUpper() == "SELECT * FROM " + tableName);
}


The method being tested here is GetSql(), which looks like so:


public static string GetSql(string tableName)
{
if (tableName == null ||
tableName == string.Empty)
{
throw new ArgumentNullException("tableName");
}
return "SELECT * FROM " + tableName;
}


Code coverage showed that not every sequence point in my code was being hit.  Can you guess what wasn't being hit?  It was the throw statement inside the if statement.  My test was not passing a null or empty string to the method.  I had to add the following row to my rowtest:


[Row("", ExpectedException = typeof(ArgumentNullException), ExceptionMessage = "Value cannot be null.\r\nParameter name: tableName")]


Now, is that a useful test?  In this case it was really simple to figure out what was missing.  But here's another example:


[RowTest]
[Row("Account")]
[Row("AccountType")]
public void CheckTablesExist(string tableName)
{
SqlCommand databaseCommand = null;
SqlDataReader databaseReader = null;
try
{
databaseCommand = databaseConnection.CreateCommand();
databaseCommand.CommandText = "SELECT 0 FROM " + tableName;
databaseCommand.CommandType = CommandType.Text;
databaseReader = databaseCommand.ExecuteReader();
}
catch
{
throw;
}
finally
{
if (databaseReader != null)
{
if (!databaseReader.IsClosed)
{
databaseReader.Close();
}
databaseReader = null;
}
if (databaseCommand != null)
{
databaseCommand = null;
}
}
}


This is a test that doesn't score 100% coverage.  So my test itself isn't even completely covered!  Again, the problem was the exception - in this case, no exception happens in this test, so the throw doesn't get hit.  I had to add the following to make it score 100% coverage:


[Row("_InvalidTable",
ExpectedException=typeof(System.Data.SqlClient.SqlException),
ExceptionMessage="Invalid object name '_InvalidTable'.")]


Now imagine all the try/catch blocks in your code, and having to call your code in such a way that you hit every catch()...  Sometimes it makes sense, but a lot of those check-if-a-null-argument-got-passed-and-throw-exception-if-not blocks of code probably don't need to get exercised.  I mean, my tidy mind wants to do it, but I also see that it's probably more a waste of time and less a useful test at that point.
Prayin' for a 20!

gcc thorin.c -pedantic -o Thorin
compile successful