Context/Spec style testing and my approach to BDD

I borrow heavily my approach to testing from a combination of Ayende’s Rhino Tools tests, and my reading of the Rspec beta book. But I think I’ve stumbled onto something I’m happy with and I can generate reports out of. Let’s go over some basic rules first:

  1. Move as much common setup logic to a base class as possible.
  2. Use your class name as the context
  3. methods are rules beginning with “should”
  4. create a new subclass of the base context every time you have a new scenario

Code ends up looking like so:

 

public class BaseAddVacationContext
{
protected AddVacationRequest _submission;
protected IEmailSender _sender;
protected IUserInformation _information;
protected ICrudRepo&ltLeaveRequest> _leaverepo;
protected LeaveRequest _request;
[SetUp]
public virtual void SetUp()
{
_sender = MockRepository.GenerateMock&ltIEmailSender>();
_information = MockRepository.GenerateMock&ltIUserInformation>();
_leaverepo = MockRepository.GenerateMock&ltICrudRepo&ltLeaveRequest>>();
_submission = new AddVacationRequest(_sender, _information, _leaverepo);
_request = new LeaveRequest() { UserName = "userman" };


}
}
[TestFixture]
public class SpecAddVacationRequestWhenEmployeeSubmitsRequest : BaseAddVacationContext
{


[SetUp]
public override void SetUp()
{
base.SetUp();
_information.Stub(x => x.GetManagersEmailAddresses("userman")).Return(new[] { "manager1@jonbank.com", "manager2@jonbank.com" });
_information.Stub(x => x.GetReviewersEmailAddress("userman")).Return(new[] { "james@jonbank.com", "jones@jonbank.com" });
_information.Stub(x => x.GetUserEmail("userman")).Return("userman@jonbank.com");
_submission.Execute(_request);

}
[Test]
public void should_email_all_managers()
{
_sender.AssertWasCalled(x => x.Send(Arg&ltMessage>.Matches(y => y.To == "manager1@jonbank.com")));
_sender.AssertWasCalled(x => x.Send(Arg&ltMessage>.Matches(y => y.To == "manager2@jonbank.com")));
}

[Test]
public void should_send_email_to_user()
{
_sender.AssertWasCalled(x => x.Send(Arg&ltMessage>.Matches(y => y.To == "userman@jonbank.com")));
}

[Test]
public void should_store_leave_request_in_database()
{
_leaverepo.AssertWasCalled(x=>x.Create(Arg<LeaveRequest>.Matches(u=>u == _request)));
}

[Test]
public void should_email_all_reviewers()
{
_sender.AssertWasCalled(x => x.Send(Arg<Message>.Matches(y => y.To == "jones@jonbank.com")));
_sender.AssertWasCalled(x => x.Send(Arg<Message>.Matches(y => y.To == "james@jonbank.com")));
}
}
[TestFixture]
public class SpecAddVacationRequestWhenRequesWasAlreadyMadeForThoseDays : BaseAddVacationContext
{


[SetUp]
public override void SetUp()
{
base.SetUp();

_leaverepo.Stub(x=>x.Create(null)).Throw(new EmployeeAlreadyRequestedTheseDaysOff()).IgnoreArguments();
}
[Test]
public void should_not_send_email_to_anyone(){

_sender.AssertWasNotCalled(x => x.Send(Arg<Message>.Is.Anything));
}
}

So here we have:

 

  • A setup that you need to override and call to setup context specific behavior
  • small small tests and asserts.
  • limited setup on mocks, you can use handrolled mocks or the real classes if you prefer (which I do often).
  • Use AssertWasCalled instead of .Expect() on my mocks

I’ll post more examples of this as they come up.

EDIT: typo in code and changes to show more than one context

Related Articles:

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

About Ryan Svihla

