2

I try to test my controller

[TestMethod]
    public void Index()
    {
        AdminController controller = new AdminController();
        ViewResult result = controller.Index() as ViewResult;
        Assert.IsNotNull(result);
    }

Here is Index() code:

public ActionResult Index()
        {
            var repository = new PostsRepository();
            var posts = repository.GetAllPosts();

            return View(posts);
        }

Repository:

public class PostsRepository : IPostsRepository
    {
        PostsDataContext _dataContext = new PostsDataContext();

        public IQueryable<Post> GetAllPosts()
        {
            var posts = from t in _dataContext.Posts select t;
            return posts;
        }

        public Post GetPostById(int id)
        {
            var post = from t in _dataContext.Posts
                       where t.id == id
                       select t;
            return post.First();
        }
    }

But my Index() unit test fails with error:

Test method MvcBlog.Tests.Controllers.AdminControllerTest.Index threw exception: 
System.NullReferenceException: Object reference not set to an instance of an object.

Stack Trace:

MvcBlog.Models.PostsDataContext..ctor() in C:\Users\cL1Nk3r\Documents\Visual Studio 2010\Projects\MvcBlog\MvcBlog\Models\Posts.designer.cs: line 38
MvcBlog.Repository.PostsRepository..ctor() in C:\Users\cL1Nk3r\Documents\Visual Studio 2010\Projects\MvcBlog\MvcBlog\Repository\PostsRepository.cs: line 11
MvcBlog.Controllers.AdminController.Index() in C:\Users\cL1Nk3r\Documents\Visual Studio 2010\Projects\MvcBlog\MvcBlog\Controllers\AdminController.cs: line 19
MvcBlog.Tests.Controllers.AdminControllerTest.Index() in C:\Users\cL1Nk3r\Documents\Visual Studio 2010\Projects\MvcBlog\MvcBlog.Tests\Controllers\AdminControllerTest.cs: line 19

Why it is an error?
If i just run my application, it works correctly.

Source code download: http://dl.dropbox.com/u/14053604/MvcBlog.rar

2
  • Can you share source code? I can't figure out why you DataContext throws exception. Commented Jan 29, 2011 at 22:00
  • Here: dl.dropbox.com/u/14053604/MvcBlog.rar Commented Jan 29, 2011 at 22:17

1 Answer 1

6

The problem is the following line in your controller action:

var repository = new PostsRepository();

Here you are tying your controller to a particular implementation of the repository making it very difficult to unit test in isolation. In order to achieve weaker coupling between your controller and data access layer I would recommend you using constructor injection:

public class PostsController: Controller
{
    private readonly IPostsRepository _repository;
    public PostsController(IPostsRepository repository)
    {
        _repository = repository;
    }

    public ActionResult Index()
    {
        var posts = _repository.GetAllPosts();
        return View(posts);
    }
}

Now your controller is completely decoupled from a particular implementation of a repository which might depend on a database, etc. Now you could use a mocking framework such as Rhino Mocks or Moq to provide a dummy implementation of this repository for the unit test.

Personally I like very much MVCContrib TestHelper which works with Rhino Mocks and allows for very elegant unit tests of controller actions. So once you've decoupled your controller from a specific implementation of the repository as I showed you could have the following unit test:

[TestMethod]
public void PostsController_Index_Action_Should_Fetch_All_Posts_From_Repository()
{
    // arrange
    var postsRepositoryStub = MockRepository.GenerateStub<IPostsRepository>();
    var sut = new PostsController(postsRepositoryStub);
    var expectedPosts = new Post[0];
    postsRepositoryStub.Stub(x => x.GetAllPosts).Return(expectedPosts);

    // act
    var actual = sut.Index();

    // assert
    actual
        .AssertViewRendered()
        .WithViewData<IEnumerable<Post>>()
        .ShouldBe(expectedPosts);
}
Sign up to request clarification or add additional context in comments.

7 Comments

@Sergey, with my code it is impossible to get the same error simply because the code stated in the error message situated in the PostsRepository is never executed in the unit test. I use a mock repository.
@Sergey, your application runs in a very different environment than your unit test. In your application you have a web.config probably containing a connection string to your database which you don't have in your unit test. In your real application you have an HttpContext which you don't have in your unit test. So if you don't decouple your code you will have very hard time unit testing your layers application layers in isolation.
Thank you. The problem was a connection string. I pass it to the repository and it works correctly.
@Sergey, the whole point of my answer is that you should never have to depend on a database or a particular implementation of a repository in your unit test. So, no, the notion of connection string shouldn't even be mentioned in a unit test. Here you are testing a controller logic. A controller should never depend on a connection string. Please read my answer more carefully.
@Sergey, your DataContext should never be used in the unit test. I am passing an interface inside the controller constructor and the action method calls the GetAllPosts method on this interface. Inside the unit test I provide a mock implementation of the IPostsRepository. In the unit test PostsRepository is never used meaning that DataContext is never used and a connection string is never used and a database access is never made. I would recommend you reading Martin Fowler's excellent article on the subject of inversion of control: martinfowler.com/articles/injection.html
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.