Monday, October 13, 2014

Async testing with Chill

Let’s face it. Async programming is difficult.

The Async Await certainly helps to make the asynchronous code more readable. Along that lines, Chill attempts to help to make your tests more readable as well.

Assume the following simple example. You have an asynchronous webapi controller. Why is this controller async? Let’s assume it needs to do IO. In this case, this is encapsulated in an async call to ICustomerStore.GetCustomerAsync().

   1: public class When_retrieving_existing_customer_async : 
   2:     GivenSubject<CustomerController, View>
   3: {
   4:     const int customerId = 12;
   5:  
   6:     public When_retrieving_existing_customer_async()
   7:     {
   8:         Given(() =>
   9:         {
  10:             SetThe<Customer>().To(EntityMother.BuildACustomer()
  11:                 .With(x => x.Id = customerId));
  12:  
  13:             // The GetCustomerAsync method needs to return a customer Asynchronously
  14:             // So it needs to return a Task<Customer>. The .Asynchronously() extension
  15:             // method conveniently wraps the customer in a task that returns the Customer for you
  16:             The<ICustomerStore>()
  17:                 .GetCustomerAsync(customerId)
  18:                 .Returns(The<Customer>().Asynchronously());
  19:         });
  20:         // When() also works nicely with asynchronous actions. 
  21:         When(() => Subject.GetAsync(customerId));
  22:     }
  23:  
  24:     [Fact]
  25:     public void Then_view_is_returned()
  26:     {
  27:         // Even though the GetAsync() method returns a Task<View>(), the Result
  28:         // Property is still a View. This allows for easy unit testing. 
  29:         Result.Should().NotBeNull();
  30:     }
  31:  
  32:     [Fact]
  33:     public void Then_model_is_the_existing_custmoer()
  34:     {
  35:         Result.Model.Should().Be(The<Customer>());
  36:     }
  37: }

In Chill, you can just define an asynchronous method in your call to When(). Chill will take care of handling the asynchronous complexity for you. Now all you need to do is to make sure your dependency return a Task instead of the ‘normal’ result. You can do this by calling the .Asynchronously() extension method.

Friday, October 10, 2014

Chill and the AutoMocking Container

One of the most important ways on how Chill helps with unit testing is by using an auto mocking container.

Compare the following code snippets:

   1: [TestMethod]
   2: public void TestWithoutTheChillFactor()
   3: {
   4:     // You have to explicitly create mocks for your dependencies 
   5:     // and store them in variables.
   6:     var mockCustomerStore = Substitute.For<ICustomerStore>();
   7:  
   8:     // Also, you have to explicitly create a variable for your test data. 
   9:     var expectedCustomer = new Customer()
  10:     {
  11:         Name = "Erwin",
  12:         Address = "At home",
  13:         Id = 123
  14:     };
  15:  
  16:  
  17:     mockCustomerStore.GetCustomer(123).Returns(expectedCustomer);
  18:  
  19:     // Also, you have to explicitly create the subject under test and insert the mocked dependencies. 
  20:     // Need to add or remove a dependency? prepare yourself to modify ALL your test. 
  21:     var sut = new CustomerController(mockCustomerStore);
  22:  
  23:  
  24:     // Call the actual function.. but you also have to create a variable to store your tests. 
  25:     var result = sut.Get(123);
  26:  
  27:     // Multiple asserts per test? 
  28:     Assert.AreSame(expectedCustomer, result.Model);
  29:     mockCustomerStore.Received().GetCustomer(123);
  30: }

There are a lot of things wrong with this example:


  • The knowledge about which dependencies by your Subject are needed are duplicated among all your tests.
  • In each test, you'll have to explicitly create mock objects. This clutters your test with code that does add any value.
  • Multiple asserts per test make it more difficult to figure out what exactly goes wrong.
  • No explicit structure to this test. What exactly is setup code.
  • Even though most tests use a subject, a result and variables, the naming of these variables will be very different across different tests by different authors, making them more difficult to understand.

Compare this with a Chill example:


   1: public class When_retrieving_existing_customer : GivenSubject<CustomerController, View>
   2:     {
   3:         const int customerId = 12;
   4:  
   5:         public When_retrieving_existing_customer()
   6:         {
   7:             Given(() =>
   8:             {
   9:                 // Storage for data used in the test. No need to create fields or variables. 
  10:                 SetThe<Customer>().To(EntityMother.BuildACustomer()
  11:                     .With(x => x.Id = customerId));
  12:  
  13:                 The<ICustomerStore>().GetCustomer(customerId).Returns(The<Customer>());
  14:             });
  15:  
  16:             // Subject under test is created automatically and accessable via the Subject property
  17:             When(() => Subject.Get(customerId));
  18:         }
  19:  
  20:  
  21:  
  22:         [Fact]
  23:         public void Then_view_is_returned()
  24:         {
  25:             Result.Should().NotBeNull();
  26:         }
  27:  
  28:         [Fact]
  29:         public void Then_model_is_the_existing_custmoer()
  30:         {
  31:             Result.Model.Should().Be(The<Customer>());
  32:         }
  33:     }

The automocking container sets up your Subject and automatically injects mock objects for any dependencies it needs. This is great if you have many tests against the same subject and you need to add a dependency. Your tests might fail, but that's exactly what you want. But they still compile and run!

There is no need to explicitly create mock objects anymore. The The<> method will create, register and return a mock object for you, ready for use.

If you want to explicitly register a value, you can use the SetThe<>().To() method to an object. There is also a shorthand for this: UseThe().

