使用来自同一 class 的已实现和模拟方法的混合

Using a mix of implemented and mocked methods from a same class

我正在尝试模拟 class 的某些方法,同时保持其他方法的真实实现。

现在,我确实在 Whosebug here 上找到了一些相关的东西。但是,none 的建议答案对我有帮助。

Lee 的回答在大多数情况下可能是好的,但在我的情况下是不合适的,因为我真的需要模拟一个接口(它有各种实现)而不是 class 本身。

Brandon 的回答非常接近,但我发现了以下问题。
为了方便起见,这里粘贴了他的代码:

var mock = new Mock<ITestClass>(); // Create Mock of interface

// Create instance of ITestClass implementation you want to use
var inst = new ActualTestClass();

// Setup to call method of an actual instance
// if method returns void use mock.Setup(...).Callback(...)
mock.Setup(m => m.SomeMethod(It.IsAny<int>())
    .Returns((int x) => inst.SomeMethod(x));

导致我出现问题的是这个示例适用于简单的场景,但如果“SomeMethod()”它从具有模拟设置的 ITestClass 调用另一个方法,它将不起作用。在那种情况下,“SomeMethod()”仍将使用真正的实现。

为清楚起见:
假设 ITestClass 实现了方法 SomeMethod 和 SomeOtherMethod。前者必须调用被模拟的后者:

mock.Setup(m => m.SomeOtherMethod(It.IsAny<bool>())
     .Returns(true);

现在 SomeMethod 如何使用这个 mock 而不是真正的实现?

编辑

我需要模拟一个接口而不是 class 本身的另一个原因是后一个选项不允许您测试 TestClass() 如果它是一个单例。

Moq、NSubstitute 或 FakeItEasy 等流行的隔离框架受限。不可能解决你的问题。

还有 不受约束 隔离框架:TypeMock、JustMock(均付费)和 MS Fakes(仅在 VS Enterprise 中可用)。

不过,我最近遇到了一个有趣的免费 Pose 库,它可以解决您的问题。

添加package.

打开命名空间:

using Pose;

测试代码:

var inst = new ActualTestClass();

Shim testClassShim = Shim.Replace(() => inst.SomeOtherMethod(Is.A<bool>()))
    .With((ActualTestClass _, bool b) => true);

int actual = 0;

PoseContext.Isolate(() =>
{
    actual = inst.SomeMethod(1); // it will use shim inside
},
testClassShim);

Assert.Equal(0, actual);

但它在许多方面仍然不如其商业对手。

需要注意的是,不受约束的工具虽然可以极其有效地解决隔离问题,但通过这种方式可以原谅设计错误,最终导致应用程序架构不佳。

完全不使用 Moq 怎么样?
您可以创建一个包装器 class,它派生自您的主要 class 并用您需要的任何东西覆盖您想要模拟的方法。
然后在你的测试中使用这个派生的class。
当然,这些重写方法可以使用 Moq 或定义为派生 class.

中的模拟