If you’ve never done Test Driven Development or aren’t even sure what this "crazy TDD stuff” is all about than this is the series for you. Over the next 30 days this series of posts take you from “I can spell TDD” to being able to consider yourself a “functional” TDD developer. Of course TDD is a very deep topic and truly mastering it will take quite a bit of time, but the rewards are well worth it. Along the way I’ll be showing you how tools like JustCode and JustMock can help you in your practice of TDD.

Previous Posts in this Series:  Day 13 – More Stub Features

The last couple posts have seen us using basic Stubs to mock out the functionality a class that our code under test is dependent on. We focused on dealing with an external resource and being able to replicate enough of that resources functionality to satisfy our test. In this post I’ll introduce a new, simple test case. Over this and the next post you’ll see that not all “simple” test cases are so simple.


Another Day, Another Test Case

For this post we’ll be continuing with our TDD Store example from the last post. As development continues, we continue to derive test cases from our requirement to complete our feature. The next test case I’ve created is
When a user places an order for an item and the quantity ordered is zero, then an InvalidOrderException should be thrown.
On the surface, this seems like a very simple requirement; each order has a list of items, if any of the item in the list have a zero quantity then throw an exception. In most cases we would expect some sort of message to come back with the exception explaining what the issue is, but for now to keep the example simple, we’ll ignore that best practice (it will be addressed in a future post). Seems pretty simple, right? Well then let’s write our test. We can start with the basics:
 
 1:         [Test]
 2:         [ExpectedException(typeof(InvalidOrderException))]
 3:  public void WhenAUserAttemptsToOrderAnItemWithAQuantityOfZeroThrowInvalidOrderException()
 4:         {
 5:  //Arrange
 6:  
 7:  //Act
 8:  
 9:  //Assert
 10:  
 11:         }
(get code sample)

This is about as basic as we can get at the moment. If you’ve been reading this series all along there is nothing here you haven’t seen already. We define our test with the Test attribute and with the ExpectedException attribute we are telling NUnit that we expect this test to throw an exception of type InvalidOrderException. We even have some comments to remind us of the Arrange, Act and Assert (AAA) pattern. Many TDD developers like to start with the Arrange section and there’s nothing wrong with that. Personally, I like to start with the Assert block and work backwards. I like to start by writing my assertions, which I should be able to derive from my test case. By having those first I know what I need to call in the Act section to get the values I need to verify my test works. And by writing my Act second, I can make sure that in my Arrange section I’m only setting up the variables, mocks and stubs that I absolutely need to call my code under test from my Act section. For this test NUnit is doing a lot of the asserting for me by virtue of the ExpectedException attribute. I’m still going to need to supply a stub for my OrderDataService. And since in this case since I’m expecting an exception to be thrown and an order not to be placed, my expectation is that the Save method is not called at all (if it is, I’m in big trouble!). This is another example of using my unit tests to not only verify my business logic, but also my non-functional requirements:

 1:         [Test]
 2:         [ExpectedException(typeof(InvalidOrderException))]
 3:  public void WhenAUserAttemptsToOrderAnItemWithAQuantityOfZeroThrowInvalidOrderException()
 4:         {
 5:  //Arrange
 6:  
 7:  //Act
 8:  
 9:  //Assert
 10:             Mock.Assert(orderDataService);
 11:         }
(get sample code)

So far, so good. Next I need to write my Act section:

 1:         [Test]
 2:         [ExpectedException(typeof(InvalidOrderException))]
 3:  public void WhenAUserAttemptsToOrderAnItemWithAQuantityOfZeroThrowInvalidOrderException()
 4:         {
 5:  //Arrange
 6:  
 7:  //Act
 8:             orderService.PlaceOrder(customerId, shoppingCart);
 9:  
 10:  //Assert
 11:             Mock.Assert(orderDataService);
 12:         }
(get sample code)

