我有一个键值存储区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;
}
});