C# Moq 模拟部分方法但抛出 NotSupportedException

C# Moq mock partial methods but throw out NotSupportedException

我关注了这个问题 Using Moq to mock only some methods 但仍然抛出异常。

这是我的主要 class。 Foo 充当装饰器。

public class Foo {
    public Bar _b {get; set;}

    public Foo(Bar b) {
        this._b = b;
    }

    public bool MyMethod(){
        
        return ComplexMethod(_b.name);
    }

    public bool ComplexMethod(){
        ...
    }
}

测试class

[TestClass]
public class Foo {
    [TestMethod]
    public void TestFoo() {
        var b = new Bar() {name = "name"};
        var mock = new Mock<Foo>(b);
        mock.CallBase = true;
        mock.Setup(x => x.ComplexMethod()).Returns(true);
        var result = mock.Object.MyMethod();
        ...
    }
}

抛出异常:

System.NotSupportedException: 'Unsupported expression: x => x.ComplexMethod() Non-overridable members (here: Foo.ComplexMethod) may not be used in setup / verification expressions.'

您应该模拟依赖项而不是实际对象。所以,如果你想评估 MyMethod of Foo 那么你应该模拟 Bar.

为了能够模拟 Barname 成员:

  • 您应该将其标记为 virtualabstract (ref)
  • 或者你应该定义一个接口

我们来看后一种情况:

public interface IBar
{
   string Name { get; }
}

public class Bar: IBar
{
   public string Name { get; set; }
}

public class Foo 
{
    private readonly IBar _b;
    
    public Foo(IBar b) {
        this._b = b;
    }
    ...
}

使用此设置,您的单元测试可能如下所示:

[TestMethod]
public void GivenABar_WhereTheNameIsEmpty_WhenICallMyMethod_ThenItReturnsTrue() 
{
    //Arrange
    var barMock = new Mock<IBar>();
    barMock.SetupGet(x => x.Name).Returns("");

    //Act
    var SUT = new Foo(barMock.Object);
    var result = SUT.MyMethod();

    //Assert
    barMock.VerifyGet(m => m.Name, Times.Once);
    Assert.True(result);
}
[TestMethod]
public void GivenABar_WhereTheNameIsNotEmpty_WhenICallMyMethod_ThenItReturnsFalse() 
{
    //Arrange
    var barMock = new Mock<IBar>();
    barMock.SetupGet(x => x.Name).Returns("test");

    //Act
    var SUT = new Foo(barMock.Object);
    var result = SUT.MyMethod();

    //Assert
    barMock.VerifyGet(m => m.Name, Times.Once);
    Assert.False(result);
}

更新:模拟 SUT 的一种方法

如果你只想模拟 FooComplexMethod 那么你可以使用最小起订量的 As.

const bool expectedResult = true;
var fooMock = new Mock<Foo>(barInstance).As<IFoo>();
fooMock.Setup(f => f.ComplexMethod()).Returns(expectedResult);

请注意,此技术仅在 Foo 实现 IFoo 时有效,这也公开了 ComplexMethod

public interface IFoo
{
    bool MyMethod();
    bool ComplexMethod();
    ...
}

public class Foo: IFoo
{
    ...
}