Login | Register   
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Achieve the Best of Two Worlds with Behavior-Driven Development : Page 4

Both Extreme Programming and Scrum are more effective when practiced together—and even more effective when combined to form Behavior-Driven Development.


advertisement
An Example
Starting with the first criteria, the amount of the transfer is debited from the "from" account and credited to the "to" account, here's a skeleton of the specification written in code:

[Concern("Funds transfer")] public class when_transferring_between_two_accounts { [Observation] public void should_debit_the_from_account_by_the_amount_transferred() { } [Observation] public void should_credit_the_to_account_by_the_amount_transferred() { } }

This class represents the specific context and observations that can be made about the specification.

It is also a standard NUnit test class and will be run by the NUnit test runners. Any assertions used in the implementation of the test methods will push information to the test runners and be included in NUnit output.

Note that this ability is in the current NUnit source, which hasn't been released as an official build. You can build this version of NUnit from source, or you could borrow the NUnit binaries that are in the SpecUnit .NET source tree.

The ConcernAttribute on the class definition is a subclass of NUnit's TestFixtureAttribute, and the ObservationAttribute is a subclass of NUnit's TestAttribute.

These attributes are part of the SpecUnit .NET helpers for NUnit. You can find the source code and binaries on Google Code.

SpecUnit .NET has a tool that produces an HTML report of the specifications. The tool is executed against the assembly that contains the specifications. I've named the specification assembly Banking.Specs.dll. You can produce the report at the command line with:

 
Figure 1. Report Specifications: Here's the report for the specifications so far.

SpecUnit.Report.exe Banking.Specs.dll

Figure 1 shows the report for the specifications so far.

You should write only one specification or observation method at a time. I wrote two above just as an example.

Here are the implementations for the first two specifications:



[Observation] public void should_debit_the_from_account_by_the_amount_transferred() { Account fromAccount = new Account { Balance = 1m }; Account toAccount = new Account { Balance = 1m }; // because fromAccount.Transfer(1m, toAccount); fromAccount.Balance.ShouldEqual(0m); } [Observation] public void should_credit_the_to_account_by_the_amount_transferred() { Account fromAccount = new Account { Balance = 1m }; Account toAccount = new Account { Balance = 1m }; // because fromAccount.Transfer(1m, toAccount); toAccount.Balance.ShouldEqual(2m); }

These specification methods share some common code—and in this case, the common code isn't really a representation of the observations themselves. That is, there are only two lines from the specification methods that are C# versions of the observations as expressed in the method names. They are:

fromAccount.Balance.ShouldEqual(0m);

and:

toAccount.Balance.ShouldEqual(2m);

The following code shows what the observations look like:

[Observation] public void should_debit_the_from_account_by_the_amount_transferred() { _fromAccount.Balance.ShouldEqual(0m); } [Observation] public void should_credit_the_to_account_by_the_amount_transferred() { _toAccount.Balance.ShouldEqual(2m); }

In both cases, the specification's methods present the exact code required to make the observation—no more and no less. This is the ideal specification code. It leads to much-improved experiences in understanding the system and the code itself.

The code below shows what the entire context becomes when you move the common code out of the specification methods:

[Concern("Funds transfer")] public class when_transferring_between_two_accounts : ContextSpecification { private Account _fromAccount; private Account _toAccount; protected override void Context() { _fromAccount = new Account { Balance = 1m }; _toAccount = new Account { Balance = 1m }; } protected override void Because() { _fromAccount.Transfer(1m, _toAccount); } [Observation] public void should_debit_the_from_account_by_the_amount_transferred() { _fromAccount.Balance.ShouldEqual(0m); } [Observation] public void should_credit_the_to_account_by_the_amount_transferred() { _toAccount.Balance.ShouldEqual(2m); } }

The Context() method above provides the resources needed to represent the context. The Because() method is the precise line of code that causes the subsequent observations to be made.

You could include the code in the Because() method in the Context() method, but isolating it in its own method calls out the exact core behavior for the specification in no uncertain terms, providing clearer documentation of the intent of the concern. Using the Because() method is completely optional (as is using the ContextSpecification base class), and you could just as easily move its contents to the Context() method if you prefer. Sometimes, only using the Context() method is more sensible and desirable.

This class's base class is ContextSpecification, which is included in the SpecUnit .NET binaries. Developers who use the Context/Specification style often create their own base class to provide more appropriate language to their specifications. You might find that you'd prefer to write your own.



Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap