当输出参数返回不同的派生类型时,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
我有一个键值存储 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;
}
});
Bug。已修复版本 4.2.1