Note the use of the .With() extension method. This simple little extension method makes it easy to modify objects afther they have been built, in a very clean way.

Asp.Net Redirect and ThreadAbortException

Today I ran into an issue that I hadn’t seen in a long time. I was asked to diagnose two errors. “Cannot Redirect after HTTP headers have been sent”, and some random “Object Reference not Set” errors.

After analyzing some of the logs, I found that they were always found after logging out. Digging further, I found this statement: Response.Redirect(url, false);

For those who might not know, the ‘False’ parameter causes a ThreadAbortException. Why? To make sure your requests ends immediately after that.

At first, I was tempted to ‘just’ change the parameter to ‘true’ and be done with it, but I wanted to know why one of my colleagues made this change. And interestingly enough.. it showed up in the history: “added ‘false’ parameter to prevent ThreadAbortException in the logs”. So, one of my colleagues probably found his logs were full of ThreadAbortExceptions. So he avoided the exceptions and now the logs were full of other, more cryptic errors.

So I figured out the best solution would be to make sure this ThreadAbortException was not being logged. It turns out that this exception already doesn’t reach the Global.asax Application_OnError method. So that wasn’t the source of the logging. it turned out that we had some exception handling logic built ourselves. So, adding a check to see if this was a redirect solved the issue. A little extension method Exception.IsCausedByRedirect() made the code a bit more readable:

   1: public partial class _Default : Page
   2: {
   3:     protected void Page_Load(object sender, EventArgs e)
   4:     {
   5:  
   6:     }
   7:  
   8:     protected void myButton_OnClick(object sender, EventArgs e)
   9:     {
  10:         try
  11:         {
  12:             Response.Redirect("About.aspx");
  13:         }
  14:         catch (Exception ex)
  15:         {
  16:             if (ex.IsCausedByRedirect())
  17:             {
  18:                 Debug.WriteLine("Redirecting to: " + Response.RedirectLocation);
  19:             }
  20:             else
  21:             {
  22:                 Debug.WriteLine(ex);
  23:             }
  24:             throw;
  25:         }
  26:     }
  27: }
  28:  
  29: public static class ExceptionExtensions
  30: {
  31:     /// <summary>
  32:     /// Returns if an exception is caused by a redirect. A redirect throws 
  33:     /// a ThreadAbortException (normal behavior to stop execution). However
  34:     /// this exception typically shouln't be put in the logs. 
  35:     /// </summary>
  36:     /// <param name="exception">The exception to check</param>
  37:     /// <returns>True if this exception is caused by a redirect. False if not. </returns>
  38:     public static bool IsCausedByRedirect(this Exception exception)
  39:     {
  40:         return exception is ThreadAbortException &&
  41:             HttpContext.Current != null &&
  42:                 HttpContext.Current.Response.IsRequestBeingRedirected;
  43:     }
  44: }

Monday, October 6, 2014

Introducing Chill, a BDD style testing framework

The last couple of years, I’ve found myself in a position where I had to ‘sell’ unit testing to my colleagues. Most of them weren't familiar with unit testing, but some were downright against it. So, I tried to work on a way that minimized overhead and maximized readability.

Unit testing, when done correctly, is a great technique to improve the quality of your code and to speed up development.  When done incorrectly, it will take all technical debt and cast them in concrete, grinding development speed down.

To overcome this, I’ve worked quite a bit to come up with a set of techniques to Of course, I’m standing on the shoulder of giants here. All I’ve done is to combine several tools, ideas and techniques together into a form that minimizes overhead and maximizes impact.

Here’s an example of my preferred style of tests:

   1: public class When_retrieving_existing_customer : 
   2:     GivenSubject<CustomerController, View>
   3: {
   4:     const int customerId = 12;
   5:  
   6:     public When_retrieving_existing_customer()
   7:     {
   8:         // Explicit phases for setup and test 
   9:         Given(() =>
  10:         {
  11:             // Storage for data used in the test. 
  12:             // No need to create fields or variables. 
  13:             SetThe<Customer>().To(EntityMother.BuildACustomer()
  14:                 .With(x => x.Id = customerId));
  15:  
  16:             // Automatic creating of mock objects using library of choice. 
  17:             // Here using NSubstitute as a friendly mocking framework
  18:             The<ICustomerStore>().GetCustomer(customerId).Returns(The<Customer>());
  19:                     
  20:         });
  21:  
  22:         // Subject under test is created automatically 
  23:         // and accessable via the Subject property
  24:         When(() => Subject.Get(customerId));
  25:     }
  26:  
  27:   
  28:  
  29:     [Fact]
  30:     public void Then_view_is_returned()
  31:     {
  32:         // Result property is created automatically, 
  33:         // if needed and allows type safe access
  34:         Result.Should().NotBeNull();
  35:     }
  36:  
  37:     // Use test framework of choice. Here using XUnit, but NUnit, MsTest works just as well. 
  38:     [Fact]
  39:     public void Then_model_is_the_existing_custmoer()
  40:     {
  41:         // One assert per test. 
  42:         // Combines realy nicely with FluentAssertions. 
  43:         Result.Model.Should().Be(The<Customer>());
  44:     }
  45: }

It’s a bit of a mixture between MSpec style, BDD Style testing, and using some really useful frameworks such as XUnit, AutoFac, NSubstitute, FluentAssertions and AutoFixture. So in the next couple of blog posts, I’d like to describe my style of unit testing further.

I've decided to call this framework Chill. "If you stick it in a container, Chill will keep it cool."

https://github.com/erwinvandervalk/chill