你是如何进行单元测试的

时间:2018-03-12 21:18:06

标签: c# asp.net-mvc unit-testing mocking moq

此问题已作为How do you Mock an class for a unit test that has a return type but no input parameters

的后续跟进发布

自从问起原来的问题后,我现在创建了一个最小的,完整的,可验证的例子,作为这个问题的基础。

我有一个控制器(如下所示)

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);
    }
}

控制器从业务逻辑层中名为OrganisationLogic的方法(如下所示)中检索数据

 public class OrganisationLogic : LogicRepository<OrganisationModel>
 {
     public OrganisationLogic()
     {

     }

     public override OrganisationModel GetOrganisation()
     {

         return new OrganisationModel { Id = 1, OrganisationName = "My Orgaisation", Address = "Logic" };

     }
}

业务逻辑稍后继承逻辑存储库(如下所示)

public abstract class LogicRepository<T> : ILogicRepository<T>
{

    protected LogicRepository()
    {

    }


    public abstract T GetOrganisation();

 }

Logic Repository实现了ILogicRepository接口(如下所示)

public interface ILogicRepository<TModel>
{
    TModel GetOrganisation();
}

我想对HomeController进行单元测试,以验证ViewModel中显示的数据是否已从OrganisationLogic正确返回并从OrganisationModel转换为OrganisationViewModel。

我编写了以下UnitTest,它使用Moq来模拟方法_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);
}

当我运行测试时,测试失败。原因是Mock没有覆盖类,而是从BusinessLogic层中的实际方法返回结果。

在我原来的问题中,我发布了生成的错误消息:

System.ArgumentException无效的回调。使用0参数设置方法不能使用不同数量的参数调用回调(1)。 Source = Moq StackTrace:在Moq.MethodCallReturn2.ValidateNumberOfCallbackParameters(MethodInfo)       callbackMethod)在Moq.MethodCallReturn2.ValidateReturnDelegate(委托回调)在Moq.MethodCallReturn2.Returns [T](Func2 valueExpression)

我现在能够通过运行以下单元测试来完全复制此错误消息。我怀疑上面的单元测试更接近我需要的,并且在下面的实例中我错误地设置了Return()。对此的想法是受欢迎的?

[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);
}

我的问题是如何设置Mock以便它使用我的测试数据。

为了帮助回答上述内容,我在GitHub上放置了一个代码版本,用于演示此问题并显示测试失败。这可以在https://github.com/stephenwestgarth/UnitTestExample

访问

非常感谢任何帮助。

1 个答案:

答案 0 :(得分:4)

更改HomeController的构造函数,使参数类型为ILogicRepository<OrganisationModel>,字段也需要该类型,并使用注入构造函数的实例。 _organisationLogic = logic;(上面的代码忽略了参数并创建了自己的OrganisationLogic具体实例,这意味着它没有使用你的模拟对象。

在测试中将_OrganisationLogic的声明更改为...... var _OrganisationLogic = new Mock<ILogicRepository<OrganisationModel>>();

正如我之前所说的那样,我认为你不需要回调。

编辑的构造函数看起来像这样......

private ILogicRepository<OrganisationModel> _organisationLogic;

public HomeController(ILogicRepository<OrganisationModel> logic)
{
   _organisationLogic = logic;
}