无法使用 StackExchange.Redis 返回 RedisResult 的方法对 class 进行单元测试

Cannot unit test a class with a method returning RedisResult with StackExchange.Redis

我有一个用于 stackexchange redis 的简单包装器:

public interface IRedisClient
{        
    Task<RedisResult> ScriptEvaluate(LuaScript script, object parameters);
}

我有一个调用 ScriptEvaluate

的方法
public class Foo
{
    private readonly IRedisClient _client;

    public Foo(IRedisClient client)
    {
        _client = client;
    }

    public void RunScript()
    {
        _client.ScriptEvaluate(LuaScript.Prepare(""), new object());
    }
}

现在,当我使用 NSubstitute 模拟注入到 FooIRedisClient,然后调用 RunScript

public void Test()
{
    _foo = new Foo(Substitute.For<IRedisClient>());
    _foo.RunScript();
}

我收到以下错误:

System.TypeLoadException: Method 'AsBoolean' in type 'Castle.Proxies.RedisResultProxy' from assembly 'DynamicProxyGenAssembly2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=a621a9e7e5c32e69' does not have an implementation.

据我所知,Nsubstitute/Castle 内部构件无法正确使用 RedisResult。我没有找到任何解决方法。

可以用这个做点什么吗?

P.S。当我尝试将模拟配置为 return 一个值(相同的异常)时出现相同的错误:

_client
    .ScriptEvaluate(null, null)
    .ReturnsForAnyArgs(RedisResult.Create((RedisKey)"result"));

RedisResult是一个abstract类型,但是有static Create个通用场景的方法,还有一些静态属性比如EmptyArrayNullArray等。我无法告诉您如何配置您的特定伪造层,但最终,我希望涉及 RedisResult.Create

我很好奇为什么模拟抽象 RedisResult 不是一个简单的解决方案。

这似乎是 NSubstitute 实施的问题。

使用以下方法尝试重现问题

public class Foo {
    private readonly IRedisClient _client;

    public Foo(IRedisClient client) {
        _client = client;
    }

    public Task<RedisResult> RunScript() {
        return _client.ScriptEvaluate(LuaScript.Prepare(""), new object());
    }
}

我能够使用 NSubstitute 重现它,但在使用另一个模拟框架 (MOQ) 时能够完成测试

[TestClass]
public class MyTestClass {
    [TestMethod]
    public async Task Test1() {
        //Arrange
        var expected = RedisResult.Create((RedisKey)"result");
        var _client = Substitute.For<IRedisClient>();

        _client
            .ScriptEvaluate(Arg.Any<LuaScript>(), Arg.Any<object>())
            .Returns(expected);

        var _foo = new Foo(_client);

        //Act
        var actual = await _foo.RunScript();

        //Assert
        actual.Should().Be(expected);
    }

    [TestMethod]
    public async Task Test2() {
        //Arrange
        var expected = RedisResult.Create((RedisKey)"result");
        var _client = Mock.Of<IRedisClient>(_ => _.ScriptEvaluate(It.IsAny<LuaScript>(), It.IsAny<object>()) == Task.FromResult(expected));

        var _foo = new Foo(_client);

        //Act
        var actual = await _foo.RunScript();

        //Assert
        actual.Should().Be(expected);
    }
}