How do you moq a class for unit test - c#

This question is posted as a follow up to How do you Mock an class for a unit test that has a return type but no input parameters
Since asking the original question I have now created a Minimal, Complete and Verifiable Example which is used as the basis for this question.
I have a controller (shown below)
public class HomeController : Controller
{
private OrganisationLogic _organisationLogic;
public HomeController(OrganisationLogic logic)
{
_organisationLogic = new OrganisationLogic();
}
public ActionResult Index()
{
var model = _organisationLogic.GetOrganisation().ToViewModel();
return View(model);
}
}
The controller retrieves data from a method in a Business Logic Layer called OrganisationLogic (shown below)
public class OrganisationLogic : LogicRepository<OrganisationModel>
{
public OrganisationLogic()
{
}
public override OrganisationModel GetOrganisation()
{
return new OrganisationModel { Id = 1, OrganisationName = "My Orgaisation", Address = "Logic" };
}
}
The business logic later inherits Logic repository (shown below)
public abstract class LogicRepository<T> : ILogicRepository<T>
{
protected LogicRepository()
{
}
public abstract T GetOrganisation();
}
The Logic Repository implements the ILogicRepository Interface (shown below)
public interface ILogicRepository<TModel>
{
TModel GetOrganisation();
}
I want to Unit Test the HomeController to verify that the data displayed in the ViewModel is returned correctly from OrganisationLogic and Transformed from OrganisationModel into an OrganisationViewModel.
I have written the following UnitTest which uses Moq to mock the method _OrganisationLogic.GetOrganisation().
[TestMethod]
public void Index()
{
var _OrganisationLogic = new Mock<OrganisationLogic>();
var testdata = new OrganisationModel { Id = 3, OrganisationName = "My Test Class Organisation" };
_OrganisationLogic.Setup(p => p.GetOrganisation()).Returns(testdata).Callback<OrganisationModel>(p=>p = testdata);
HomeController controller = new HomeController(_OrganisationLogic.Object);
ViewResult result = controller.Index() as ViewResult;
OrganisationViewModel model = (OrganisationViewModel)result.Model;
Assert.AreEqual(testdata.OrganisationName,model.OrganisationName);
}
When I run the test the test fails. The reason for this is that the Mock has not overridden the class and has instead returned the result from the actual method in the BusinessLogic Layer.
In my original question I posted that the error message being generated was:
System.ArgumentException Invalid callback. Setup on method with 0 parameter(s) cannot invoke callback with different number of parameters (1). Source=Moq StackTrace: at Moq.MethodCallReturn2.ValidateNumberOfCallbackParameters(MethodInfo
callbackMethod) at Moq.MethodCallReturn2.ValidateReturnDelegate(Delegate callback) at Moq.MethodCallReturn2.Returns[T](Func2 valueExpression)
I have now been able to replicate this error message exactly by running the following Unit Test. I suspect the unit test above is closer to what I need and that in the instance below I am setting up the Return() incorrectly. Thoughts on this are welcome?
[TestMethod]
public void Index()
{
var _OrganisationLogic = new Mock<OrganisationLogic>();
var testdata = new OrganisationModel { Id = 3, OrganisationName = "My Test Class Organisation" };
_OrganisationLogic.Setup(p => p.GetOrganisation()).Returns<OrganisationModel>(p=>p = testdata).Callback<OrganisationModel>(p=>p = testdata);
HomeController controller = new HomeController(_OrganisationLogic.Object);
ViewResult result = controller.Index() as ViewResult;
OrganisationViewModel model = (OrganisationViewModel)result.Model;
Assert.AreEqual(testdata.OrganisationName,model.OrganisationName);
}
My question is how to I setup the Mock so that it uses my test data.
In order to assist with answering the the above I have placed a version of the Code on GitHub that demonstrates this issue and shows the test failing. This can be accessed at https://github.com/stephenwestgarth/UnitTestExample
Any help would be very much appreciated.