I consider myself a full stack polyglot, and I have been writing a lot of JS and Ruby as of late. Currently, I'm a solutions architect at DataStax
This entry was posted in BDD, Context, Spec. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Wookie

    Wouldn’t it be more readable for the stakeholders if the test fixture’s class name reflected what is the context in which things should happen, e.g. starting with “When..”?

    SpecAddVacationRequest sounds quite cryptic, IMO

  • Paco

    It might be more readable as well when the setup is split up in context and because

  • http://www.lostechies.com/members/rssvihla/default.aspx Ryan Svihla

    @Wookie

    Yes I normally do the When for the context, but usually only after the first scenario is baked then the naming is easier. I’ll update it later with the new scenario’s and maybe it’ll be closer to your thinking.

    @Paco

    Could you give me an example? do you mean like naming the SetUp methods BecauseUserWasAlreadyInTheSystem ?

    Good feedback guys I appreciate it.

  • http://www.lostechies.com/members/rssvihla/default.aspx Ryan Svihla

    @Wookie

    Updated code to reflect what I was talking about. If you still have comments or suggestions let me know.

    thanks again.

  • jlivesay

    Might you want to template the base context Setup method for clarity?

    [SetUp]
    public void SetUp()
    {
    // init common fields …
    _sender = …

    AddContextSpecificBehavior();
    }

    public abstract void AddContextSpecificBehavior();

    Then each new context would need to implement the AddContextSpecificBehavior method but not be tied to the base.Setup() call, which can be an easy thing to forget to do. The compiler forcing the implementation of the abstract method also is more explicit about how the pattern works.

  • http://www.lostechies.com/members/rssvihla/default.aspx Ryan Svihla

    @jlivesay depends on how I’m interpreting your example there:

    I considered and played with using a template for the base setup method when i started doing this. I ended up having the same calls a lot. for example when setting up context that has to be there for most scenarios like return values that are passed on to another method, or the MockRepository.GenerateMock calls over and over again.

    Now if you’re suggesting have a base abstract to help remind people to AddContextSpecific behavior I think you’re correct that would help matters out for my team and other future maintainers. I’d still need a base.SetUp() to setup all the normal boring stuff that doesn’t change from test to test.

  • Paco

    I don’t mean renaming the setup method, but I mean placing the executing of the method under test seperated from the context where it’s executed in.

    public class ContextSpec
    {
    public void SetUp()
    {
    Context();
    Because();
    }

    public virtual void Context() {}
    public virtual void Because() {}
    }

    I have done this for a while, but I’m using MSpec now.

  • http://www.lostechies.com/members/rssvihla/default.aspx Ryan Svihla

    @paco I’d be happier still with.

    public void SetUp()

    {
    //common stubbing and service instantiation
    Context();

    Because();

    }

    public virtual void Context() {}

    public virtual void Because() {}

    }

    and then i’m totally in agreement, nice touch.

  • Paco

    I use inheritance for that.
    public class When_creating_a_user : with_new_user_service
    {
    override Context()
    {
    DoSomeSpecificMocking();
    }

    Because()
    {
    user = service.CreateUser();
    }

    }

    public class with_new_user_service : ContextSpec
    {
    override Context()
    {
    DoCommonStubbingAndServiceInstantiation();
    }
    }

  • http://scottbellware.com Scott Bellware

    Not exactly what I had in mind when I started using “Context Specification” to describe the method I use with *spec tools.

    Solubility is not an option in context specification. I don’t find the descriptions above as soluble as they should be. Sure I can read them – they’re readable. But the focus is first on scannability. Don’t force me to have to read what should be scanned.

    Context specification is as much about code usability as it is adopting a new kind of testing framework.

  • http://www.lostechies.com/members/rssvihla/default.aspx Ryan Svihla

    @Scott

    Thanks for the constructive criticism and I appreciate you dropping by.

    My goal with this was to aim for something that could create a decent output while still structuring my tests in a way that I could dive into a rule when I needed to maintain something later. In that so far I’m happy, but I’m more than open to any improvements.

    I’m still an eager student with this stuff, would you care to enlighten me further on “scannability”? or can you point me to where I’m off specifically? Maybe a resource i need to read first?

  • http://www.lostechies.com/members/rssvihla/default.aspx Ryan Svihla

    @Scott

    Ok reading some of your older comments on other blogs, and thinking about it some more. Ultimately, I’m making too many trivialities viewable in the code.

    My setup methods should be hidden by a useful named factory, and anyone should be able to scan my test instantly and not be distracted by the extra noise going on, further analysis would just involved inspecting the method.

    Naming of that factory or object mother would be a key import trick and something I’d have to practice to get right.

    I’ll take everyone’s advice here to heart try some experiments in my day to day code and see what I come up with. once I’m done I’ll try and post a beginner focused BDD post.