使用来自同一 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.
中的模拟
我正在尝试模拟 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.