Caster Troy
Caster Troy

Reputation: 2866

Should I mock or fake my repository?

I have a controller called PostsController

public class PostsController : Controller
{
    private const int PageSize = 8;
    private readonly IPostsRepository repository;

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

    public ViewResult Index(int pageNumber = 1)
    {
        var posts = 
            repository.All()
                      .Where(post => !post.Draft)
                      .OrderBy(post => post.PublishedAt);

        var model = 
            posts.MapTo<PostViewModel>()
                 .ToPagedList(pageNumber, PageSize);

        return View("Index", model);
    }

    public ActionResult Post(string slug)
    {
        var post =
            repository.Find(slug);

        if (post == null || post.Draft)
        {
            return HttpNotFound();
        }

        return View("Post", post.MapTo<PostViewModel>());
    }
}

And a corresponding test fixture called PostsControllerTest

[TestFixture]
public class PostsControllerTest
{
    private PostsController controller;
    private Mock<IPostsRepository> repository;

    [SetUp]
    public void SetUp()
    {
        AutoMapperConfig.Configure();
        repository = new Mock<IPostsRepository>();
        controller = new PostsController(repository.Object);
    }

    [Test]
    public void Index_ReturnsCorrectViewName()
    {
        var actual = controller.Index();

        Assert.AreEqual(actual.ViewName, "Index");
    }

    [Test]
    public void Index_ReturnsCorrectModel()
    {
        var result = controller.Index();
        var actual = result.Model as PagedList<PostViewModel>;

        Assert.NotNull(actual);
    }

    [Test]
    public void Index_WithPageNumber_ReturnsCorrectViewName()
    {
        var actual = controller.Index(2);

        Assert.AreEqual(actual.ViewName, "Index");
    }

    [Test]
    public void Index_WithPageNumber_ReturnsCorrectModel()
    {
        var result = controller.Index(2);
        var actual = result.Model as PagedList<PostViewModel>;

        Assert.NotNull(actual);
    }

    [Test]
    public void Post_ReturnsCorrectViewName()
    {
        repository.Setup(repo => repo.Find("abc"))
                  .Returns(new Post());

        var actual = controller.Post("abc") as ViewResult;

        Assert.NotNull(actual);
        Assert.AreEqual(actual.ViewName, "Post");
    }

    [Test]
    public void Post_ThatIsDraft_ReturnsNotFound()
    {
        var post = new Post { Draft = true };
        repository.Setup(repo => repo.Find("abc"))
                  .Returns(post);

        var actual = controller.Post("abc");

        Assert.IsAssignableFrom<HttpNotFoundResult>(actual);
    }

    [Test]
    public void Post_ThatDoesNotExist_ReturnNotFound()
    {
        var actual = controller.Post("abc");

        Assert.IsAssignableFrom<HttpNotFoundResult>(actual);
    }

    [Test]
    public void Post_ReturnsCorrectModel()
    {
        var post = new Post
        {
            Slug = "continuing-to-an-outer-loop",
            Title = "Continuing to an outer loop",
            Summary = "When you have a nested loop, sometimes",
            Content = "When you have a nested loop, sometimes",
            PublishedAt = DateTime.Now.AddDays(7),
            Tags = new Collection<Tag> { new Tag { Name = "Programming" } }
        };

        repository.Setup(repo => repo.Find("continuing-to-an-outer-loop"))
                  .Returns(post);

        var viewResult = (ViewResult)controller.Post("continuing-to-an-outer-loop");
        var actual = viewResult.Model as PostViewModel;

        Assert.NotNull(actual);
        Assert.AreEqual(actual.Slug, post.Slug);
        Assert.AreEqual(actual.Title, post.Title);
        Assert.AreEqual(actual.Summary, post.Summary);
        Assert.AreEqual(actual.Content, post.Content);
        Assert.AreEqual(actual.PublishedAt, post.PublishedAt);
        Assert.AreEqual(actual.Tags, post.Tags);
    }
}

I learned to mock the repository in this way by observing how other projects arranged their tests. One particular example of this approach (from which I learned) can be found here. Personally I have found this approach to be somewhat laborious. Furthermore, since adopting this approach I stumbled upon this post that states that you should not mock your repository but fake it instead.

Now I am conflicted. Should I proceed to mock my repository of fake it instead?

I kindly ask that you include code examples that show how to fake and seed the repository in this case as I am not sure how to do so.

Upvotes: 4

Views: 1072

Answers (1)

Steve Wilkes
Steve Wilkes

Reputation: 7135

To pick a side, I'd say mocking is just fine and is less work than faking.

But to voice an opinion - without trying to be awkward - I'd say... neither.

Consider how much value is being added by a test which goes through your Index action without hitting a real repository - the vast majority of the code you're testing is in Linq and AutoMapper, both of which have already been heavily tested by other people.

I would recommended writing Integration tests which run through your controller actions, through your real repositories, hit your database and come back out the other side. That adds value and gives you some real assurance that your system (i.e. the bits you've written) actually works.

Finally, Jimmy Bogard (of AutoMapper fame) has a good blog entry about testing repositories here.

Upvotes: 4

Related Questions