问题
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.
回答1:
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;
}
来源:https://stackoverflow.com/questions/49244545/how-do-you-moq-a-class-for-unit-test