在调用方法中模拟局部变量

Mocking local variable in calling method

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

我尝试模拟我的 Worker class,但它调用了一个方法,该方法使 return 的空值和我的上下文变量变为空。因此,当我尝试获取名称 属性.

时出现异常

如何强制 CreateWorkerContext() 到 return 模拟值?可能有一种模拟局部变量的方法(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; }
    }
}

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

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

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

第三 - 我会注入 WorkerContext。如果你不能注入它,因为你需要用 come .ctor 参数对其进行参数化,那么注入一个 WorkerContextFactory 这将允许你模拟你的 WorkerContext

的创建和参数化

一般来说,像 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 能够模拟所需的行为。