NSubstitute mock extension method

点点圈 提交于 2019-12-21 04:24:52

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!