Testing with queries and repositories (a simple example)

Not being much of a fan of the Repository pattern, or better yet, not a fan of applying it as a universal data access strategy, one question that comes up often is “but what about testing”? But the question should really be “what am I testing”?

Let’s look at something like our original controller action we were trying to test:

public ActionResult Archive(int year, int? month, int? day)
{
	RavenQueryStatistics stats;
	var postsQuery = RavenSession.Query<Post>()
		.Include(x => x.AuthorId)
		.Statistics(out stats)
		.WhereIsPublicPost()
		.Where(post => post.PublishAt.Year == year);
			
	if (month != null)
		postsQuery = postsQuery.Where(post => post.PublishAt.Month == month.Value);

	if (day != null)
		postsQuery = postsQuery.Where(post => post.PublishAt.Day == day.Value);

	var posts = 
		postsQuery.OrderByDescending(post => post.PublishAt)
		.Paging(CurrentPage, DefaultPage, PageSize)
		.ToList();

	return ListView(stats.TotalResults, posts);
}

This is a fairly complicated query, and one worth testing. The question then comes back to “what should I test”? Is my goal to write a unit test for the controller action, or an integration test for the query? Both? Let’s try both, and see what happens.

In order for us to isolate the controller action from the query, we need to create some sort of seam. Whether I go with a query object or repository, I’m looking at faking the results of the query. Let’s go with a repository just for familiarity sake, though the query path will be largely the same:

public ActionResult Archive(int year, int? month, int? day)
{
    List<Post> posts;

    var stats = postsRepository.QueryPostStats(year, month, day, out posts);

    return ListView(stats.TotalResults, posts);
}

I’ve extracted the query part and moved it to a specific class. Whether it’s a repository or query again is irrelevant, as I now have a seam to introduce a fake query result through the constructor:

private readonly IPostsRepository postsRepository;

public PostsController(IPostsRepository postsRepository)
{
    this.postsRepository = postsRepository;
}

Let’s write a test for this:

[Fact]
public void Should_get_posts_and_show_a_view()
{
    var repo = MockRepository.GenerateStub<IPostsRepository>();

    var results = new List<Post>();

    var stats = new RavenQueryStatistics
    {
        TotalResults = 20
    };

    repo.Stub(r => r.QueryPostStats(1, 2, 3, out results)).Return(stats);

    var controller = new PostsController(repo);

    var result = controller.Archive(1, 2, 3) as ViewResult;

    result.ShouldNotBeNull("Should be a view result");

    // etc
}

Is there any value in this test? I don’t think so. It’s heavily coupled to the inner workings of the method, and in the end, doesn’t really prove anything.

This isn’t so much the fault of the test as what I was trying to test. Controller unit tests don’t have too much value in my opinion – it still requires a lot of plumbing to truly know that it will work. To put it another way, I don’t really have confidence of anything other than those three lines of code in my controller are correct. And that’s not really saying much at all.

For our repository, we’d just write an integration test. Since RavenDB supports running the server in memory, we don’t really sacrifice much speed in our tests by doing so.

So where does that leave us? Our unit test for the controller action was next to worthless, but testing the query is actually important. The question really comes back to goals. If our goal is to test the query, providing a seam in that direction could prove helpful. If I can test my query through the controller action, fine, otherwise, I’ll create a seam for the query.

But in my controller action, unless there is truly interesting behavior, I don’t feel the need to test those three lines of code. It’s just not worth the time or effort.

Related Articles:

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

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in Testing. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • AquaBirdConsult

    Hey I use RavenDB and my code used to look like yours with If statements but then I realized that I could just write an extension method to tighten up my code.

    https://gist.github.com/4025344

    Here is the code you might find useful. I put a ticket in to RavenDB issues to get it in the client library but not sure if it will get in there.

  • Rémi BOURGAREL

    What’s the worth of creating a repository instead of an extension method here ? I totally agree with the fact that unit testing the controller is most of the time worthless.

  • http://twitter.com/ntcoding Nick

    this pattern works well for me. I just integration test controllers and things are a lot easier.

    - 1 for prefixing your test with the word “should” though. :)

  • dave falkner

    How would you recommend handling the testing of any business or application flow logic that might act on the data that has been retrieved? Other than injecting a mock of a repository, the only method I can imagine might be:

    public ActionResult Archive(int year, int? month, int? day)
    {
    var myData = preferredDataAccessStragety.GetData();
    var viewModel = ApplyLogicToData(myData);
    }

    and then only writing a unit test for the ApplyLogicToData method, providing it with a fake myData and writing asserts against the resulting viewModel.
    What are the pros and cons of each method, and are there other methods that might be even better still?

    • jbogard

      Good question! That’s in the next post, for the “complex” example.

  • KevinLaBranche

    And you’ve answered my question to your earlier post on the matter. Perfect. :-)

  • Tudor

    In my opinion, the value in unit testing the controller itself, even it’s just 4 lines of code, shows when, 4 years later, when the original developer is long gone from the company, some junior developer has to make some changes in that part of the application, and assumes it’s safe to do some “small” changes, that are not caught at compile time..

    This, and the fact that people usually are to lazy to run the integration tests because they take too much to run, or they don’t write any integration tests..

    • jbogard

      I think the value of unit tests to help in this situation is overstated. When I’ve seen junior teams take over for more senior ones, typically what happens is that the tests simply aren’t run, commented out, or marked explicit. What you’re describing is not so much a failure in code, but a failure in management and team building.

    • http://twitter.com/paulvconnolly Paul Connolly

      You are pretty much testing the flow of the calls in the controller. Especially a lot of “AssertWasCalled” stuff which basically just verifies the lines I’ve written, nothing to do with verifiying logic.

  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1227

  • Pingback: What am I Testing? « danielksmith

  • Terence Lewis

    Transposing the out and return parameters between your repository implementation and Raven’s .Statistics() method distracted me a little. I realize it’s tangential to your point, but was there a reason for this that I’m not aware of?

    • jbogard

      Not my code, I’m just refactoring someone else’s. I have no idea!

  • http://twitter.com/asier_ba Asier Barrenetxea

    In my opinion, that test to the controller looks dull because the code itself is dull. So it feels like we are overdoing things.
    I mean, you are mimicking the controller action in the repository, basically the method in the repository and the controller have the same signature – almost same arguments, same output. Is this right? what’s the reuse of that QueryPostStats method? None. It is very specific to this and just this action in the controller.