问题
I want to do mock extension method, but it does not work. How can this be done?
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 });
}
回答1:
NSubstitute can not mock extension methods as per Sriram's comment, but you can still pass a mocked argument to an extension method.
In this case, the Random
class has virtual methods, so we can mock that directly with NSubstitute and other DynamicProxy-based mocking tools. (For NSubstitute in particular we need to be very careful mocking classes. Please read the warning in 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);
}
}
回答2:
Yes you can mock if you create an interface such as IRandom
and extend the interface instead of the actual implementation. Then you should be able mock the interface in your test 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)
{
}
}
In your test class add:
IRandom randomizer = Substitute.For<IRandom>();
var result = randomizer.NextInt32s(3,0,10);
By this process you are just mocking the interface not the actual class.
回答3:
As an extension to other answers, here is how I got round it.
Imagine there is an interface IDoStuff
and there is a library that extends IDoStuff
. You have a class MyClass
that implements IDoStuff
and somewhere someone uses the extension method against the interface. It looks like this;
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());
}
}
You want to mock IDoStuff
but you cannot mock the extension method SayHelloToBob
. What you can do is to create another interface that implements IDoStuff
but also includes 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());
}
}
Now you can happily mock IDoStuffWithExtensions
.
回答4:
According to SOLID principle dependence inversion defines that lower level model should not be depended high level model but depended on abstract like interface and mocking concept is mainly used to mock interface so that low level model is not tested.
来源:https://stackoverflow.com/questions/28693698/nsubstitute-mock-extension-method