TDD’ing the GUI
By jdawkins | July 31, 2007
Can’t be done, right?
We have some patterns that help us do this: MVP, Passive View, Supervising Controller, Presenter First. Rolling your own implementation of these patterns is challenging and time consuming. Additionally, we haven’t had any frameworks to make it any easier.
So that’s the problem I set out to solve. I’ve reached a point where I’ve been able create a proof of concept application with this framework I’ve been writing. I’m hoping to get some feedback from the community on this.
So what did I do?
I chose to solve the problem using the Passive View pattern. I wanted to be able to design a form with the Delphi form designer but I didn’t want to put any code in the form implementation. That left me with the problem of controlling the GUI from the controller object. Since the point of this exercise is to keep everything testable from a unit test I couldn’t have the controller interacting with the concrete GUI elements. So I came up with the following:
IView = interface
GetViewElement(const ID : string) : IViewElement;
end;
IView has the responsibility of providing access to the view elements to the controller. So the controller can interact with these IViewElement interfaces instead of the concrete GUI elements (like TButton, TEdit, etc.).
What I didn’t want is to have to use special VCL components to make this work. First, because it would be too much trouble to create a full suite of components that implemented IViewElement and second because I wanted to be able retro fit an existing application to this style of design. To accomplish this I did the following:
TGuiAdaptor = class(TInterfacedObject, IViewElement)
public
constructor Create(AControl : TControl)
end;
In GetViewElement the concrete view wraps concrete GUI elements with the TGuiAdaptor and returns it to the controller. So now we can choose to mock the view or, more appropriately to the purpose, use the concrete view for testing.
Having got this far I can now use TDD to express behavioral requirements in the form of unit tests. Have a look at the demo application included in the archive.
Why would I do this?
- I find that using TDD produces better code faster
- Automated testing has been my friend
- I hope I can convince someone at CodeGear that this is style of development is a Good Thing ™ and to enable the IDE to directly support this type of development
Be aware: This is a proof of concept release. I know there’s a great deal more work to go before this would be viable for production.
Here is the code: PassiveViewFramework.rar
Topics: XP, TDD | 7 Comments »
Executable stories
By jdawkins | June 19, 2007
For some time the (for lack of a better word) founder of BDD, Dan North, has been working on a BDD framework from Java called jBehave. The idea behind jBehave is to make stories and their associated scenarios executable. I’ve sent some time looking at jBehave and decided it looked a little too cumbersome for my taste. This week Dan released rBehave for the Ruby language. For some reason I’ve been able to sink my teeth into this more than jBehave. As such, I’ve started a Delphi implementation. Using Dans example story and scenarios:
Story: transfer to cash account
As a savings account holder
I want to transfer money from my savings account
So that I can get cash easily from an ATM
Scenario: savings account is in credit
Given my savings account balance is $100
And my cash account balance is $10
When I transfer $20
Then my savings account balance should be $80
And my cash account balance should be $30
Scenario: savings account is overdrawn
Given my savings account balance is -$20
And my cash account balance is $10
When I transfer $20
Then my savings account balance should be -$20
And my cash account balance should be $10
(For more on BDD stories see Dan North’s article What’s in a Story)
I’ve come up with some code that looks like this:
procedure TransferToCashAccount.SavingsAccountIsInCredit;
begin
Given('my savings account balance is', 100, SavingsAccountBalanceKey);
Given('my cash account balance is', 10, CashAccountBalanceKey);
When('I transfer', 20, DoTransfer);
&Then('my savings account balance should be', 80, SavingsAccountBalanceKey);
&Then('my cash account balance should be', 30, CashAccountBalanceKey);
end;
procedure TransferToCashAccount.SavingsAccountIsOverdrawn;
begin
Given('my savings account balance is', -20, SavingsAccountBalanceKey);
Given('my cash account balance is', 10, CashAccountBalanceKey);
When('I transfer', 20, DoTransfer);
&Then('my savings account balance should be', -20, SavingsAccountBalanceKey);
&Then('my cash account balance should be', 10, CashAccountBalanceKey);
end;
The elements in play are:
- Given - sets a value in the world (context)
- When - executes an action
- Then - verifies that a value in the world (context) is that which is specified
There is one other element that’s not immediately visible here: the World. The world is simple a holding area for values. In this case we store the values of the savings account balance and the cash account balance in the world for manipulation in the When action. The DoTransfer method looks like this:
procedure TransferToCashAccount.DoTransfer(Value: Variant);
var
TransferAmount,
SavingsAccountBalance,
CashAccountBalance : Currency;
AccountTransferObject : IAccountTransfer;
begin
TransferAmount := Value;
SavingsAccountBalance := World[SavingsAccountBalanceKey].Value;
CashAccountBalance := World[CashAccountBalanceKey].Value;
AccountTransferObject := TAccountTransferObject.Create;
AccountTransferObject.TransferAmount(SavingsAccountBalance, TransferAmount, CashAccountBalance);
World[SavingsAccountBalanceKey].Value := SavingsAccountBalance;
World[CashAccountBalanceKey].Value := CashAccountBalance;
end;
I built this on top of DUnit both for speed and the ability to mix these executable stories in with the unit tests or BDD specs used to implement the code. You can check out what I’ve done so far here:
dBehave.rar
What do you think?
Topics: BDD | 2 Comments »
Don’t test your privates!
By jdawkins | May 18, 2007
This blog post brought to you by the wonderful ambiguity of the English language!
And now back your regularly scheduled program ….
“How do I/Should I test private methods?”
This question seems to come up quite often. In this article I hope to convince that not only do you not need to test your private methods but that it is also bad practice.
Do what do I mean when I say “don’t test your private methods”? Am I telling you not to test code that might break? Not at all. What I mean is when you follow these two principles:
- Code from the outside-in
- Code to an interface, not an implementation
you will not need to go to any lengths to test your private methods. The first principle is why you won’t need to test private methods and the second is why it’s bad practice to test them. To better understand this lets look an example:
We’ll start with a test
procedure MyStack.ShouldPushItems;
begin
FStack.Push('foo');
CheckEquals(1, FStack.GetCount);
end;
And now lets fill in some code to make it compile:
type
TMyStack = class(TObject)
private
FData : TStringList;
procedure DoPush(const Value : string);
public
constructor Create;
destructor Destroy; override;
procedure Push(const Value : string);
function GetCount : Integer;
end;
implementation
procedure TMyStack.DoPush(const Value : string);
begin
FData.Insert(0, Value);
end;
procedure TMyStack.Push(const Value : string);
begin
DoPush(Value);
end;
function TMyStack.GetCount : Integer;
begin
Result := FData.Count;
end;
No surprises here (except that I left out the constructor and destructor code). The test now passes with this code. Now how do we make sure that the value we pushed onto the stack was actually stored? That is to say, how do we know DoPush() did its job? How do we test DoPush()? We can’t look at the internal storage because it’s private and we don’t want to break encapsulation for testing. We don’t yet have a Pop() method as our test didn’t call for it.
So what do we do? Right now our testing context is very generalized and non-specific; just a stack. Let see what changing our testing context could get us:
procedure MyStackWithOneItem.SetUp;
begin
FStack := TMyStack.Create;
FStack.Push('foo');
end;
procedure MyStackWithOneItem.ShouldHaveACountOfOne;
begin
CheckEquals(1, FStack.GetCount);
end;
procedure MyStackWithOneItem.ShouldPopThePushedItem;
begin
CheckEquals('foo', FStack.Pop);
end;
And now some code to make it compile:
{ this is a private method }
function TMyStack.DoPop : string;
begin
Result := FData[0];
FData.Delete(0);
end;
function TMyStack.Pop : string;
begin
Result := DoPop;
end;
So now our testing context is a stack with one item pushed onto it. We already know that ShouldHaveACountOfOne() will pass since we proved it in the previous iteration of the code. However, we still need to prove that the item we pushed onto the stack was indeed stored. That’s where ShouldPopThePushedItem() comes in. We’re able to infer that DoPush() did its job by verifying it using Pop().
But wait! What about DoPop()! We didn’t test that! You right … while we did check that the value returned from Pop() matched our pushed item there is one behavior of DoPop() that we didn’t check: the stack should now be empty. Let add a test:
procedure MyStackWithOneItem.ShouldBeEmptyAfterPop;
begin
FStack.Pop;
CheckEquals(0, FStack.GetCount);
end;
Remember that DUnit runs SetUp() before each test. ShouldBeEmptyAfterPop() will find the stack containing one item even though it executes after ShouldPopThePushedItem().
By changing our testing context we were able to verify that our private methods worked the way we expected them to.
Now, why is it bad practice to test private methods? Well lets say we made some decisions that led us to expose the internal data storage so that we could verify that Push() worked by accessing the internal storage. What happens when we refactor the code to use some other form of storage? This is where “Code to an interface, not an implementation” comes in. In our case Push(), Pop() and GetCount() make up the interface to the stacks implementation. Our test shouldn’t care about how the stack does its work any more than our customers care. As long as Push(), Pop() and GetCount() work the way they’re expected to work then all is well.
Given this arrangement let say that we decide to refactor the stack to store its data in database. I’ll leave the code for that as an exercise for the reader (!). Even though this is a large change to the code we can proceed with confidence. Why? Because our tests will tell us when we’ve broken something. Why can we rely on the tests? Because when we embarked on this big change the tests were passing and as we make this change, we don’t have to change the tests.
So, please, don’t test your privates. =)
Topics: BDD, XP | 1 Comment »
dSpec checked into SourceForge
By jdawkins | April 30, 2007
http://sourceforge.net/projects/dspec
You can get the very latest code from the SubVersion repository. While I haven’t released a new package of files yet you can get the latest and check out the new AgileDox stuff as well as some new specs for strings and integers.
Topics: BDD | 2 Comments »
AgileDox style output
By jdawkins | April 26, 2007
I really like having specs that read like plain English. Communicating behavior and intent between developers is very smooth this way. But why should we developers get to have all the fun? How about including your QA and analysts in the fun?
StringSpecification - Should have specs
–Specify.That(Boolean).Should.Equal(True)
StringSpecification - Should evaluate equality
–Specify.That(String).Should.Equal(Greetings Footpad!)
StringSpecification - Should evaluate inequality
–Specify.That(String).Should.Not.Equal()
StringSpecification - Should evaluate numeric
–Specify.That(String).Should.Not.Be.Numeric
StringSpecification - Should evaluate alpha
–Specify.That(String).Should.Be.Alpha
StringSpecification - Should evaluate alpha numeric
–Specify.That(String).Should.Be.AlphaNumeric
StringSpecification - Should evaluate beginning of strings
–Specify.That(String).Should.StartWith(Greetings)
StringSpecification - Should evaluate string contents
–Specify.That(String).Should.Contain(Foot)
StringSpecification - Should evaluate end of strings
–Specify.That(String).Should.EndWith(pad)
StringSpecification - Should have case insentive modifier
–Specify.That(String).Should.Equal(GREETINGS FOOTPAD!).IgnoringCase
StringSpecification - Should evaluate empty
–Specify.That(String).Should.Not.Be.Empty
(The actual output is formatted nicer than what I can reproduce in the WordPress editor)
I’ve been playing around with AgileDox style output from dSpec. If you’ve gone to all the trouble to represent your requirements as test cases why not have the test cases output documentation?
What do you think?
Update -
Ooooo - or how about this: an optional ContextDescription you can use to describe what you’re specifing
StringSpecification - Should evaluate empty
–When a string = ‘Greetings Footpad!’, Specify.That(String).Should.Not.Be.Empty
Topics: BDD | 1 Comment »
dSpec - BDD for Delphi
By jdawkins | April 20, 2007
Released under the Mozilla Public License 1.0 (MPL).
This release adds specifiers for Interfaces: Support() and ImplementedBy().
Specify.That(MyIntfList).Should.Support(IInterfaceList);
Specify.That(MyIntfList).Should.Be.ImplementedBy(TInterfaceList);
See the specs in the specification folder for the full list functionality.
Have fun!
Topics: BDD | 3 Comments »
SimpleMock
By jdawkins | March 22, 2007
I recently had occasion to demonstrate how to write a very lightweight mocking framework. The result was SimpleMock. SimpleMock does four things:
- Reports failure when no expectations are set
- Reports failure when expectations are not met
- Reports failure when unexpected calls are found
- Succeeds when all expectations are met
- Allows you specify if expectations are positionally sensitive or not
SimpleMock will not do the following:
- Set return values for functions
- Mock parameters
- Or any of the other really cool things that PascalMock1.1 will do
Tests are included in the archive. These will show you how to use it.
Download the code here: SimpleMock.zip
Updated to added behavior #5 above. I recently had a reason to not care in what order method expectations are called.
Topics: XP, Mocks | 2 Comments »
Code for today
By jdawkins | March 15, 2007
So we have this whiz-bang app for tracking whosits and we’ve decided we need to also track whatsits. So, we need a list. We could just use a string list for now.
“But wait!”, you say, “We should use a custom list object for this! You know very well that you’ll need it.”
Maybe. Maybe not.
You can’t know today what you’ll know tomorrow. That’s why we have the precept “Code and design for today”.
We’ve got two different approaches. Just so we have a point of reference lets refer to the custom list decision as BDUF (big design up front) and the generic list as YAGNI (you ain’t gonna need it).
Let’s stipulate that we decide upfront that we need this custom list object instead of making due with a generic one. Let’s also stipulate that after 10 “units” of production code we’re going to later discover that we took the wrong approach and have change directions. How would it look if we compared the BDUF approach with the YAGNI approach? Let’s see:
Let’s say that …
Pluses represent time working on production code (code that’s being continuously integrated and reviewed).
Question marks represent the phase during which we found out we took the wrong approach and have to change direction.
Minuses represent the time developing the custom list object.
Here is the time line for BDUF approach with the custom list object:
1234567890123456789012345678901234567890 ----------++++++++++??????????++++++++++
Here is the time line for the YAGNI approach making due with a generic, non-customized list:
1234567890123456789012345678901234567890 ++++++++++??????????++++++++++
In both cases we get 10 pluses before we find out we have to change approaches, 10 question marks for changing the approach and 10 more pluses to finish the work.
Why would YAGNI finish first? Because we can find out that we took the wrong approach earlier. Why? Because the code is getting into the system faster and being reviewed more often.
Deciding up front that we need this or that is a common trap that we as developers fall into. It’s easy to decide that we know everything we need to now and begin making plans based on that information. I’m not saying don’t make plans. I’m saying to commit to as little as possible. Make your plans as short range as you can. In the above time lines we had to have 10 “plus” units before the customer says “Oh, when I said A, I meant B”. The faster we got to the tenth plus the sooner we could get the customer feedback.
So … release early, release often, and code for today.
Topics: XP | No Comments »
dSpec update
By jdawkins | March 5, 2007
Grab the code here: dSpec.rar
What’s new?
First, some semantics. In the last iteration you said:
ShouldEqual()
ShouldNotEqual()
Now you say:
Should.Equal()
Should.Not_.Equal()
You can use “Not_” on any specifier. Additionally you can now say:
Should.Be.GreaterThan().And_.Be.LessThan()
You can string together specifiers using “And_”.
I’ve added some new specifiers:
GreaterThan()
LessThan()
AtLeast()
AtMost()
Nil_()
Assigned()
DecendFrom()
OfType()
True()
False()
Also, all specifiers can be modified where before only ShouldEqual() could be modified.Some examples of some wild specs:
TheNumber := 3;
Specify.That(TheNumber).Should.Equal(3).And_.Not_.Equal(2).And_.Not_.Equal(4); // 5 is right out!Specify.That(False).Should.Not_.Be.True.And_.Be.False.And_.Not_.Be.True;
Specify.That(42).Should.Be.AtLeast(40).And_.Not_.Be.GreaterThan(50).And_.Be.LessThan(50);
Have fun!
Topics: BDD | 2 Comments »
dSpec alpha
By jdawkins | February 23, 2007
Release early and release often … =)
I’ve uploaded my first go-around here: http://www.delphixtreme.com/downloads/dspec/dSpec.rar
dSpec is an extension of the DUnit framework. Behavior specifications are creating by descending from TContext (instead of TTestCase) and using published methods to execute the specifications (just like in DUnit). As discussed in the previous blog entry you assert behavior using the Specify.That method instead of using CheckEquals. Where before we would have said:
if WeAreInSectorZZ9PluralZAlpha then
CheckNotEquals(42, TheAnwserToLifeTheUniverseAndEveryThing)
else
CheckEquals(42, TheAnwserToLifeTheUniverseAndEverything)
We can now say:
Specify.That(TheAnwserToLifeTheUniverseAndEverything).ShouldEqual(42).Unless(WeAreInSectorZZ9PluralZAlpha);
(that line was a little too long for this blog theme. See the example project for the whole Hitchhikers Guide to the Galaxy joke =P )
As you can see we can modify the specification “ShouldEqual” with “Unless”. We can even say:
Specify.That(f).ShouldEqual(3.5).WithAToleranceOf(0.01).Unless(ToleranceIsInvalid)
Here we string together many modifiers.
See the dSpecBehaviors project in the specifications folder of the archive for more examples.
Remember this is a first stab. I only implemented enough to show how this sort of thing might work. I’m interested in hearing your comments.
Topics: BDD | No Comments »