In the previous example I captured the result of the PlaceOrder method and used it in my Assert block. In this case I’m expecting an exception, so the result is trivial. Now comes the Arrange section. Looking at my code I can see that the first thing I need is an instance of my OrderService. My other test also uses an instance of the OrderService. I like to keep my tests as DRY (Don't Repeat Yourself) as I can, and there’s no reason that the steps to create this object can’t be moved to a setup method as we did in a previous post:

 1:     [TestFixture]
 2:  class OrderServiceTests
 3:     {
 4:  private OrderService _orderService;
 5:  
 6:         [TestFixtureSetUp]
 7:  public void SetupTestFixture()
 8:         {
 9:             _orderService = new OrderService();
 10:         }
(get sample code)

I’m half-way there. If you’ll recall the OrderService has a dependency on the OrderDataService, which I need to create a mock for. Luckily, I can declare my mock as an instance variable and inject it in to the construction of the OrderService in the TestFixtureSetup:
 
 1:     [TestFixture]
 2:  class OrderServiceTests
 3:     {
 4:  private OrderService _orderService;
 5:  private IOrderDataService _orderDataService;
 6:  
 7:         [TestFixtureSetUp]
 8:  public void SetupTestFixture()
 9:         {
 10:             _orderDataService = Mock.Create<IOrderDataService>();
 11:             _orderService = new OrderService(_orderDataService);
 12:         }

(get sample code)

Once that’s done I refactor my existing test to use these instance variable instead of the local instances:

 1:         [Test]
 2:  public void WhenUserPlacesACorrectOrderThenAnOrderNumberShouldBeReturned()
 3:         {
 4:  //Arrange
 5:             var shoppingCart = new ShoppingCart();
 6:             shoppingCart.Items.Add(new ShoppingCartItem { ItemId = Guid.NewGuid(), Quantity = 1 });
 7:             var customerId = Guid.NewGuid();
 8:             var expectedOrderId = Guid.NewGuid();
 9:  
 10:             Mock.Arrange(() => _orderDataService.Save(Arg.IsAny<Order>()))
 11:                 .Returns(expectedOrderId)
 12:                 .OccursOnce();
 13:  
 14:  //Act
 15:             var result = _orderService.PlaceOrder(customerId, shoppingCart);
 16:  
 17:  //Assert
 18:             Assert.AreEqual(expectedOrderId, result);
 19:             Mock.Assert(_orderDataService);
 20:         }

(get sample code)

As you can see I’ve removed the lines of code that created the OrderDataService and the OrderService mocks. I’ve also updated the variable names on lines 10, 15 and 19 to reflect the new instance variable names. But after refactoring to use the instance variable my existing test does not passes. Instead I get a compilation error (Figure 1):

image

Figure 1 – Our solution no longer compiles

We haven’t needed a type of InvalidOrderException until now, but now that we have a test that specifically calls for it, we need to create one.

I like to keep my projects highly organized. As part of that I like to keep my custom exceptions in their own folder within the project (which also puts them in their own namespace). I create a folder in the TddStore.Core project called Exceptions. I add a new C# class to that folder (if you are using JustCode, you have select the folder and use the Ctrl + Alt + Ins keyboard shortcut to bring up the JustCode file template list, then select “C# Class”). I will call my new class InvalidOrderException, and (for now) the implementation is about as easy as you can possibly get:

 1: using System;
 2:  
 3: namespace TddStore.Core.Exceptions
 4: {
 5:  public class InvalidOrderException : Exception
 6:     {
 7:  
 8:     }
 9: }
(get sample code)

I simply need to add a using statement for the new TddStore.Core.Exceptions namespace. If you are using JustCode, simply put the cursor over the InvalidOrderException type in the argument of the ExpectedException attribute (it should be the thing with the red squiggly line under it) and hit Ctrl+` and select “Add using for TddStore.Core.Exceptions.

Once that’s done my original test passes, so I can move on back to my new test. First I need to refactor what I have already written to use the instance variables I just declared (shown below). Then I need to start working on my Arrange block. First I need to setup the variables for my call to PlaceOrder, so I’ll declare customerId and shoppingCart. While I’m at it, I’ll add an item to shoppingCart with a quantity of zero:

 1:         [Test]
 2:         [ExpectedException(typeof(InvalidOrderException))]
 3:  public void WhenAUserAttemptsToOrderAnItemWithAQuantityOfZeroThrowInvalidOrderException()
 4:         {
 5:  //Arrange
 6:             var shoppingCart = new ShoppingCart();
 7:             shoppingCart.Items.Add(new ShoppingCartItem { ItemId = Guid.NewGuid(), Quantity = 0 });
 8:             var customerId = Guid.NewGuid();
 9:  
 10:  //Act
 11:             _orderService.PlaceOrder(customerId, shoppingCart);
 12:  
 13:  //Assert
 14:             Mock.Assert(_orderDataService);
 15:         }
(get sample code)

Next I need to setup my mock:

 1:         [Test]
 2:         [ExpectedException(typeof(InvalidOrderException))]
 3:  public void WhenAUserAttemptsToOrderAnItemWithAQuantityOfZeroThrowInvalidOrderException()
 4:         {
 5:  //Arrange
 6:             var shoppingCart = new ShoppingCart();
 7:             shoppingCart.Items.Add(new ShoppingCartItem { ItemId = Guid.NewGuid(), Quantity = 0 });
 8:             var customerId = Guid.NewGuid();
 9:             var expectedOrderId = Guid.NewGuid();
 10:  
 11:             Mock.Arrange(() => _orderDataService.Save(Arg.IsAny<Order>()))
 12:                 .Returns(expectedOrderId)
 13:                 .OccursNever();
 14:  
 15:  //Act
 16:             _orderService.PlaceOrder(customerId, shoppingCart);
 17:  
 18:  //Assert
 19:             Mock.Assert(_orderDataService);
 20:         }

(get sample code)

Unlike the previous test I want to make sure that the Save method on the OrderDataService is not called at all. As you saw in the previous post, JustMock provides a fluent syntax that let me define a stub with an expectation of being called once. Similarly, I can use the OccursNever method (line 13) to state that this method should not be called in the scope of this test.

And there we have it; a pretty easy run-of-the mill test.

Running the test shows that if fails as I have not implemented the business logic yet. Following the idea of the simplest thing that might work, I went through a few iterations of business logic before I finally came up with this implementation: 

 1:  public Guid PlaceOrder(Guid customerId, ShoppingCart shoppingCart)
 2:         {
 3:  foreach (var item in shoppingCart.Items)
 4:             {
 5:  if (item.Quantity == 0)
 6:                 {
 7:  throw new InvalidOrderException();
 8:                 }
 9:             }
 10:  
 11:             var order = new Order();
 12:  return _orderDataService.Save(order);
 13:         }

(get sample code)

Running my tests, I can see that they both pass (Figure 2):

image

Figure 2 – Passing tests

My work is done!

… Or Is It?

In the next post we’ll discover an issue with our test that may be giving us a false sense of security (If you know what it is, don’t spoil it!)

Continue the TDD journey:

JustCode download banner image

JustMock banner


About the Author

James Bender

is a Developer and has been involved in software development and architecture for almost 20 years. He has built everything from small, single-user applications to Enterprise-scale, multi-user systems. His specialties are .NET development and architecture, TDD, Web Development, cloud computing, and agile development methodologies. James is a Microsoft MVP and the author of two books; "Professional Test Driven Development with C#" which was released in May of 2011 and "Windows 8 Apps with HTML5 and JavaScript" which will be available soon. James has a blog at JamesCBender.com and his Twitter ID is @JamesBender. Google Profile

Comments