Thursday, September 01, 2005

TDD without FIT or NUnit: PocketPC and .NET CF

Can you be agile and "test-infected" in environements that provide no or little similarity to the platforms used for doing unit and functional tests? Can you still do test first programming in an development environement where you don't have NUnit or FIT?

Test Driven Development and Test First Programming

Let's start from the roots of Test Driven Development and Test First Programming. I noticed that around these two concepts there is a little missundestanding, some people condidering them to be the same, others considering them different. My opinion is that the two are slightly different, TDD being a method of programming where you think about how you are going to implement a feature before implementing it. The second, TFP is a more concrete approach, that says to write a test before you write the code. So where's the difference. The difference in my opinion is that TDD does not really force you to write the test before writing the code or writing the test at all. It just says to think about how you could automate the testing of the feature you want to implement, how to design a testable piece of software. TFP forces you to write the test first, whereas TDD forces you to design thinking about testing, but giving you the option to write the test when you want to.

So, as a conclusion, in my opinion TFP is a particular case of TDD whichis more generic. Don't get me wrong I do like very much to write the tests before.

Were there automated tests before xUnit and/or FIT?

Coming back to our problem with TDD on environements where you have no or little documentation, where you're a pioneer, I would say that we need to go back to the basics. Automated tests do not mean NUnit (xUnit) or Fit. These two frameworks and the ones deived were build to help making automated tests easier, but what was before these arrived?. Did people not write automated tests for their software? I doubt that. But how did they do automated tests it?

Well I belive that there were several techniques but the most popular were either adding a test button on the UI (desktop apps , we are talking about early internet or before internet times) or by adding a main method to the class (java). This were probably the most populat methods of building both functional and unit tests.

The 'test button' functional tests actualy put values into the controls on the form then simulated the click of a button then checking the results of that action. As a dear friend of mine said, this had the drawback that you needed to remove the test button when going into production, and more complex testing scenarios were increasingly hard to do.

How do you build functional tests on Windows.Forms apps for .NET CF?

You have no NUnitForms, no NUnit not a thing like this. What do you do? In order to be able to automate the testing of my forms, I thought how about adding the test button, but I remebered I wasn't particularily fond of it. Then I had the idea that for each form, I could add a "region" where I can have my test helper methods, each of them starting with Test. This meant, that in my application designed for PocketPC and used to collect measurements for medical equipment on daily, weekly, monthly and quarterly basis, having for each of these at least two forms, one dispalying the
list of available measurements and the add/edit/delete methods and the other to be able to add or edit a record, with ok/cancel buttons.

In each form, I added a region "Testers" where I added my testing helper methods like:

#region testers
public void TestTypeQuantity(double q)
{
TestingHelper.Type(this.txtQuantity, q);
}
public void TestSelectMeasurementUnit(int index)
{
TestingHelper.SelectMeasurementUnit(this.cbxQuantity, index);
}
public void TestClickAddDailyMeasurement()
{
TestingHelper.Click(btnAddDailyMeasurement);
}

#endregion


In order to be able to interact easier with the controls I have a TestingHelper class, that helps me work with the controls as if I were the user. For instance in order to click on a button, I have:

public virtual void Click()
{
if (Control.Visible)
{
FireEvent( "Click" );
}
else
{
throw new Exception("Control " +control+" is not visible." );
}
}

public void FireEvent( string eventName )
{
MethodInfo minfo = Control.GetType().GetMethod( "On" + eventName, BindingFlags.Instance BindingFlags.Public BindingFlags.NonPublic );
ParameterInfo[] param = minfo.GetParameters();
Type parameterType = param[0].ParameterType;
minfo.Invoke( Control, new object[]
{
Activator.CreateInstance( parameterType )
} );
}


or for typing something in a TextBox:

public static void Type(Control control, string text)
{
control.Focus();
control.Text = text;
}


The test and handling modal windows

For the test, I have a simple singleton class, that tests like:


