简体   繁体   中英

NSubstitute mock extension method

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 });
}

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);
    }
}

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.

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 .

根据SOLID原则,依赖性反演定义了较低级别的模型不应该依赖于高级模型,而是依赖于类似接口的抽象,而模拟概念主要用于模拟接口,以便不测试低级模型。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM