There's a lot of talk about Design By Contract (DBC) out there in the development world. Various development languages have varying support for it, but more importantly various processes have various levels of support for it.
It seems, though, that the farther down the path of development we travel, the more important it is for us to consider DBC in the code that we write. Large projects with multiple developers are in great need of DBC. Projects that have publicly distributed API's are in even greater need of DBC. Even if you are working on a simple, one person project for yourself, and you are the only one that will ever use it's methods and objects, I'm willing to bet that you will forget about the assumptions that you are making when writing the methods and objects, at some point.
So where does this distinct need for DBC leave us, in the world of .NET (C#, VB, and the other "common" .NET languages)? We still need a way to enforce DBC, but our language of choice doesn't support it, natively. So we have two real choices (excluding DSL writing, and/or switching languages) - documentation (via code comments or written / published documentation) or Unit Tests.
Yes, that's right - Unit Tests are not just for testing, anymore. Or more correctly, the tests executed by unit testing are not just for the sake of testing. The intention is verify the pre and post conditions of a design-by-contract. Of course, I'm not the first one to suggest this. It's mentioned briefly in "Agile Principles, Patterns, and Practices in C#" by Robert C. Martin and countless other times as well.
I am propose a new unit testing framework. I know, I know... "not ANOTHER xUnit framework... *sigh* ". In this case, I am proposing a semantic change along with the mechanical (syntax) change, specifically for the purpose of introducing unit testing to a group of developers that may not believe in "Unit Testing".
As an example of my proposed syntax, in a file called "SomeContract.cs", this test code would exist:
[Conditional]
public class WhenSomeConditionIsMet
{
SomeValue someValue;
[PreCondition]
public void PreCondition()
{ Setup.My.Inputs inputs = Here;
}
[Execution]
public void Execution()
{ someValue = Execute.TheContract.With(inputs);
}
[PostCondition]
public void ThenSomeOutputIsSomeValue()
{ Assert.That(someValue).Equals("Some Known Value"); }
[PostCondition]
public void ThenSomeOutputIsSomeValue()
{ Assert.That(someValue).DoesNotEqual("Some Unknown Value"); }
}
To make this proposed syntax easier to understand, I'm basing it on the NUnit style of using attributes, at the moment; but it doesn't have to be that way. There is almost a one to one translation between NUnit and this.
- The [Conditional] attribute is equivalent to [TestFixture]
- The [PreCondition] attribute is equivalent to [TestFixtureSetup]
- The [Execution] attribute is equivalent to [Setup]
- The [PostCondition] attribute is equivalent to [Test]
In this simple example, I don't have an equivalent to [Teardown] or [TestFixtureTearDown]. I'm sure I'll need those at some point, but until I see the need, I'm not going to worry about them. I also don't really care about the Assert syntax. I'm just putting that syntax in place to illustrate the point.
Where I differ from typical NUnit style of testing is that I want to see a single "PreCondition" and "Execution" per test fixture, and have multiple "PostConditions" that only contain the assert statements. This style of test code more closely resembles that of Scott Bellware's SpecUnit.NET and for good reason - I like it.
I'm a fan of having as little as possible in the method that does the assert - keep it simple and explicit.
The biggest problem I have with my proposed syntax is a problem inherent to Design By Contract - the idea that you know the object (contract) being executed. A huge part of why I love Behavior/Specification Testing vs. Unit Testing is that Unit Tests and TestFixtures typically tell you that for class/file "XYZ.cs" you have "TestFixtureXYZ.cs". Whereas, Behavior/Specification Testing says that we have "BehaviorSpecification.cs" regardless of the classes used to implement it. I love this about Behavior Driven Development - it freed my mind from the horrible constraints that I saw in standard Unit Testing / TestFixtures. Unfortunately, Design By Contract basically takes us right back to the same place. We are specifying contract (class) "XYZ" so we have a "XYZContract.cs" file to hold all of our [Conditional]s.
...
Does anyone else see any value in this style of unit testing? I can certainly see scenarios where this would appeal to some developers more than the standard xUnit frameworks.