当输出参数返回不同的派生类型时,NSubstitute 抛出 ArgumentSetWithIncompatibleValueException

NSubstitute throws ArgumentSetWithIncompatibleValueException when out parameters gives back different derived type

我有一个键值存储 keyValueDatabase。要请求数据
IKeyValueResult keyValueDatabase.GetKeyValue(string id, out IKeyValue value) 必须使用请求值的 id 调用。该值作为从 IKeyValue 派生的对象通过 out 参数返回。界面如下所示:

public interface IKeyValue
{
  string ID { get; set; }
}

//analogue IKeyValueString...
public interface IKeyValueDouble : IKeyValue
{
  double Value { get; set; }
}

现在我使用下面的代码配置这个键值存储的存根。

ReturnedKeyValues 是我创建的不同类型 IKeyValue 存根的集合。

IKeyValue keyValue;
keyValueDatabase.GetKeyValue(Arg.Any<string>(),
    out Arg.Any<IKeyValue>()).ReturnsForAnyArgs(info =>
{
    if (ReturnedKeyValues.Select(keyVal => keyVal.ID).Contains(info[0]))
    {
        info[1] = ReturnedKeyValues.First(keyVal => keyVal.ID == (string)info[0]);
        return okResult;
    }
    else
    {
        info[1] = null;
        return unavailableResult;
    }
});

当第一次使用此存根时 keyValueDatabase.GetKeyValue id 比方说 'a' 它会返回 IKeyValueDouble 类型的输出值,因为它应该。现在,当使用 id 'b' 第二次调用此方法时,应返回 IKeyValueString 类型的值。但是在这种情况下会抛出 ArgumentSetWithIncompatibleValueException:

Could not set value of type ObjectProxy_1 to argument 1 (IKeyValue&) because the types are incompatible.'

使用 Returns 而不是 ReturnsForAnyArgs 的行为方式相同。我正在使用 NSubstitute 4.2.0 和 .Net-Framework 4.7。

编辑:

在我的测试中,我使用通过外部库指定的接口创建了自己的数据库存根。我必须在生产代码中实现这个数据库。

public interface IDatabase
{
    /// <summary>Returns the value of one entry</summary>
    /// <param name="id">The entry's ID</param>
    /// <param name="value">Returns the value of the entry</param>
    /// <returns>The result of the read operation</returns>
    IKeyValueResult GetKeyValue(string id, out IKeyValue value);
}

在 ReturnedKeyValues 中是我存储的存根 IKeyValue 列表,应该归还:

private static List<IKeyValue> ReturnedKeyValues = new List<IKeyValue>()
{
    createKeyValueA(), createKeyValueB()
};

private static IKeyValue createKeyValueA()
{
    var keyVal = Substitute.For<IKeyValueDouble>();
    keyVal.ID.Returns("a");
    keyVal.Value.Returns(21.31);
    return keyVal;
}

private static IKeyValue createKeyValueB()
{
    var keyVal = Substitute.For<IKeyValueString>();
    keyVal.ID.Returns("b");
    keyVal.Value.Returns("GA7713");
    return keyVal;
}

进一步调查发现问题与重用out变量有关。在第一次分配 outVal 后,它是 IKeyValueDouble 类型。当它被重用时,它应该被分配给 IKeyValueString 类型。但是抛出上述异常。连续调用时都会发生这种情况:

IKeyValue outVal;
keyValueDatabase.GetKeyValue("a", out outVal);
keyValueDatabase.GetKeyValue("b", out outVal);

或者在循环中通过编译器优化重用变量时:

foreach (string key in keys)
{
    ...
    IKeyValue outVal;
    IKeyValueResult success = keyValueDatabase.GetKeyValue(key.ID, out val);
   ...
}

作为解决方法,我在将 out 变量的值设置为实际值之前将其设置为 null:

IKeyValue keyValue;
keyValueDatabase.GetKeyValue(Arg.Any<string>(),
    out Arg.Any<IKeyValue>()).ReturnsForAnyArgs(info =>
{
    if (ReturnedKeyValues.Select(keyVal => keyVal.ID).Contains(info[0]))
    {
        //Workarround: Setting the out variable to null before assigning a
        //new value fixes the problem
        info[1] = null;
        info[1] = ReturnedKeyValues.First(keyVal => keyVal.ID == (string)info[0]);
        return okResult;
    }
    else
    {
        info[1] = null;
        return unavailableResult;
    }
});
NSubstitute 3.1.0 中的

Bug。已修复版本 4.2.1