C# 为单元测试设置模拟ViewModel

C# 为单元测试设置模拟ViewModel,c#,unit-testing,tdd,C#,Unit Testing,Tdd,以下是场景: 我正在为我的控制器编写测试,需要设置一个名为CheckoutViewModel的视图模型。我的控制器方法,Products没有将CheckoutViewModel作为参数,因此我无法以这种方式传递它 当前,由于未设置和调用CheckoutViewModel,测试无法返回Null异常 问题:如何使用数据设置我的CheckoutViewModel 错误详细信息: System.NullReferenceException 对象引用未设置为对象的实例 当前测试 [TestMethod

以下是场景:

我正在为我的控制器编写测试,需要设置一个名为
CheckoutViewModel
的视图模型。我的控制器方法,
Products
没有将
CheckoutViewModel
作为参数,因此我无法以这种方式传递它

当前,由于未设置和调用
CheckoutViewModel
,测试无法返回
Null异常

问题:如何使用数据设置我的
CheckoutViewModel

错误详细信息:

  • System.NullReferenceException

  • 对象引用未设置为对象的实例

当前测试

[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));
}
此方法失败

private CheckoutViewModel GetCheckoutViewModel()
{
    if(Session["CheckoutViewModel"] == null)
    {
        return new CheckoutViewModel();
    }
    return (CheckoutViewModel)Session["CheckoutViewModel"];
}

如果GetCheckoutViewModel对服务、dbConnection或其他复杂类具有某些依赖关系,则需要添加一个带有接口的类,将GetCheckoutViewModel的方法移动到该类,并将新接口作为控制器的依赖关系。然后需要模拟新接口


或者编辑您的viewmodel,使接口依赖于阻碍单元测试的东西,即会话。

我认为您可以创建一些接口:

公共接口ISessionManager
{
会话{get;set;}
}

然后,您的控制器构造函数:

公共产品控制器(ISessionManager sm)
{
_会话管理器=sm;
}


然后,您可以将模拟实例传递给控制器。

我猜想,出现异常的原因是,当您运行单元测试时,将没有任何(Web服务器)会话可用。您要做的是将测试与任何外部依赖项隔离开来,而作为Web服务器宿主环境一部分的会话状态将是外部依赖项


要解决这个问题,您需要从测试中模拟或删除会话对象。有很多方法可以做到这一点,但最简单的方法是使会话成为控制器上的公共属性。在测试中,您可以将会话设置为在测试中创建的实例

能否从控制器中显示
Products()
的代码?能否按照代码中的步骤调试单元测试?如果是这样,您能否确定
\u productOrchestrator.Products(checkoutId,currentSession)
是否实际返回一个非
空的
结果?我打赌这就是问题所在。当然,调用
GetCheckoutViewModel()
方法时失败。。。它在
if(Session[“CheckoutViewModel”]==null)
行中特别失败。看起来您需要模拟
Session
对象进行单元测试。举一个例子来看。
private CheckoutViewModel GetCheckoutViewModel()
{
    if(Session["CheckoutViewModel"] == null)
    {
        return new CheckoutViewModel();
    }
    return (CheckoutViewModel)Session["CheckoutViewModel"];
}