We have been doing a Lunch-N-Learn session every Wednesday for a few weeks now. Today's session was presented by a coworker, covering the use of RhinoMocks in unit testing. (I'm a regular reader of Ayende's blog so I hear a lot about RhinoMocks on a regular basis, and all the cool stuff that people are doing with it.) The demo was good - well planned, well executed, and open to Q&A during the entire session. I even leard about the "ExceptedException" attribute in nUnit, which I had been wanting to use a few weeks ago but didn't realize was there. What really struck me during the demo, was how clear of a difference there is between nUnit and RhinoMocks; not just because one is a unit test assertion framework and another is a mock object / IoC / DI repository, but also because of how clear the distinction between state based testing and behaviour based testing became.
State based or bevahiour based?
Examine the following code for a moment. This is an example of the type of code that I have recently used in my unit tests and illustrates what I thought was behaviour testing.
//... code classes
public interface ISomething
{
public void DoSomething();
}
public class MockSomething: ISomething
{
public void DoSomething()
{
_SomethingCalled = true;
}
private _SomethingCalled = false;
public bool SomethingCalled
{
get { return _SomethingCalled; }
}
}
public class MyProcess
{
private ISomething _something = null;
public MyProcess(ISomething something)
{
_something = something;
}
public void PerformSomeProcess
{
//do some code here...
if (SomeInteralVariable == true)
_something.DoSomething();
// do some more stuff here if needed.
}
}
//... into my unit test classes
[Test]
public void TestPerformSomeProcess()
{
MockSomething something = new MockSomething();
MyProcess proc = new MyProcess(something);
proc.PerformSomeProcess();
Assert.IsTrue(something.SomethingCalled, "DoSomething() was not called on the ISomething object");
}
What's wrong with this code scenario? The code looks fine (aside from being strange names on objects that aren't real) and the unit test should pass... The problem is that I have previously called this behaviour testing and it is not truely behaviour testing. I've actually used state testing to make sure that a behaviour is happening. This in itself is not necessarily bad. It is important to understand the distinction between state based testing and behaviour based testing, though, so that you truely know what is being tested where.
What is state based testing?
As a general guideline, any time you see an nUnit (or other Assert based unit testing framework) Assert statement, you are doing state based testing. More specifically, a state based test is testing any value on an object that may or may not be modified by the execution of code. The check ensures that the value is what the developer expects it to be. In the above example, the "Assert.IsTrue" statement above is checking the state of the "something" object after a process has worked with the object. If the state of the object is what was expected, the test passes.
What is behaviour based testing?
Also known as "Interaction" based testing - this is where my mind had not been clear, previously. Behaviour testing is precisely what is sounds like - testing the behaviour, or process, of an object in a given scenario. The problem is - how do you test the behaviour of an object without introducing some state that can checked as a result of the behaviour? The simple answer is that you can't test behaviour without a state to examine. That doesn't mean we are out of luck and must always provide a state to be tested, though. Frameworks such as RhinoMocks can be used to provide the mock object functionality and behaviour testing that we are looking for, without us having to code a bunch of leaky abstractions like the code example above.
The difference between State and Behaviour testing
From Ayende's Guidelines to using Interaction Based Testing:
One of the major differences between interaction based and state based testing is the level that you are testing the code. State based testing is testing results of an operation, interaction based testing tests the operation itself. This means that you can get into situations where you are atrophied by the tests. Any change to the code causes a cascading affect on all the tests.
There are several blogs out there that already describe testing in much more detail and I would recommend reviewing them for further information on state based testing vs. behaviour based testing.
There are also plenty of good articles on using RhinoMocks to get up and running with both state and behaviour testing