Wednesday, July 15, 2009

Unit Testing - First Attempts

I had some time to use while I was investigating the smart card issues, so I decided to do the right thing. Something I have never done before. To learn how to write and use unit tests. Since I had a very small application that was testing the capabilities of the printer, it looked like a perfect guinea pig for my experiment. It turned out that writing tests is not so hard as I expected it to be. Well, I can not promise that I did it right, of course, because no one writes them here anyway and there is no one to mentor me or point to my mistakes.

So first of all I downloaded and installed NUnit framework

NUnit framework

Then I added a project of type class library to my solution and a single class called UnitTest to this solution. Here is the full code of the UnitTest class:

using System;
using NUnit.Framework;
using SmartCardTest;
using DataCardCP40;

[TestFixture]
public class UnitTest
{
public DataCardPrinter printer;
ICE_API.DOCINFO di;

[Test]
public void CreateObjects()
{
printer = new DataCardPrinter();
di = DataCardPrinter.InitializeDI();
printer.CreateHDC();
Assert.AreNotEqual(printer.Hdc, 0);
Assert.Greater(di.cbSize, 0);
}

[Test]
public void SetInteractiveMode()
{
int res = ICE_API.SetInteractiveMode(printer.Hdc, true);
Assert.Greater(res, 0);
}

[Test]
public void StartDoc()
{
int res = ICE_API.StartDoc(printer.Hdc, ref di);
Assert.Greater(res, 0);
}

[Test]
public void StartPage()
{
int res = ICE_API.StartPage(printer.Hdc);
Assert.Greater(res, 0);
}

[Test]
public void RotateCardSide()
{
int res = ICE_API.RotateCardSide(printer.Hdc, 1);
Assert.Greater(res, 0);
}

[Test]
public void FeedCard()
{
int res = ICE_API.FeedCard(printer.Hdc, ICE_API.ICE_SMARTCARD_FRONT + ICE_API.ICE_GRAPHICS_FRONT);
Assert.Greater(res, 0);
}

[Test]
public void SmartCardContinue()
{
int res = ICE_API.SmartCardContinue(printer.Hdc, ICE_API.ICE_SMART_CARD_GOOD);
Assert.Greater(res, 0);
}

[Test]
public void EndPage()
{
int res = ICE_API.EndPage(printer.Hdc);
Assert.Greater(res, 0);
}

[Test]
public void EndDoc()
{
int res = ICE_API.EndDoc(printer.Hdc);
Assert.Greater(res, 0);
}
}

There's not much to explain. First I create the objects required and verify that the device context was created and the DOCINFO struct was initialized. All the other tests just check the return codes of the printer functions. The error code is 0, so the check is for return value being greater than zero.

After compiling and fixing errors I realized that I have no way to set the sequence of the execution. Supposedly, as the theory teaches us, each test should be able to run alone and independent of whether the rest were passed, failed or run at all. Well, does not work so well in my case - if I want to test that the card can be ejected from the printer, I need to somehow insert it first! I found out, however, that the tests are executed in the alphabetic order of their names. Okay, that'll do for now. So I just renamed my tests like this A_CreateObjects(), B_SetInteractiveMode() etc. Then I compiled the solution, creating the "DataCardTest.dll". Next step is to run NUnit and open the dll. Wow! The smart thing can see all my tests now. When ready, just select Test->Run all from the menu and enjoy ...

It does not alway end that well, however - it might be like this (see how it tells what was the line where the error happened and how the expected test result was different from the actual).

What happened here? Took me some time to figure out ... the default printer was not set to my card printer.

by . Also posted on my website

No comments: