如何对创建另一个 class 的新实例的方法或构造函数进行单元测试

How to unit test a method or constructor which creates a new instance of another class

我已经对这个主题进行了广泛的研究,但尚未找到可靠的答案,所以这里开始吧。

我有一个要测试的构造函数。此构造函数初始化另一个 class 以设置一个全局变量,该变量在整个 class 的其余部分都是必需的。但是,构造函数初始化的 class 的构造函数依赖于 Web 会话和其他正在设置的变量,这些变量在任何测试 运行 时都没有设置。我怎样才能正确地模拟它以确保我只是在测试构造函数?

这里是要测试的构造函数:

public CheckoutManager()
{
    _orderController = new OrderController();
    _orderController.PropertyChanged += (sender, args) => NotifyPropertyChanged(args.PropertyName);
}

问题出在OrderController的构造函数上:

public OrderController()
{
    _customer = WebUserSession.Current.Profile.Customer;
    _srd = ContextManager.GetStandardRequestByCulture( CultureInfo.CurrentUICulture.Name );
    WarehouseData data = new WarehouseData();
    data.WarehouseID = 0;   // TODO: Convert the 0 warehouse to a web warehouse type

    // Grab the attention items from the session
    AttentionItems = WebUserSession.Current.OrderAttentionItems;

    // Load the shopping cart
    _cart = ShoppingCartManager.Cart;
}

这是我尝试的测试:

[TestMethod]
public void Constructor_ExpectInstantiation()
{
    var checkoutManager = new CheckoutManager();

    Assert.IsNotNull( checkoutManager );
}

它轰炸了_customer = WebUserSession.Current.Profile.Customer;线。你如何使用最小起订量模拟这个?如果它不能被嘲笑,是否有一个很好的解释为什么?如何以更可测试的方式修改原始构造函数以具有相同的功能?提前谢谢你。

您可以使用Dependency Injection, which is a form of Inversion of Control来解决这类问题。

与其在构造函​​数中实例化您的 OrderController,不如将其作为参数传递:

public CheckoutManager(OrderController orderController)
{
    _orderController = orderController;
    _orderController.PropertyChanged += (sender, args) => NotifyPropertyChanged(args.PropertyName);
}

然后您可以在类似 Main 的方法中手动实例化 OrderController,或者使用像 Autofac 这样的库来为您完成所有连接。

现在您可以在测试中模拟您的界面了​​:

[TestMethod]
public void Constructor_ExpectInstantiation()
{
    Mock<OrderController> mockOrderController = new Mock<OrderControler>();
    var checkoutManager = new CheckoutManager(mockOrderController.Object);

    Assert.IsNotNull( checkoutManager );
}

这样,OrderController 的构造函数将永远不会被调用(它是一个模拟)并且它不会干扰您对 CheckoutManager 的测试。如果您需要测试与它的某些交互,您可以使用 Moq SetupVerify 特定方法。

您将无法对此代码使用 Moq 测试。你可以

  • 通过添加接受 OrderController 的构造函数修改代码以使其更易于测试
  • 使用更 "advanced" 的测试技术,例如 shims.