To address some odd concerns about differences between DUnit and Smoketest, I thought it would be useful to demonstrate how it is entirely within the gift of a Smoketest user to create their own “comfort” layer, to make using Smoketest more similar to the DUnit framework if they wish (though why in that case they wouldn’t simply use DUnit, I can’t quite fathom. But still).
vs and Inheritance
The great thing about Delphi is that when a class implements an interface any derived classes inherit that implementation. Even better, if you implement the interface methods virtually, then those derived classes can override the implementation without having to redeclare the interface.
So, if you really don’t like the idea of declaring and implementing interfaces to provide Setup and Cleanup methods on TTestCase derived classes you have a very simple remedy:
Implement your own class from which to derive your test cases and have this provide default, no-op, virtual implementations of these framework hooks, just as the DUnit test case base class does for the virtual methods it relies on.
If it really bothers you that much, you can even take this opportunity to replace the framework Cleanup method with a virtual TearDown method in it’s stead.
Heck, if you really want to write CheckEquals() for your test and then describe it separately, you can even provide that in your DUnit comfortable compatibility class. You can even have the CheckEquals() method always “fail early”, just like DUnit.
You might end up with something like this:
type TDUnitTestCase = class(TTestCase, ISetupTestCase, ICleanupTestCase) protected procedure Setup; virtual; procedure Cleanup; procedure TearDown; virtual; procedure CheckEquals(aResult: Boolean; aMsg: String = ''); end; procedure TDUnitTestCase.Setup; begin // NO-OP - just like mother used to make end; procedure TDUnitTestCase.Cleanup; begin TearDown; end; procedure TDUnitTestCase.TearDown; begin // NO-OP - the comfort of the familiar end; procedure TDUnitTestCase.CheckEquals(aResult: Boolean; aMsg: String); begin Test(aMsg).Expect(aResult).IsTRUE.IsShowStopper; end;
You could even implement the same pattern for the project and method specific setup and cleanup hooks. DUnit doesn’t have these so doesn’t provide a “specification” to comply with in this area, but you would presumably choose to follow the same virtual method pattern that DUnit adopts for the test case setup/teardown.
Why Not Just Copy DUnit ?
Some people don’t seem to understand why I didn’t simply mindlessly copy DUnit, thus making such adaptations unnecessary.
The thing is, I didn’t implement Smoketest with compatibility with DUnit in mind. Quite the opposite. I didn’t like the way DUnit worked! Little wonder then that Smoketest works differently!
I like to think however that the approach I took demonstrates it’s benefits no more obviously than when considering the ease with which it is possible to build such alternative API’s on top of the framework without having to resort to modifying the framework itself.
I imagine – though I have not tried – that you could even contrive to make such a compatibility layer so seamless and complete that an existing DUnit project would compile directly against Smoketest with only the slightest of changes, primarily around not having to explicitly state which “runner” you are using, since in Smoketest this is governed by the project compiler settings/directive directly (console vs GUI app) without the need for parallel changes to the project source code.
In the case of DUnit, it was impossible to achieve the changes in the test writing experience that I desired; you cannot reduce the visibility of inherited members, so with any pattern that relies on visibility of certain inherited class members you are stuck with the choices made in the implementation of the base class.
Smoketest doesn’t paint you into this particular corner.
For people – such as myself – who don’t like DUnit, Smoketest exists precisely to offer a choice. A choice albeit initially only I was making (in the sense of deciding how my preferred alternative would work differently).
If someone doesn’t like the choices I made it seems to me they themselves have a choice: They can – with a modicum of thought and a minimum of effort – add a layer of comfort that they will find acceptable. Or they can choose simply not to use Smoketest.
I would not presume to tell them what choice they should make. 😉