0
[TestMethod] public void Can_Login_With_Valid_Credentials() { //Arrange Mock<IMembershipRepository> mockRepository = new Mock<IMembershipRepository>(); Mock<LoginViewModel> mockModel = new Mock<LoginViewModel>(); mockModel.Setup(x => x.IsLoggedIn()).Returns(true); AccountController target = new AccountController(mockRepository.Object); //Act ActionResult result = target.Login(mockModel.Object); //Assert Assert.IsNotInstanceOfType(result, typeof(ViewResult)); } 

And ActionResult in the controller

public ActionResult Login(LoginViewModel viewModel) { string returnUrl = (string)TempData["ReturnUrl"]; if (ModelState.IsValid) { LoginViewModel model = new LoginViewModel(repository, viewModel); if (model.IsLoggedIn()) { if (String.IsNullOrEmpty(returnUrl)) return RedirectToAction("Index", "Home"); else return Redirect(returnUrl); } else { ModelState.AddModelError("Email", ""); ModelState.AddModelError("Password", ""); } } return View(viewModel); } 

I'm having problems with mocking model.IsLoggedIn() in the ActionMethod, and it is probably because I'm creating a new instance of the viewmodel LoginViewModel in that ActionMethod. That is why mockModel.Setup(x => x.IsLoggedIn()).Returns(true); in the unit test is not caching it because there is a new instance of the class that has that method.

Is there any way i can mock model.IsLoggedIn() in the ActionMethod and make it return true?

4
  • 4
    That's indeed the problem. Is there a specific reason you're creating a new LoginViewModel object instead of manipulating the existing one? Commented Dec 22, 2013 at 20:12
  • In a word, no. You're trying to mock a concrete implementation. If the dependency is not being injected in, you can't mock it. The instance of LoginViewModel that you pass in is not the instance that IsLoggedIn is actually called upon, hence the setup not matching. Commented Dec 22, 2013 at 20:19
  • As it stands only TypeMock Isolator or Fakes can help you out. With some refactoring you could can easily test this, creating a protected virtual CreateLoginViewModel in your controller would allow you to manipulate it through normal mocking tools. Or reuse the model as Jeroen suggests. Commented Dec 22, 2013 at 20:29
  • Yep that's what I was think too! I can't mock it without using existing LoginViewModel. Commented Dec 22, 2013 at 20:30

2 Answers 2

2

If there is no way to avoid creating new instance of LoginViewModel in the action method, then introduce a factory which does that. Avoid at all costs direct creation of any concrete class - that makes unit testing impossible.

If action method creates an object using the factory, then concrete factory is passed as controller's constructor parameter. That requires IoC container and custom controller factory, which is relatively simple to add to the project.

With this solution in place, unit test actually mocks factory so that mocked factory returns mocked LoginViewModel object (in fact: mocked object that implements ILoginViewModel interface). This is the way in which I do it in all MVC projects and it works perfectly for production code, unit tests and integration tests.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the good idea! That's what I'm gonna do from now on as well.
0

Based on the comments above and research, the best way to do that would be by using an existing LoginViewModel instead of creating a new instance of it. Here is the re-factored vision of ActionResult that works with Moq.

public ActionResult Login(LoginViewModel viewModel) { string returnUrl = (string)TempData["ReturnUrl"]; if (ModelState.IsValid) { if (viewModel.IsLoggedIn(repository)) { if (String.IsNullOrEmpty(returnUrl)) return RedirectToAction("Index", "Home"); else return Redirect(returnUrl); } else { ModelState.AddModelError("Email", ""); ModelState.AddModelError("Password", ""); } } return View(viewModel); } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.