NSubstitute 模拟扩展方法
NSubstitute mock extension method
我想做模拟扩展方法,但它不起作用。如何才能做到这一点?
public static class RandomExtensions
{
public static IEnumerable<int> NextInt32s(this System.Random random, int neededValuesNumber, int minInclusive, int maxExclusive)
{
// ...
}
}
[Fact]
public void Select()
{
var randomizer = Substitute.For<DefaultRandom>();
randomizer.NextInt32s(3, 1, 10).Returns(new int[] { 1, 2, 3 });
}
根据SOLID原则,依赖倒置定义低层模型不应该依赖高层模型,而是依赖像接口这样的抽象,模拟概念主要用于模拟接口,这样低层模型就不会被测试。
NSubstitute 不能根据 Sriram 的评论模拟扩展方法,但您仍然可以将模拟参数传递给扩展方法。
在这种情况下,Random
class 具有虚拟方法,因此我们可以直接使用 NSubstitute 和其他基于 DynamicProxy 的模拟工具来模拟它。 (特别是对于 NSubstitute,我们需要非常小心地模拟 classes。请阅读 the documentation 中的警告。)
public static class RandomExtensions {
public static IEnumerable<int> NextInt32s(this System.Random random, int neededValuesNumber, int minInclusive, int maxExclusive) { /* ... */ }
}
public class RandomExtensionsTests {
[Test]
public void Select()
{
const int min = 0, max = 10;
var randomizer = Substitute.For<Random>();
randomizer.Next(min, max).Returns(1, 2, 3);
var result = randomizer.NextInt32s(3, 0, 10).ToArray();
Assert.AreEqual(new[] {1, 2, 3}, result);
}
}
是的,如果您创建一个接口(例如 IRandom
)并扩展该接口而不是实际实现,您就可以模拟。然后你应该能够在你的测试中模拟接口 class.
public interface IRandom
{
}
public class Random : IRandom
{
}
public static class RandomExtensions
{
public static string NextInt32s(
this IRandom random,
int neededValuesNumber,
int minInclusive,
int maxExclusive)
{
}
}
在你的测试中class添加:
IRandom randomizer = Substitute.For<IRandom>();
var result = randomizer.NextInt32s(3,0,10);
通过这个过程,您只是在模拟界面而不是实际的界面 class。
作为对其他答案的扩展,以下是我如何绕过它。
假设有一个接口 IDoStuff
并且有一个扩展 IDoStuff
的库。您有一个实现了 IDoStuff
的 class MyClass
,并且有人在某处使用针对接口的扩展方法。看起来像这样;
using System;
interface IDoStuff
{
string SayHello();
}
class MyClass : IDoStuff
{
public string SayHello()
{
return "Hello";
}
}
// somewhere someone wrote an extension method for IDoStuff
static class DoStuffExtensions
{
static string SayHelloToBob(this IDoStuff other)
{
return other.SayHello() + " Bob";
}
}
class UserOfIDoStuff
{
void UseIDoStuff(IDoStuff incoming)
{
Console.WriteLine(incoming.SayHelloToBob());
}
}
您想模拟 IDoStuff
但不能模拟扩展方法 SayHelloToBob
。您可以做的是创建另一个实现 IDoStuff
但还包括 SayHelloToBob
.
的接口
interface IDoStuffWithExtensions : IDoStuff
{
string SayHelloToBob();
}
class MyClass : IDoStuffWithExtensions
{
public string SayHello()
{
return "Hello";
}
// Wrap / internalise the extension method
public string SayHelloToBob()
{
return DoStuffExtensions.SayHelloToBob(this);
}
}
class UserOfIDoStuff
{
void UseIDoStuff(IDoStuffWithExtensions incoming)
{
Console.WriteLine(incoming.SayHelloToBob());
}
}
现在你可以愉快地模拟了IDoStuffWithExtensions
。
我想做模拟扩展方法,但它不起作用。如何才能做到这一点?
public static class RandomExtensions
{
public static IEnumerable<int> NextInt32s(this System.Random random, int neededValuesNumber, int minInclusive, int maxExclusive)
{
// ...
}
}
[Fact]
public void Select()
{
var randomizer = Substitute.For<DefaultRandom>();
randomizer.NextInt32s(3, 1, 10).Returns(new int[] { 1, 2, 3 });
}
根据SOLID原则,依赖倒置定义低层模型不应该依赖高层模型,而是依赖像接口这样的抽象,模拟概念主要用于模拟接口,这样低层模型就不会被测试。
NSubstitute 不能根据 Sriram 的评论模拟扩展方法,但您仍然可以将模拟参数传递给扩展方法。
在这种情况下,Random
class 具有虚拟方法,因此我们可以直接使用 NSubstitute 和其他基于 DynamicProxy 的模拟工具来模拟它。 (特别是对于 NSubstitute,我们需要非常小心地模拟 classes。请阅读 the documentation 中的警告。)
public static class RandomExtensions {
public static IEnumerable<int> NextInt32s(this System.Random random, int neededValuesNumber, int minInclusive, int maxExclusive) { /* ... */ }
}
public class RandomExtensionsTests {
[Test]
public void Select()
{
const int min = 0, max = 10;
var randomizer = Substitute.For<Random>();
randomizer.Next(min, max).Returns(1, 2, 3);
var result = randomizer.NextInt32s(3, 0, 10).ToArray();
Assert.AreEqual(new[] {1, 2, 3}, result);
}
}
是的,如果您创建一个接口(例如 IRandom
)并扩展该接口而不是实际实现,您就可以模拟。然后你应该能够在你的测试中模拟接口 class.
public interface IRandom
{
}
public class Random : IRandom
{
}
public static class RandomExtensions
{
public static string NextInt32s(
this IRandom random,
int neededValuesNumber,
int minInclusive,
int maxExclusive)
{
}
}
在你的测试中class添加:
IRandom randomizer = Substitute.For<IRandom>();
var result = randomizer.NextInt32s(3,0,10);
通过这个过程,您只是在模拟界面而不是实际的界面 class。
作为对其他答案的扩展,以下是我如何绕过它。
假设有一个接口 IDoStuff
并且有一个扩展 IDoStuff
的库。您有一个实现了 IDoStuff
的 class MyClass
,并且有人在某处使用针对接口的扩展方法。看起来像这样;
using System;
interface IDoStuff
{
string SayHello();
}
class MyClass : IDoStuff
{
public string SayHello()
{
return "Hello";
}
}
// somewhere someone wrote an extension method for IDoStuff
static class DoStuffExtensions
{
static string SayHelloToBob(this IDoStuff other)
{
return other.SayHello() + " Bob";
}
}
class UserOfIDoStuff
{
void UseIDoStuff(IDoStuff incoming)
{
Console.WriteLine(incoming.SayHelloToBob());
}
}
您想模拟 IDoStuff
但不能模拟扩展方法 SayHelloToBob
。您可以做的是创建另一个实现 IDoStuff
但还包括 SayHelloToBob
.
interface IDoStuffWithExtensions : IDoStuff
{
string SayHelloToBob();
}
class MyClass : IDoStuffWithExtensions
{
public string SayHello()
{
return "Hello";
}
// Wrap / internalise the extension method
public string SayHelloToBob()
{
return DoStuffExtensions.SayHelloToBob(this);
}
}
class UserOfIDoStuff
{
void UseIDoStuff(IDoStuffWithExtensions incoming)
{
Console.WriteLine(incoming.SayHelloToBob());
}
}
现在你可以愉快地模拟了IDoStuffWithExtensions
。