当out参数返回不同的派生类型时,NSubstitute引发ArgumentSetWithIncompatibleValueException

时间:2019-06-27 18:50:32

标签: c# nsubstitute

我有一个键值存储区keyValueDatabase。要请求数据,必须使用请求值的ID调用
IKeyValueResult keyValueDatabase.GetKeyValue(string id, out IKeyValue value)。该值通过out参数作为从IKeyValue派生的对象返回。接口看起来像这样:

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类型的out值。现在,当再次使用id'b'调用此方法时,应返回IKeyValueString类型的值。但是,在这种情况下会抛出ArgumentSetWithIncompatibleValueException:

  

无法将类型ObjectProxy_1的值设置为参数1(IKeyValue&)   因为类型不兼容。'

使用Returns代替ReturnsForAnyArgs的行为相同。我正在使用带有.Net-Framework 4.7的NSubstitute 4.2.0。

编辑:

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

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

1 个答案:

答案 0 :(得分:0)

NSubstitute 3.1.0中的

Bug。已在4.2.1版中修复