【问题标题】:Test a method with a dependency on a base property测试一个依赖于基础属性的方法
【发布时间】:2014-11-20 04:57:12
【问题描述】:

我有一个继承自 BaseController 的 QuickController 类。 QuickController 中的一个方法调用 BaseController 上的一个属性,该属性依赖于 ConfigurationManager.AppSettings。

我想对 QuickController 进行单元测试,但找不到摆脱这种依赖关系的方法。这是我的测试:

[TestMethod]
public void TestMethod1()
{
    var moqServiceWrapper = new Mock<IServiceWrapper>();

    var controller = new QuickController(moqServiceWrapper.Object);

    //Act
    var result = controller.Estimator(QuickEstimatorViewModel);

    //Assert
    Assert.IsInstanceOfType(result, typeof(ViewResult));
}

QuickController 类

public class QuickController : BaseController
{
    public QuickController(IServiceWrapper service)
        : base(service) { }

    public ActionResult Estimator(QuickEstimatorViewModel viewModel)
    {
        viewModel.RiskAddressLocation = RiskAddressLocation;

        ....

        return View("QuickQuote", viewModel);
    }
}

还有 BaseController 属性

public RiskAddressLocation RiskAddressLocation
{
    get { return ConfigurationManager.AppSettings["..."]
                            .ToEnum<RiskAddressLocation>(true); }
}

我也尝试在继承自 QuickController 的 FakeQuickController 上调用该方法,但无法覆盖该属性,它是 BaseController 中始终被调用的那个。

有什么我可以在这里做的吗?

更新

从接受的答案来看,这是 VS2013 不喜欢的地方

public class BaseController{
    public virtual RiskAddressLocation RiskAddressLocation {get{...;}
}

public class QuickController : BaseController{}

public class FakeQuickController : QuickController{
    public override RiskAddressLocation RiskAddressLocation
    { 
        get { return ...} // Doesn't compile (cannot override because
                 //BaseController.RiskAddressLocation' is not a function
    }
}

但是,这很好用

public class BaseController{
    public virtual RiskAddressLocation RiskAddressLocation(){...}
}

public class QuickController : BaseController{}

public class FakeQuickController : QuickController{
    public override RiskAddressLocation RiskAddressLocation()
    { 
        return ... ;
    }
}

【问题讨论】:

  • 看起来你想mock ConfigurationManager而不是试图模拟基地?
  • 足够公平@RGraham,但我如何拦截基类中的 RiskAddressLocation 以便能够模拟它?

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


【解决方案1】:

您可以为 Configuration 编写一个适配器,以允许您在单元测试中提供存根配置。我敢肯定有很多不同的实现。我喜欢Nathan Gloyn's IConfigurationManager implementation

然后您将WebConfigurationManagerAdapter 注册为用于IConfigurationManager 服务以在生产中使用的组件,并使用Moq 在您的单元测试中模拟接口。

另外需要注意的是,如果您的 ViewModel 是一个简单的 DTO,我会在单元测试中传递一个真实实例,因为模拟它没有任何优势。

【讨论】:

    【解决方案2】:

    您有一个非虚拟属性,该属性取决于您要模拟的具体方法。

    基本上你有三个选择(没有上下文,我建议后两个中的任何一个):

    • 使用隔离框架,例如Microsoft Fakes
    • 修改您的BaseController 并将属性更改为虚拟
    • 删除代码中的依赖项(如果您想遵循 SOLID 设计,首先应该这样做),例如将其包装在单独的界面中

    【讨论】:

    • 如果我将 RiskAddressLocation 设为虚拟,我只能从 QuickController 覆盖它。我不能覆盖它,比如从继承自 QuickController 的 FakeQuickController。
    • 这是不正确的——如果你让一个成员为虚拟的,你可以在任何派生类中覆盖它,无论分隔多少次。给你一个最简单的例子——你可以在任何类上覆盖ToString(即使是从不同类继承的),即使原始方法是在System.Object上定义的。
    • 你是对的......也是错的;)你看,RiskAddressLocation 是一个属性,它不允许我覆盖孙子的 base。最后我得到了一些有意义的错误,它说这是不可能的,因为它不是一个函数。所以我从一个属性更改为一个函数......它可以工作:) 无论如何都接受你的答案。谢谢。
    • @kooshka 我很高兴它对你有用,但出于好奇 - 你能分享一下你是如何尝试的吗?因为您绝对可以覆盖“孙子”的属性;这段代码很简单:pastebin.com/fqc5Sf5Z
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-26
    • 1970-01-01
    • 2023-04-10
    相关资源
    最近更新 更多