.Net Behavior Driven Development Using NUnit—Next Steps

Oren Eini left a comment on a recent post I did on Behavior Driven Development.  At first the questions seemed very easy and I thought I would be able to answer it within 15 minutes but as pondered the different possibilities of answering the question, I wanted to make sure that I gave an answer that was true to BDD as it relates to C#.  A day later, I finally came up with one but in the process I learned a great deal about the value of “context” and “behavior”, so I wanted to take this time to share what I have learned.


The questions Oren asked:


How would you handle a case such as:
The entity is valid IFF:
- The name is not empty
- The Date is greater than last year
- At least one of the following contact methods must be filled (phone, mobile, email)


Seems pretty easy right?  Well let’s take from a TDD perspective first.


Test Driven Development


This is a pretty basic test but let’s examine its structure more than what it is asserting.

    [TestFixture]    public class PersonValidatorTest    {        [Test]        public void ValidateThePersonWhenEmpty()        {            Person person = new Person();             person.Name = null;            person.WhatEverDate = DateTime.Today.Subtract(new TimeSpan(362));            person.Phone = null;            person.Mobile = null;            person.Email = null;             Assert.IsFalse(PersonValidator.IsValid(person));        }

What can we gather from this test?


·         We know from the test fixture name that this class contains test about the PersonValidator class


·         We know that the test has to do with validating the person object when it is empty


·         We know that the test sets up a person object and sets all the accessors to null and sets the date property to 1 year from today


·         We know that the test call the IsValid static method on the PersonValidator objects and asserts that the result is false


Hmm… There is a lot going on here in this simple test.  But can anyone tell me “Why?” you are doing this test?  Why are you asserting that the PersonValidator specification validates the Person object?  You may know implicitly why you are creating this test but a year later would you know why?  Ok maybe I am getting a little too deep here but the premise is we have lost something during TDD.


So how do we clarify the test to know the “Why”


I am going to have to expand on Oren’s question a little to gain greater incite.


Taking a cue from Dan North again, we use the following template:

 Scenario 1: Person information is filled in

Given the user has filled out a person’s information


When the user is about to save the persons information


Then make sure the name is not empty


and the date is greater than the last year


and at least one of the following contact fields must be filled in (Phone, Mobile, Email)

 

Let’s see how we would write a specification that captures the behavior of PersonValidator.  Consider the following:

namespace Domain.PersonValidatorSpecifcation{    [TestFixture]    public class APersonWithInformationFilledIn    {        [Test]        public void MakeSureTheNameIsNotEmpty()        {                    }    }}

This BDD example gives us quite a bit of information.


·         According to the namespace all of the “contexts” in this file deal with the PersonValidator object


·         We look at the test fixture to determine the context, in this case it is “A person with information filled in”


·         The first specification “Make sure the name is not empty”


So let’s read it as if it would appear in the test runner:

Domain.PersonValidatorSpecificaton.APersonWithInformationFilledIn.MakeSureTheNameIsNotEmpty

I don’t know about you but that doesn’t read well to me, it needs to more explicit about why we need this specification.  Remember we are declaring the Person object invalid if it is in this context. Does the spec name explain that concept? How about this?

Domain.PersonValidatorSpecificaton.APersonWithInformationFilledIn.ShouldBeInValidIfTheNameIsBlank

That’s much better.


So now the code reads like this:

namespace Domain.PersonValidatorSpecifcation{    [TestFixture]    public class APersonWithInformationFilledIn    {        [Test]        public void ShouldBeInValidIfTheNameIsBlank()        {                    }    }}

So like traditional TDD you go through the first two phases “Red”, “Green” But let’s talk about the “Refactoring” exercise.  (I am not going to go into the actual validator code let’s just assume it does what it says.)

namespace Domain.PersonValidatorSpecifcation{    [TestFixture]    public class APersonWithInformationFilledIn    {        [Test]        public void ShouldBeInValidIfTheNameIsBlank()        {            Person person = new Person();                        person.Name = null;            person.WhatEverDate = DateTime.Today.Subtract(TimeSpan.FromDays(367));            person.Phone = null;            person.Mobile = null;            person.Email = null;             Assert.IsFalse(PersonValidator.IsValid(person));        }    }}

This test works as is but this isn’t the only specification we are going to write and I don’t want to create the Person object every time. So let’s refactor. Remember unlike traditional TDD we are asserting within a context in BDD.  The context of this fixture is “A person with information filled in” so we need to “SetUp” the context.

namespace Domain.PersonValidatorSpecifcation{    [TestFixture]    public class APersonWithInformationFilledIn    {        private Person person;          [SetUp]        public void SetUp()        {            person = new Person();            person.Name = null;            person.WhatEverDate = DateTime.Today.Subtract(TimeSpan.FromDays(367));            person.Phone = null;            person.Mobile = null;            person.Email = null;         }         [Test]        public void ShouldBeInValidIfTheNameIsBlank()        {            Assert.IsFalse(PersonValidator.IsValid(person));        }    }}

Well that’s a lot better but wait a minute.  The context reads “A person with information filled in” according to our setup there isn’t anything filled in.  We must stay true to the context. So let’s continue to refactor.

namespace Domain.PersonValidatorSpecifcation{    [TestFixture]    public class APersonWithInformationFilledIn    {        private Person person;          [SetUp]        public void SetUp()        {            person = new Person();                        person.Name = “Joe Smith”;            person.WhatEverDate = DateTime.Today.Subtract(TimeSpan.FromDays(367));            person.Phone = “555-5555″;            person.Mobile = “555-5555″;            person.Email = “555-5555″;         }         [Test]        public void ShouldBeInValidIfTheNameIsBlank()        {            Assert.IsFalse(PersonValidator.IsValid(person);        }    }}

Much better but wait, you run the unit test and it fails. You need to clear the Name property since this is what you are specifying on.

namespace Domain.PersonValidatorSpecifcation{    [TestFixture]    public class APersonWithInformationFilledIn    {        private Person person;          [SetUp]        public void SetUp()        {            person = new Person();                        person.Name = “Joe Smith”;            person.WhatEverDate = DateTime.Today.Subtract(TimeSpan.FromDays(367));            person.Phone = “555-5555″;            person.Mobile = “555-5555″;            person.Email = “555-5555″;         }         [Test]        public void ShouldBeInValidIfTheNameIsBlank()        {            person.Name = null;            Assert.IsFalse(PersonValidator.IsValid(person));        }}}

Now everything passes again but let’s talk about passing. We know that the person object “ShouldBeInValidIfTheNameIsBlank” so can we infer that if it isn’t blank that it should pass.  I believe we can as long as the domain specifications allow it and in this case they do.  So you can assert on both aspects of the specification since you have isolated the context around the spec.

namespace Domain.PersonValidatorSpecifcation{    [TestFixture]    public class APersonWithInformationFilledIn    {        private Person person;          [SetUp]        public void SetUp()        {            person = new Person();                        person.Name = “Joe Smith”;            person.WhatEverDate = DateTime.Today.Subtract(TimeSpan.FromDays(367));            person.Phone = “555-5555″;            person.Mobile = “555-5555″;            person.Email = “555-5555″;         }         [Test]        public void ShouldBeInValidIfTheNameIsBlank()        {            person.Name = null;            Assert.IsFalse(PersonValidator.IsValid(person));            person.Name = “John Glenn”;            Assert.IsTrue(PersonValidator.IsValid(person));        }}}

At this point you I think we can finish the rest of the specifications.

namespace Domain.PersonValidatorSpecifcation{    [TestFixture]    public class APersonWithInformationFilledIn    {        private Person person;          [SetUp]        public void SetUp()        {            person = new Person();                        person.Name = “Joe Smith”;            person.WhatEverDate = DateTime.Today.Subtract(TimeSpan.FromDays(367));            person.Phone = “555-5555″;            person.Mobile = “555-5555″;            person.Email = “billy@bob.com”;         }         [Test]        public void ShouldBeInValidIfTheNameIsBlank()        {            person.Name = null;            Assert.IsFalse(PersonValidator.IsValid(person));            person.Name = “John Glenn”;            Assert.IsTrue(PersonValidator.IsValid(person));        }         [Test]        public void ShouldBeInValidIfTheWhatEverDateIsGreaterThanLastYear()        {            person.WhatEverDate = DateTime.Today;            Assert.IsFalse(PersonValidator.IsValid(person));            person.WhatEverDate = DateTime.Today.Subtract(TimeSpan.FromDays(367));            Assert.IsTrue(PersonValidator.IsValid(person));        }         [Test]        public void ShouldBeInValidIfAtLeastOneOfTheFollowingFieldsIsBlankPhoneMobileEmail()        {            person.Phone = null;            Assert.IsFalse(PersonValidator.IsValid(person));            person.Phone = “555-5555″;            Assert.IsTrue(PersonValidator.IsValid(person));             person.Mobile = null;            Assert.IsFalse(PersonValidator.IsValid(person));            person.Mobile = “555-5555″;            Assert.IsTrue(PersonValidator.IsValid(person));             person.Email = null;            Assert.IsFalse(PersonValidator.IsValid(person));            person.Email = “Test@test.com”;            Assert.IsTrue(PersonValidator.IsValid(person));         }    }}It is important to note that BDD allows the context and specifications of the test to be easily shared.  The next steps would be to use the XML that NUnit produces and create a style sheet that will display the following information similar to AgileDox.Person Validator Specifcation-    A person with information filled in should be inValid if the name is blank : passed should be inValid if the what ever date is greater than last year : passed should be inValid if at least one of the following fields is blank phone mobile email : passed

Summary


I know I have covered a lot of concepts in this post but my hope, is that I sparked interest into the realm of Behavior Driven Development.  I want to mention that by no means am I saying that this is the defacto way to practice BDD is .Net but merely an idea or concept that helps to gain the acceptance and value of BDD within the .Net community.


Guidelines to BDD in .Net


·               Utilize the Namespace to categorize the domain entities


·               The class names should convey the context of the specification you are asserting.


·               Keep the SetUp of the test fixture true to the context.


·               The method name should convey the specification you are asserting.


·               Stay true to the principles of TDD Red, Green, Refactor!

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Joe Ocampo

My personal philosophy is simple: "Have a good strategy that sets the environment for success through the enablement of the whole. Be agile but with a mind towards pragmatism. Delegate to the best qualified individuals, but don’t be afraid to involve yourself in all parts of a job. Treat everyone with respect, humility, and with a genuine pursuit towards excellence." Respected business and technical leader with expertise in directing organization towards effective results driven outcomes. Proven ability to perform and communicate from both technical and business perspectives. Strong technical and business acumen developed through experience, education and training. Provides the ability to utilize technology, harness business intelligence and execute strategically by optimizing systems, tools and process. Passionate about building people, companies and software by containing cost, maximizing operational throughput and capitalize on revenue. Looks to leverage the strengths of individuals and grow the organization to their maximum potential by harnessing the power of their collective whole and deliver results. Co-Founder of LosTechies.com
This entry was posted in BDD (Behavior Driven Development). Bookmark the permalink. Follow any comments here with the RSS feed for this post.

3 Responses to .Net Behavior Driven Development Using NUnit—Next Steps

  1. joeyDotNet says:

    Comment/question on the code sample itself. Up until now I’ve tried to keep as much validation as possible inside my domain objects (somewhat similar to the way Nilsson demonstrates) which in some ways is nice, but I feel it’s starting to violate SRP more than I want.

    I know it’s impossible to generalize something like this and the answer is usually context-sensitive, but have you found that keeping validation outside of the domain objects to be more maintainable in your experience?

  2. agilejoe says:

    In a word Absolutely!
    When I am modeling a domain I pay special attention to treat Entities and Value object as simple data containers. When the customer asks for a rule to determine the validity of an Entity, I model this concept as a specification or constraint. The beauty of this concept is that specification or constraint are simply predicates that can be chained together using “&&” or “||” operators to determine an outcome. Evans discusses this in the supple design section of the book.

    Remember the most important concept is that the model express what is going on in the domain. If you hide actions inside of Entities you can never refer to them in the model as the “PersonSpecificaiton”. The architectural side-effect is that it is much easier to test. I can code my PersonSepcification and pass in a mock Person type to determine if it is valid. The Person type is clueless to this validation.

    Don’t forget about Services as well, using the old Order, line item model. You can pass the Order to a CalcualteOrderTotalService to determine the order total. Same architectural side-effect as before.

    Hope that helps?

    Happy coding!

  3. joeyDotNet says:

    Right, yeah I’ve used specifications quite a bit for both generic (empty strings, dates, etc.) and for specific business rules. But up until now I’ve usually put a IsValid and ValidationErrors properties on the actual domain objects that internally use specifications (either as nested class or external) to actually perform the validation. But I can definitely see the benefits of moving that responsibility to its own validator objects, especially to simplify the tests.

    Thanks!