Change the constructor of HomeController so that the parameter type is ILogicRepository<OrganisationModel>, and the field will need that type as well, and use the instance that was injected into the constructor. _organisationLogic = logic; (your code above ignores the parameter and creates its own concrete instance of the OrganisationLogic, which means it is not using your mock object.
In the test change the declaration of _OrganisationLogic to be...
var _OrganisationLogic = new Mock<ILogicRepository<OrganisationModel>>();
As I said when you previously asked, I don't think you need that Callback in there.
Edited constructor would look like this...
private ILogicRepository<OrganisationModel> _organisationLogic;
public HomeController(ILogicRepository<OrganisationModel> logic)
{
_organisationLogic = logic;
}

Related

How mock class in Unit Test for a Action in MVC

I'm implementing Unit Test on existing software.
These software is in ASP.NET MVC, and I want test the return of a Action, of a Controller.
But in this Action a I have the code:
public ActionResult EditProfile(
string accountId = null,
string partnership = null,
AccountType? subscriptionType = null,
bool forcePartnerUpdate = false)
{
var model = new EditProfileModel();
var account = _authUser.Account;
var catalogClient = new CatalogService.CatalogClient(Request.Cookies, Globals.CatalogURL);
The problem is because the last line : "new CatalogService.CatalogService()"
I'm using this class in other part of Action:
model.ListBrands = new List<Brands>();
model.ListAvailableBrands = new List<Brands>();
model.ListSubscribedBrands = new List<Brands>();
var brands = catalogClient.GetBrandsWithManufacturer();
So, How I can mock this for tests?
I have thought in abstract for a interface and send how a parameter in action, but I don't have the Request in my NInjectModule to IoC and in other codes this class has other parameters, so I think I can't do IoC.
How I can mock this with moq?
Thank you for help.
I've thought and tested a lot and now I got to do the mock for my tests.
First, I've created a "Factory" to my class:
public interface ICatalogClientFactory
{
ICatalogClient Create(HttpCookieCollection cookies, string catalogUrl);
}
public class CatalogClientFactory : ICatalogClientFactory
{
public ICatalogClient Create(HttpCookieCollection cookies, string catalogUrl)
{
return new CatalogClient(cookies, catalogUrl);
}
}
Than, this Interface I've registered in the NinjectModule and set this in the constructor of Controller. So, I'm calling in my actions with this code:
ICatalogClientFactory _catalogClientFactory = catalogClientFactory;
var clientCatalog = _catalogClientFactory.Create(Request.Cookies, Globals.CatalogURL);
Now, I can mock "ICatalogClientFactory" and "ICatalogClient" to use in unit tests.

Setup Mocked ViewModel for Unit Testing

Here is the scenario:
I'm writing a test for my controller and need to setup a view model titled CheckoutViewModel. My controller method, Products does not take CheckoutViewModel as a parameter, so I cannot pass it in that way.
Currently, the test fails returning a Null Exception because CheckoutViewModel is not getting set and called.
Question: How can I setup my CheckoutViewModel with data.
Error Details:
System.NullReferenceException
Object reference not set to an instance of an object
Current Test
[TestMethod]
public void Products_ProductControllerIsCalled_ReturnsViewWithProducts()
{
// Arrange
var currentSession = _autoMoqer.GetMock<ICurrentSession>().Object;
ProductController productController = new ProductController(currentSession);
var checkoutViewModel = new CheckoutViewModel
{
CheckoutId = new Guid()
};
// Act
ActionResult result = productController.Products();
// Assert
Assert.IsInstanceOfType(result, typeof(ViewResult));
}
Controller
[AccectReadVerbs]
public ActionResult Products()
{
CheckoutViewModel checkoutViewModel = GetCheckoutViewModel();
var checkoutId = checkoutViewModel.CheckoutId;
var result = _productOrchestrator.Products(checkoutId, currentSession)
return View(result);
}
Failing on this method
private CheckoutViewModel GetCheckoutViewModel()
{
if(Session["CheckoutViewModel"] == null)
{
return new CheckoutViewModel();
}
return (CheckoutViewModel)Session["CheckoutViewModel"];
}
If GetCheckoutViewModel has some dependencies on i.e services, dbConnection or other complex classes, you need to add a class with an interface, move the method for GetCheckOutViewModel to the class and take the new interface as a dependency to the controller. Then you need to mock the new interface.
Or edit your viewmodel to take interface dependencies on the stuff that stands in the way of unit testing, i.e the Session.
I think you could create some interface:
public interface ISessionManager
{
Session session {get; set;}
}
Then your controller constructor:
public ProductsController(ISessionManager sm)
{
_sessionManager = sm;
}
Then you can pass a mocked instance to your controller.
I'm guessing that the exceptions is due to the fact that when you're running the unit test there will not be any (webserver) session available. What you want do is to isolate your tests from any external dependencies - and a session state that is part of the webserver hosting environment would be an external dependency.
To solve this you need to either mock or stub out the Session object from your test. There are many ways to do this, but the easiest way would be to make Session a public property on the Controller. From your test you would then set the Session to an instance you create within your test.

Mocking (MOQ) passed parameter methods (WebAPI MVC Controller)

My apologies in advanced for not knowing the technical name of this scenario. I am mocking for unit test and that is all fine. However on this section of code I have run into a scenario that exceeds my mocking knowledge. Basically I have MethodA that takes 3 parameters. One of the parameters is passed as another method's output.
When I step through the method passed as a parameter is executed
My difficulty is that the passed method is being executed BEFORE my mocked object. Now it seems like a simple solution...mock the second method as well...that is where my knowledge falls down. I don't know how to get the "second" method mock into the testing context.
My controller being tested (simplified of course):
public class OrderController : ApiController
{
public OrderController(IRepositoryK repositoryk)
{}
public HttpResponseMessage NewOrder()
{
...snip....
string x = repositoryk.MethodA("stuff", "moreStuff", MethodB("junk"));
}
public string MethodB(string data)
{
using (var client = new HttpClient())
{...make call to Google API...}
}
}
My test:
[TestMethod]
public void AddOrder_CorrectResponse()
{
private Mock<IRepositoryK> _repK = new Mock<IRepositoryK>();
_repK.Setup(x => x.MethodA(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
.Returns("Yippe");
//of course I've left out all the controller buildup and execution stuff.
}
So I really have no desire to dive into MethodB but it seems to be doing it anyway. What am I doing wrong?
TIA
Thank you for your responses. I understand completely what you are saying. I'm trying to get some testing coverage in place before refactoring. So is there no way of keeping methodB from executing and just let my repositoryK mock just return what I've specified in the setup.
Your code is not easy to test, because it has hard dependency on HttpClient. You have nicely separated repository implementation, but if you want to easily test the code you should also separate code which calls Google API. The idea is to have something like this:
// Add interfece for accessing Google API
public interface IGoogleClient
{
string GetData(string data);
}
// Then implementation is identical to MethodB implementation:
public class GoogleClient : IGoogleClient
{
public string GetData(string data)
{
using (var client = new HttpClient())
{
//...make call to Google API...
}
}
}
// Your controller should look like this:
public class OrderController : ApiController
{
private readonly IRepositoryK repositoryk;
private readonly IGoogleClient googleClient;
public OrderController(IRepositoryK repositoryk, IGoogleClient googleClient)
{
this.googleClient = googleClient;
this.repositoryk = repositoryk;
}
public HttpResponseMessage NewOrder()
{
//...snip....
string x = repositoryk.MethodA("stuff", "moreStuff", MethodB("junk"));
}
public string MethodB(string data)
{
return googleClient.GetData(data);
}
}
If you have such setup you can easily mock both IRepositoryK and IGoogleClient:
Mock<IRepositoryK> repK = new Mock<IRepositoryK>();
Mock<IGoogleClient> googleClient = new Mock<IGoogleClient>();
repK.Setup(x => x.MethodA(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>())).Returns("Yippe");
googleClient.Setup(It.IsAny<string>()).Returns("something");
var controller = new OrderController(repK.Object, googleClient.Object);
// Test what you want on controller object
However, if you want to keep your code tightly coupled you can mock the call to MethodB with small changes.
First, you need to make method MethodB virtual, so it could be overridden in mock:
public virtual string MethodB(string data)
{
// your code
}
Then in your test instead of instantiating controller, instantiate and use mock of your controller:
var repK = new Mock<IRepositoryK>();
// create mock and pass the same constructor parameters as actual object
var controllerMock = new Mock<OrderController>(repK.Object);
controllerMock.CallBase = true;
// mock MethodB method:
controllerMock.Setup(x => x.MethodB(It.IsAny<string>())).Returns("data");
// call the method on mock object
// instead of calling MethodB you will get a mocked result
var result = controllerMock.Object.NewOrder();

How should I unit test a controller that returns a model?

I have a controller called PostsController
public class PostsController : Controller
{
private readonly IPostsRepository repository;
public PostsController(IPostsRepository repository)
{
this.repository = repository;
}
public ViewResult Index()
{
var posts =
repository.All()
.OrderBy(post => post.PublishedAt);
return View("Index", posts.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_ReturnsCorrectModel()
{
var actual = controller.Index().Model;
Assert.IsAssignableFrom<IEnumerable<PostViewModel>>(actual);
}
}
At the moment I am only testing that the controller returns the correct model type. Should I also stub the repository and test that the correct data is returned like this:
[Test]
public void Index_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.All()).Returns(new[] { post });
var actual = controller.Index().Model as IEnumerable<PostViewModel>;
Assert.NotNull(actual);
Assert.AreEqual(1, actual.Count());
Assert.AreEqual(post.Title, actual.First().Title);
}
I feel so frustrated not knowing if I am unit testing properly. A clear explanation of which I approach I should take why would be very helpful.
I don't think you need to test the functionality of IPostRepository in this unit test. You should create a seperate unit test class for it.
Similarly the functionality of the MapTo<T> should be tested separately.
Unit tests should only test the functionality of the SUT (System Under Test) which in this case is the Index method of your PostsController class.
This is a simple method so the 2 things you want to be verifying in this unit test are:
1- The repository.All() method gets called once
2- Your view model is mapped correctly (which you are already doing)
This is how I would unit test this method:
[Test]
public void Index_ReturnsCorrectModel()
{
// Arrange
repository.Setup(repo => repo.All()).Returns(Enumerable.Empty<Post>());
// Act
var actual = controller.Index().Model;
// Assert
Assert.IsAssignableFrom<IEnumerable<PostViewModel>>(actual);
repository.Verify(repo => repo.All(), Times.Once);
}
Also, to minimize the effort required to arrange your unit test, use can use a library like AutoFixture which will automatically create the post object for you.

Should I test if a stubbed method was called?

I'm just starting out with BDD/TDD using MSpec (with AutoMocking by James Broome) and RhinoMocks. Here's an excerpt from my practice project:
namespace Tests.VideoStore.Controllers
{
public abstract class context_for_movie_controller :
Specification<MovieController>
{
private static IList<Movie> movies;
protected static IMovieRepository _movieRepository;
protected static ActionResult _result;
protected static string title;
protected static string director;
Establish context = () =>
{
_movieRepository = DependencyOf<IMovieRepository>();
};
}
[Subject(typeof(MovieController))]
public class when_searching_for_movies_with_director :
context_for_movie_controller
{
Establish context = () =>
{
title = null;
director = "James Cameron";
var movie4 = new Movie {
Title = "Terminator", Director = "James Cameron"};
var movie6 = new Movie {
Title = "Avatar", Director = "James Cameron"};
movies = new List<Movie> {movie4, movie6};
// Repository returns all movies.
_movieRepository.Stub(x => x.FindMovies(title, director))
.Return(movies);
};
Because of = () => _result = subject.Find(title, director);
It should_fetch_movies_from_the_repository = () =>
_movieRepository.AssertWasCalled(x =>
x.FindMovies(title, director));
It should_return_a_list_of_movies_matching_the_director = () =>
_result.ShouldBeAView().And()
.ShouldHaveModelOfType<IEnumerable<Movie>>)
.And().ShouldContainOnly(movies);
}
As you can see, I stubbed out the FindMovies() method on the MovieRepository class. Then I'm calling the MoviesController.Find() action. My question is, should there be an assert to check if the stubbed method (FindMovies) was called by the controller? Or perhaps should I only care about the returned result and not where it was taken from? Furthermore, a spec that says "should_fetch_movies_from_the_repository" looks a lot like an engineering task, not something that a client might understand - does it have its place in BDD?
the general rule to follow for assertions is that you assert against output interactions, not input interactions.
the FindMovies stub is returning a "movies" collection to the class that called it and you are then verifying that the class received the correct list via the "it should return a list of movies matching the director" assertion. if the FindMovies method is not called, then this assertion will fail.
therefore, you do not need to assert the calls against the FindMovies method.
to counterpoint this, if you have an mock or stub that is purely output - let's say an IView interface being called by a Presenter class, then you do want to assert against the IView being called. for example, this code:
public class MyPresenter
{
... other code here
public DoSomething()
{
IList data = GetSomeData();
myView.DisplayData(data);
}
}
you would want to assert that the view.DisplayData method is called in this case, because you are not retrieving anything from this call that can be asserted by another test.
as for the "fetch from repository" - of course your customers care about that. they want the system to save movies to the storage and load them from the storage. however ... the FindMovies call is an input into the class being tested, so it's not necessary to have this assetion or test, at all. if the FindMovies method is not called, then the other test will fail and let you know that there is a problem.

Categories

Resources