tags:

views:

664

answers:

7

I'm convinced from this presentation and other commentary here on the site that I need to learn to Unit Test. I also realize that there have been many questions about what unit testing is here. Each time I go to consider how it should be done in the application I am currently working on, I walk away confused. It is a xulrunner application application, and a lot of the logic is event-based - when a user clicks here, this action takes place.

Often the examples I see for testing are testing classes - they instantiate an object, give it mock data, then check the properties of the object afterward. That makes sense to me - but what about the non-object-oriented pieces?

This guy mentioned that GUI-based unit testing is difficult in most any testing framework, maybe that's the problem. The presentation linked above mentions that each test should only touch one class, one method at a time. That seems to rule out what I'm trying to do.

So the question - how does one unit testing procedural or event-based code? Provide a link to good documentation, or explain it yourself.

On a side note, I also have a challenge of not having found a testing framework that is set up to test xulrunner apps - it seems that the tools just aren't developed yet. I imagine this is more peripheral than my understanding the concepts, writing testable code, applying unit testing.

+1  A: 

The problem is that "event based programming" links far too much logic to the events. The way such a system should be designed is that there should be a subsystem that raises events (and you can write tests to ensure that these events are raised in the proper order). And there should be another subsystem that deals only with managing, say, the state of a form. And you can write a unit test that will verify that given the correct inputs (ie. events being raised), will set the form state to the correct values.

Beyond that, the actual event handler that is raised from component 1, and calls the behavior on component 2 is just integration testing which can be done manually by a QA person.

Joel Martinez
+2  A: 

At first I would test events like this:

private bool fired;

private void HandlesEvent(object sender, EventArgs e)
{
    fired = true;
 } 

public void Test()
{
   class.FireEvent += HandlesEvent;
   class.PErformEventFiringAction(null, null);

   Assert.IsTrue(fired);
}

And Then I discovered RhinoMocks. RhinoMocks is a framework that creates mock objects and it also handles event testing. It may come in handy for your procedural testing as well.

Gilligan
Rhino makes testing events a breeze!
Chris Canal
A: 

An approach I've found helpful for procedural code is to use TextTest. It's not so much about unit testing, but it helps you do automated regression testing. The idea is that you have your application write a log then use texttest to compare the log before and after your changes.

+4  A: 

The idea of unit testing is to test small sections of code with each test. In an event based system, one form of unit testing you could do, would be to test how your event handlers respond to various events. So your unit test might set an aspect of your program into a specific state, then call the event listener method directly, and finally test the subsequent state of of your program.

If you plan on unit testing an event-based system, you will make your life a lot easier for yourself if you use the dependency injection pattern and ideally would go the whole way and use inversion of control (see http://martinfowler.com/articles/injection.html and http://msdn.microsoft.com/en-us/library/aa973811.aspx for details of these patterns)

(thanks to pc1oad1etter for pointing out I'd messed up the links)

David Arno
You actually linked to the same article twice there - did you mean to?
pc1oad1etter
I think these two concepts (DI and IoC) go over my head. I don't think I ever learned about design patterns in school, or in any of my jobs.
pc1oad1etter
I haven't learned about those in school either. I've learned about them in the field. Don't let your background constrain you. You won't get a good grasp of these concepts without giving it a shot (at implementation.)
Ates Goral
A: 

See the oft-linked Working Effectively with Legacy Code. See the sections titled "My Application Is All API Calls" and "My Project is Not Object-Oriented. How Do I Make Safe Changes?".

In C/C++ world (my experience) the best solution in practice is to use the linker "seam" and link against test doubles for all the functions called by the function under test. That way you don't change any of the legacy code, but you can still test it in isolation.

Joe Schneider
I ordered the book - I hope it's as good as everyone says.
pc1oad1etter
+1  A: 

Answering my own question here, but I came across an article that take explains the problem, and does a walk-through of a simple example -- Agile User Interface Development. The code and images are great, and here is a snippet that shows the idea:

Agile gurus such as Kent Beck and David Astels suggest building the GUI by keeping the view objects very thin, and testing the layers "below the surface." This "smart object/thin view" model is analogous to the familiar document-view and client-server paradigms, but applies to the development of individual GUI elements. Separation of the content and presentation improves the design of the code, making it more modular and testable. Each component of the user interface is implemented as a smart object, containing the application behavior that should be tested, but no GUI presentation code. Each smart object has a corresponding thin view class containing only generic GUI behavior. With this design model, GUI building becomes amenable to TDD.

pc1oad1etter
A: 

Your question doesn't state your programming language of choice, but mine is C# so I'll exemplify using that. This is however just a refinement over Gilligans answer by using anonymous delegates to inline your test code. I'm all in favor of making tests as readable as possible, and to me that means all test code within the test method;

// Arrange
var car = new Car();
string changedPropertyName = "";
car.PropertyChanged += delegate(object sender, PropertyChangedEventArgs e)
                        {
                           if (sender == car) 
                                  changedPropertyName = e.PropertyName;
                        };

// Act
car.Model = "Volvo";

// Assert 
Assert.AreEqual("Model", changedPropertyName, 
    "The notification of a property change was not fired correctly.");

The class I'm testing here implements the INotifyPropertyChanged interface and therefore a NotifyPropertyChanged event should be raised whenever a property's value has changed.

Kjetil Klaussen