我试图为我的测试模拟第三方组件。 该组件可以存储一个字节数组,但没有属性,只有两种方法可以访问它。
// simple demo code, _comp is an object of this component
byte[] val = new byte[] { 5 };
// write new value
await _comp.WriteAsync(val);
// read value
byte[] newVal = _comp.ReadAsync();
现在在我的测试中,我想我可以设置WriteAsync方法来更改ReadAsync方法的Setup以返回传入的值。
Mock<IComponent> comp = new Mock<IComponent>;
comp.Setup(x => x.WriteAsync(It.IsAny<byte[]>())).Callback<byte[]>(bytes =>
{
comp.Setup(x => x.ReadAsync()).ReturnsAsync(bytes);
});
但是当我在测试中调试演示代码时,一切运行良好,
行comp.Setup(x => x.ReadAsync()).ReturnsAsync(bytes);
使用正确的值调用,但是当WriteAsync
调用返回时,我在行中的演示代码中获得NullReferenceException
await _comp.WriteAsync(val);
我不知道这里的空是什么,_comp
和val
不是。
我相信这是Moq框架内部的深层内容,但我没有得到任何有关它的堆栈跟踪信息。 Stacktrace只包含我的演示代码。
我在这里做错了吗? 或者是否无法从其自己的一个回调中更改Mock-object? 我认为,我需要的可以用一个属性完成,但不幸的是该组件没有。
答案 0 :(得分:1)
将写入的字节存储在本地变量中作为模拟的后备存储。在回调中,您可以设置数组的值,以便在ReadAsync中使用时可用。
WriteAsync
也是异步方法,因此需要返回Task
以允许异步流。
Mock<IComponent> comp = new Mock<IComponent>();
byte[] bytes = null;
comp.Setup(x => x.WriteAsync(It.IsAny<byte[]>()))
.Callback<byte[]>(b => bytes = b)
.Returns(Task.FromResult((object)null));
comp.Setup(x => x.ReadAsync()).ReturnsAsync(bytes);
根据评论进行更新。
在评论中提到WriteAsync返回Task<bool>
。虽然返回值的条件基于OP中提供的内容是未知的,但是具有基于所提供的数组的条件的示例可以看起来像这样
.Returns<byte[]>(b => Task.FromResult(b != null && b.Length > 0));
答案 1 :(得分:1)
所以对于偶然发现这个的人来说,我的初始代码是有效的,但是当该方法返回void之外的其他东西时,必须指定这个。 异步方法返回任务或任务&lt;&gt;,因此安装代码应该是例如
Mock<IComponent> comp = new Mock<IComponent>;
comp.Setup(x => x.WriteAsync(It.IsAny<byte[]>())).Callback<byte[]>(bytes =>
{
comp.Setup(x => x.ReadAsync()).ReturnsAsync(bytes);
}).ReturnsAsync(true);