【问题标题】:C# Moq mock partial methods but throw out NotSupportedExceptionC# Moq 模拟部分方法但抛出 NotSupportedException
【发布时间】:2021-06-16 21:45:06
【问题描述】:

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

这是我的主要课程。 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(){
        ...
    }
}

测试类

[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: '不支持的表达式:x => x.ComplexMethod() 不可覆盖的成员(这里:Foo.ComplexMethod) 不能在设置/验证表达式中使用。'

【问题讨论】:

  • 只能模拟虚拟方法或接口。使方法虚拟或抽象出一个接口
  • 模拟框架有两种类型:受约束的和不受约束的。前者:Moq、NSubstitute、FakeItEasy - 只允许您伪造虚拟会员。后者:TypeMock、JustMock、MS Fakes、Prig、Ionad.Fody、Pose、Harmony、MethodRedirect - 允许你伪造任何你想要的东西:非虚拟、私有和静态成员、密封类等等。

标签: c# moq


【解决方案1】:

您应该模拟依赖项而不是实际对象。所以,如果你想评估MyMethodFoo,那么你应该模拟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,那么您可以使用 moq 的 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
{
    ...
}

【讨论】:

  • 嗯,我没看懂,但是我要模拟的方法位于 Foo.在您的示例中,我看不到您在哪里模拟 ComplexMethod
  • @CodaChang 在您的问题中,您在测试中调用了 Foo 的 MyMethod。在提供的 Foo 实现中,此方法与 ComplexMethod 无关。那么,如果你想测试不依赖 ComplexMethod 的 MyMethod,那你为什么要模拟它呢?
  • 对不起,我的问题没有说清楚,在 MyMethod 内部,它调用 ComplexMethod,这就是嘲笑的重点。
  • @CodeChang 想在MyMethod 上测试什么?它只是ComplexMethod 的包装。是否要确保它不会改变ComplexMethod 的结果?或者您到底想评估什么?
  • @CodaChang 我刚刚更新了我的帖子,请检查一下。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-07-11
  • 2012-06-16
  • 1970-01-01
  • 2020-02-18
  • 2011-03-23
  • 1970-01-01
相关资源
最近更新 更多