Writing tests with the AAA syntax to improve readability using Rhino Mocks
Published 12/20/2009 6:00 AM by Toran Billups 2 Comments
When I first started writing automated unit tests I had a lot to learn about mock objects. And the first mock object framework I started playing around with was Rhino Mocks.
But having to explicitly setup a record and playback step felt like a lot of extra code. And when you first start writing tests you quickly realize that you will be maintaining more code than ever before. Not that you won’t see a huge return on investment for each test you add to your solution, but with that said the less code you have to write the better.
And for me this came in the form of a new syntax with the release of Rhino Mocks 3.5. The new syntax was labeled "Arrange, Act, Assert" or AAA for short. With this new syntax you wouldn’t need to explicitly wrap anything inside a using block for record or playback.
A simple example that shows the readability improvement using this new syntax can be found below. In the simple controller action we expect that given a valid blog post the save method of IPostService is called.
public ActionResult Archive(int id, FormCollection collection)
{
var ArchivedBlogPost = _PostService.GetPostById(id);
if (ArchivedBlogPost != null)
{
//we can assume that the post is updated here ...
_PostService.SavePost(ArchivedBlogPost);
}
return View(ArchivedBlogPost);
}
With the previous syntax we might write something like the below.
[TestMethod]
public void SimpleTestShowingRecordReplaySyntax()
{
MockRepository BaseMockRepository = new MockRepository();
Post ArchivedBlogPost = new Post { ID = 1, Content = "test" };
IPostService PostService = BaseMockRepository.DynamicMock();
BlogController Controller = new BlogController(PostService);
using (BaseMockRepository.Record())
{
Expect.Call(PostService.GetPostById(1)).Return(ArchivedBlogPost);
PostService.Expect(x => x.SavePost(ArchivedBlogPost));
}
using (BaseMockRepository.Playback())
{
ActionResult Result = Controller.Archive(1, null);
}
}
But with the new AAA syntax we could write the same test with less code. I also found it more natural to express what each fake was doing and why it was needed in the specific test case.
[TestMethod]
public void SimpleTestShowingAAASyntax()
{
Post ArchivedBlogPost = new Post { ID = 1, Content = "test" };
IPostService PostService = MockRepository.GenerateStub();
BlogController Controller = new BlogController(PostService);
PostService.Stub(x => x.GetPostById(1)).Return(ArchivedBlogPost);
ActionResult Result = Controller.Archive(1, null);
PostService.AssertWasCalled(x => x.SavePost(ArchivedBlogPost));
}
I always strive for maximum readability in production code, so why not test code?
Hi Toran, first of all, thanks for the follow on Twitter. I just found this post and wanted to share a little bit of my experience with unit testing and mocking. I've found that mock-based tests, such as the one written here, are the most fragile tests I (used to) write and have been the source of a lot of friction in my development cycle. I found that the easiest way to reduce friction was to separate my code into two types of classes: 1) dependency-free "units" and 2) connector classes that only glue dependencies together. Here's an example using a simple controller action ActionResult Archive(int id) { var post = PostService.GetPostById(id); post.Archived = true; PostService.SavePost(post); return View("index"); } To me this looks like a "fat" controller because several things are going on. I'd recommend refactoring all of this out of the controller similar to this: controller: ActionResult Archive(int id) { PostService.ArchivePost(id); return View("index"); } service: void ArchivePost(int id) { var post = PostRepository.GetById(id); post.Archive(); //How you persist the change to post depends on your DAL/ORM. I use NHibernate so simply modifying post will automatically get saved without calling "save" anywhere. } post entity class: void Archive() { this.Archived = true; } Now the only "unit" I have to test is Post.Archive(). [Test] public void Archive_NonArchivedPost_BecomesArchived() { var post = new Post(); post.Archive(); Assert(post.Archived == true); } This is still AAA syntax. Notice that your original test never actually checked to see if the post had become archived (or changed state at all); it just made sure a post was retrieved and then saved. In my opinion testing the state change is much more important than verifying that you call a service method. The remaining two methods are simply gluing a web request to calling the Archive method. How you test this "glue" is up to personal preference. I usually write a few integration tests (with an in-memory database) to smoke test my service layer. One test could look like: [Test] public void Can_create_archive_and_retrieve_a_post() { var postService = new PostService(...); //Resolving this with IoC is best var postParams = new PostParams() { Title = "test" }; var post = postService.CreatePost(params); postService.ArchivePost(post.Id); var retrievedPost = postService.GetPost(post.Id); Assert(retrievedPost.Title == "test"); Assert(retrievedPost.Archived == true); } As far as the controller action goes, I never test it because it's usually just a service call. Anyway, I wrote a lot more than I intended to. I hope you find this helpful. I just wanted to share the pain points that I've encountered with mock based testing. Happy coding!
I couldn't agree with you more regarding your suggestion to shrink the above controller methods. I should have mentioned that this was for discussion purposes only to show how one might use the AAA syntax with the latest release of Rhino Mocks. Thanks for the detailed comment!