Moq Extended Setup with Single 共享 Return

Moq Extended Setup With Single shared Return

我想做的是定义一个或多个 Setup(s) 到一个 Return 对模拟对象的多个方法调用的调用;因此避免了多次调用 Setup().Return()s.

编译器在尝试以下演示目标时提供错误,因此这不是实现该目标的合适方法。

var mPlatform = new Mock<IPlatformCommunicator>();

mPlatform.Setup(mp => mp.PreStart(It.IsAny<Action<IStatus>>()))
         .Setup(mp => mp.Start(It.IsAny<Action<IStatus>>()))
         ...
         .Returns(mockStatusIndeterminate.Object as IStatus);

有没有办法在Setup中定义多个方法调用来节省代码的总行数?


这也不是一个选项:

.Setup(mp => mp.PreStart(It.IsAny<Action<IStatus>>()) || mp.Start(It.IsAny<Action<IStatus>>())) 

正如@Nkosi 所说,并非如此。但根据您的使用情况,有些选项可能会起作用。

  1. SetReturnsDefault
void Main()
{
    var fooMock = new Mock<IFoo>();
    fooMock.SetReturnsDefault<string>("This is a mocked value");
    var foo = fooMock.Object;

    Console.WriteLine($"foo.Bar(): {foo.Bar()}");
    Console.WriteLine($"foo.Baz(): {foo.Baz()}");
}

public interface IFoo
{
    string Bar();
    string Baz();
}

任何 return 字符串的方法都会 return 您指定的任何内容。

  1. 默认值提供程序类似但全面。
void Main()
{
    var fooMock = new Mock<IFoo>();
    fooMock.DefaultValueProvider = new MyDefaultValueProvider();
    var foo = fooMock.Object;

    Console.WriteLine($"foo.Bar(): {foo.Bar()}");
    Console.WriteLine($"foo.Baz(): {foo.Baz()}");
}

public interface IFoo
{
    string Bar();
    string Baz();
}

public class MyDefaultValueProvider : DefaultValueProvider
{
    protected override object GetDefaultValue(Type type, Mock mock)
    {
        return "This is my default value";
    }
}
  1. 创建您自己的扩展;以下是一个有效的 mvp
void Main()
{
    var fooMock = new Mock<IFoo>();
    fooMock.Setup(new Expression<Func<IFoo, string>>[] { x => x.Bar(), x => x.Baz() }, "This is a mocked value");
    var foo = fooMock.Object;

    Console.WriteLine($"foo.Bar(): {foo.Bar()}");
    Console.WriteLine($"foo.Baz(): {foo.Baz()}");
}

public interface IFoo
{
    string Bar();
    string Baz();
}

public static class MoqExtensions
{
    public static Mock<T> Setup<T, U>(this Mock<T> self, Expression<Func<T, U>>[] setups, U returns)
        where T : class
    {
        foreach (var setup in setups)
        {
            self.Setup(setup).Returns(returns);
        }

        return self;
    }
}

以上所有产生以下结果

您可以在 Mock<T> 上创建自己的扩展方法来执行您想要的操作:

public static class MoqExt {
    public static void SetupReturnOnAll<T, TResult>(
        this Mock<T> mock,
        TResult returnValue,
        params Expression<Func<T, TResult>>[] expressions)
        where T: class {
            foreach (var expr in expressions)
                mock.Setup(expr).Returns(returnValue);
        }
}

用法如下:

mPlatform
    .SetupReturnOnAll(
        mockStatusIndeterminate.Object as IStatus,
        mp => mp.PreStart(It.IsAny<Action<IStatus>>()),
        mp => mp.Start(It.IsAny<Action<IStatus>>()));

通过一些额外的努力可以改进界面:

mPlatform
    .SetupAll(
        mp => mp.PreStart(It.IsAny<Action<IStatus>>()),
        mp => mp.Start(It.IsAny<Action<IStatus>>()))
    .Return(mockStatusIndeterminate.Object as IStatus);

为此,您需要一个额外的 class:

public class MultiSetup<T, TResult>
    where T: class {
    public Mock<T> Mock { get; }
    public Expression<Func<T, TResult>>[] Expressions { get; }
    
    public MultiSetup(Mock<T> mock, Expression<Func<T, TResult>>[] expressions)
        => (Mock, Expressions) = (mock, expressions);
    
    public void Return(TResult returnValue) {
        foreach (var expr in Expressions)
            Mock.Setup(expr).Returns(returnValue);
    }
}

并且您将使用此扩展名创建它:

public static class MoqExt {
    public static MultiSetup<T, TResult> SetupAll<T, TResult>(
        this Mock<T> mock,
        params Expression<Func<T, TResult>>[] expressions)
        where T : class
        => new MultiSetup<T, TResult>(mock, expressions);
}