public class TestingInstance
{
#region members
private static TestingInstance testingInstance = null;
private Hashtable expectedModals = new Hashtable();
#endregion

private TestingInstance()
{

//specify what method to be invoked when DailyChecksForm modal is shown
ExpectModal(typeof(DailyChecksForm),"RunDailyChecksForm");
}

private void ExpectModal(Type formType, string param2)
{
this.expectedModals[formType] = param2;
}

public static TestingInstance Instance
{
get
{
if(testingInstance==null)
{
testingInstance = new TestingInstance();
}
return testingInstance;
}
}

public void OnModalShown(Form form)
{
string methodName = expectedModals[form.GetType()] as string;
MethodInfo mi = this.GetType().GetMethod(methodName);
object[] parameters = new object[]{form};
mi.Invoke(this,parameters);
}

public void Run()
{
MainForm mf = new MainForm();
mf.Show();
mf.TestSelectUnitByIndex(2);
mf.Close();
}

public void RunDailyChecksForm(Form form)
{
DailyChecksForm mf = form as DailyChecksForm;
//mechanical checks
mf.TestSelectMechanicalChecksTab();
mf.TestTypeSignature("Dan Bunea");
//x-ray tube
mf.TestSelectXRayTubeTab();
mf.TestSelectTargetFilter1(1);
mf.TestTypeReading1("1.0");
mf.TestSelectTargetFilter2(1);
mf.TestTypeReading2("2.0");
mf.TestSelectTargetFilter3(1);
mf.TestTypeReading3("3.0");
mf.TestSelectTargetFilter4(1);
mf.TestTypeReading4("4.0");
//4cm
mf.TestSelect4cmTab();
mf.TestTypekV("1");
mf.TestTypeMAS("2");
mf.TestSelectTargetFilter4cm(1);
mf.TestTypeDensity("3");
//click ok and save
mf.TestClickOK();
}

...


Since my Win32 knowledge level is low, what could I do to see when a modal form is being shown. Well as always there is a solution, you just have to use your head a little.

All my forms extended a TestableForm, which on load notifies the testing instance that it has been shown . For instance in DailyMeasurementForm I have:


private void DailyChecksForm_Load(object sender, System.EventArgs e)
{
TestingInstance.Instance.OnModalShown(this);
}



which when is loaded and shown tells my TestingInstance that it has been shown, then my testing instance looks in the list of expected modal forms for the type and invokes the specified method, in our case, we have in the tesing instance constructor:

ExpectModal(typeof(DailyChecksForm),"RunDailyChecksForm");

which means that when DailyChecksForm is shown, it will invoke OnModalShown in TestingInstance and that will invoke RunDailyChecksForm. Simple , isn't it?

How do you start the test on the mobile device?

public static void Main()
{
if(IsTestingMode())
{
TestingInstance.Instance.Run();
}
else
{
Application.Run(new MainForm());
}
}


public static bool IsTestingMode()
{
return true;//this can be modified very easely
}


Maybe I could have manipulated VS.NET and would have found a more elegant way of doing it, but let's not forget that I needed to develop software that could be automatically tested not really invent the new all good testing framework for PDAa and respecting the two agile priciples of simplicity and evolutionary design, I think the code is good enough and can be refactored and improved later.

Conclusion

The bad thing about my solution is that it mixes testing code with production code, but I think that even with this drawback, it is still better then testing everything manually. And maybe the test code could be removed from the production code using preprocessor directives, but that is just a supposition.

So do we really need NUunit?...TDD can be done without but apps shouldn't be done without TDD in my opinion. But I guess that I am just "test infected" ... :)

1 comment:

Dan Bunea said...

Hi,

Unfortunately I don't know if I can be of very much help. The applications I wrote are commercial applications and I can't really share the source code. And the purpose of the article was to show how I can build some functional tests, in order to develop in a TDD manner (see http://danbunea.blogspot.com/2005/12/test-little-code-little-practical.html)

About sample code, and controls and an entire framework to help you develop PocketPC apps from .NET, I would recommend http://www.opennetcf.org/CategoryView.aspx?category=Home

Thanks and sorry not to be more helpful,
Dan