【问题标题】:Mocking local variable in calling method在调用方法中模拟局部变量
【发布时间】:2015-10-09 09:00:33
【问题描述】:

我在类的方法中模拟局部变量时遇到了一些麻烦。

我尝试模拟我的 Worker 类,但它调用了一个返回空值的方法,并且我的上下文变量变为空。因此,当我尝试获取 Name 属性时出现异常。

如何强制CreateWorkerContext() 返回模拟值?可能有办法模拟局部变量 (context)?

谢谢!

我的代码会更详细地说明问题:

namespace Moq
{
    class Program
    {
        static void Main(string[] args)
        {
            var workerMock = new Mock<Worker>();
            workerMock.Object.GetContextName();
        }
    }

    public class Worker
    {
        public string GetContextName()
        {
            // something happens and context does not create (imitated situation)
            WorkerContext context = CreateWorkerContext();

            // exception because _context is null
            return context.Name;
        }

        private WorkerContext CreateWorkerContext()
        {
            // imitate that context is not created
            return null;
        }
    }

    public class WorkerContext
    {
        public string Name { get; set; }
    }
}

【问题讨论】:

  • 您需要完全提取或更改WorkerContext CreateWorkerContext()。也许将IWorkerContextFactory 注入Worker 的构造函数。
  • 1 代表工厂。或者使IWorkerContex 可注射。
  • 不幸的是,你不能模拟任何东西,它必须是一个接口或一个带有虚拟方法的类。
  • 好吧,这并不难过,只是您当前的架构不容易测试,因为它不太遵守 SOLID 原则 (en.wikipedia.org/wiki/SOLID_(object-oriented_design))
  • 需要注入对象才能得到正确的结果。但是,您正在嘲笑部分并期望部分返回正确的结果。真的是你想写的吗?如果您想使用这个模拟类来测试其他东西,您不能锁定 GetContextName() 以返回 Dummy 字符串然后使用它吗?

标签: c# class unit-testing moq


【解决方案1】:

这里有几件事是有争议的。

首先, - 但这只是我的意见 - 你应该避免部分模拟(部分模拟 = 模拟一个没有实现接口的抽象类,因此,保留原始实现没有被嘲笑的方法。)。最好有一个IWorker 接口,Worker 将实现它。

第二 - 我会创建严格的模拟。松散的模拟似乎是一个不错的捷径,但通常会让您的方法和属性在您不打算返回默认值时返回默认值(在您的情况下为null

第三 - 我会注入 WorkerContext。如果您因为需要使用 .ctor 参数对其进行参数化而无法注入它,则注入一个 WorkerContextFactory,这将允许您模拟 WorkerContext 的创建和参数化

【讨论】:

    【解决方案2】:

    像 Moq 这样的动态模拟库一般来说并不神奇。 They can only replace the behaviour that you yourself could replace with code.

    在此特定示例中,Moq 不能替换 CreateWorkerContext,因为它是私有方法。

    一种选择是使其成为virtual:

    public class Worker
    {
        public string GetContextName()
        {
            WorkerContext context = CreateWorkerContext();
            return context.Name;
        }
    
        public virtual WorkerContext CreateWorkerContext()
        {
            return new WorkerContext();
        }
    }
    

    另一种选择是将CreateWorkerContext 方法替换为Strategy

    public class Worker
    {
        private readonly IWorkerContextFactory factory;
    
        public Worker(IWorkerContextFactory factory)
        {
            if (factory == null)
                throw new ArgumentNullException(nameof(factory));
    
            this.factory = factory;
        }
    
        public string GetContextName()
        {
            WorkerContext context = this.factory.Create();
            return context.Name;
        }
    }
    

    这两个选项都可以让 Moq 模拟所需的行为。

    【讨论】:

      猜你喜欢
      • 2012-08-03
      • 2014-06-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-19
      • 1970-01-01
      • 2021-10-31
      相关资源
      最近更新 更多