g t*_*g t 55 c# nunit unit-testing moq sequential
我需要测试以下方法:
CreateOutput(IWriter writer)
{
writer.Write(type);
writer.Write(id);
writer.Write(sender);
// many more Write()s...
}
Run Code Online (Sandbox Code Playgroud)
我创建了一个Moq'd IWriter
,我想确保Write()
以正确的顺序调用这些方法.
我有以下测试代码:
var mockWriter = new Mock<IWriter>(MockBehavior.Strict);
var sequence = new MockSequence();
mockWriter.InSequence(sequence).Setup(x => x.Write(expectedType));
mockWriter.InSequence(sequence).Setup(x => x.Write(expectedId));
mockWriter.InSequence(sequence).Setup(x => x.Write(expectedSender));
Run Code Online (Sandbox Code Playgroud)
但是,第二次调用Write()
in CreateOutput()
(写入id
值)会抛出一条MockException
消息" IWriter.Write()调用失败,模拟行为为Strict.模拟上的所有调用都必须有相应的设置. "
我也发现很难找到任何明确的,最新的Moq序列文档/示例.
我做错了什么,或者我不能使用相同的方法设置序列?如果没有,有没有我可以使用的替代品(最好使用Moq/NUnit)?
Ser*_*kiy 61
在同一个模拟器上使用MockSequence时有bug .它肯定会在Moq库的后续版本中修复(您也可以通过更改Moq.MethodCall.Matches实现来手动修复它).
如果您只想使用Moq,那么您可以通过回调验证方法调用顺序:
int callOrder = 0;
writerMock.Setup(x => x.Write(expectedType)).Callback(() => Assert.That(callOrder++, Is.EqualTo(0)));
writerMock.Setup(x => x.Write(expectedId)).Callback(() => Assert.That(callOrder++, Is.EqualTo(1)));
writerMock.Setup(x => x.Write(expectedSender)).Callback(() => Assert.That(callOrder++, Is.EqualTo(2)));
Run Code Online (Sandbox Code Playgroud)
g t*_*g t 10
我设法得到了我想要的行为,但它需要从http://dpwhelan.com/blog/software-development/moq-sequences/下载第三方库.
然后可以使用以下方法测试序列:
var mockWriter = new Mock<IWriter>(MockBehavior.Strict);
using (Sequence.Create())
{
mockWriter.Setup(x => x.Write(expectedType)).InSequence();
mockWriter.Setup(x => x.Write(expectedId)).InSequence();
mockWriter.Setup(x => x.Write(expectedSender)).InSequence();
}
Run Code Online (Sandbox Code Playgroud)
我已经添加了这个作为答案,部分是为了帮助记录这个解决方案,但我仍然对是否可以单独使用Moq 4.0实现类似的东西感兴趣.
我不确定Moq是否仍处于开发阶段,但是修复问题MockSequence
,或者在Moq中包含moq-sequences扩展可以很好地看到.
我写了一个扩展方法,它将根据调用顺序断言.
public static class MockExtensions
{
public static void ExpectsInOrder<T>(this Mock<T> mock, params Expression<Action<T>>[] expressions) where T : class
{
// All closures have the same instance of sharedCallCount
var sharedCallCount = 0;
for (var i = 0; i < expressions.Length; i++)
{
// Each closure has it's own instance of expectedCallCount
var expectedCallCount = i;
mock.Setup(expressions[i]).Callback(
() =>
{
Assert.AreEqual(expectedCallCount, sharedCallCount);
sharedCallCount++;
});
}
}
}
Run Code Online (Sandbox Code Playgroud)
它的工作原理是利用闭包对作用域变量的工作方式.由于sharedCallCount只有一个声明,因此所有闭包都将引用同一个变量.使用expectedCallCount,在循环的每次迭代中实例化一个新实例(而不是简单地在闭包中使用i).这样,每个闭包都有一个i范围的副本,仅在调用表达式时与sharedCallCount进行比较.
这是扩展的小单元测试.请注意,此方法在您的设置部分调用,而不是您的断言部分.
[TestFixture]
public class MockExtensionsTest
{
[TestCase]
{
// Setup
var mock = new Mock<IAmAnInterface>();
mock.ExpectsInOrder(
x => x.MyMethod("1"),
x => x.MyMethod("2"));
// Fake the object being called in order
mock.Object.MyMethod("1");
mock.Object.MyMethod("2");
}
[TestCase]
{
// Setup
var mock = new Mock<IAmAnInterface>();
mock.ExpectsInOrder(
x => x.MyMethod("1"),
x => x.MyMethod("2"));
// Fake the object being called out of order
Assert.Throws<AssertionException>(() => mock.Object.MyMethod("2"));
}
}
public interface IAmAnInterface
{
void MyMethod(string param);
}
Run Code Online (Sandbox Code Playgroud)
最近,我为 Moq 整合了两个功能:VerifyInSequence() 和VerifyNotInSequence()。它们甚至可以与 Loose Mocks 一起使用。但是,这些仅在起订量存储库分支中可用:
https://github.com/grzesiek-galezowski/moq4
并等待更多评论和测试,然后再决定是否可以将其包含在官方起订量发布中。然而,没有什么可以阻止您以 ZIP 形式下载源代码,将其构建到 dll 中并尝试一下。使用这些功能,您需要的序列验证可以写成这样:
var mockWriter = new Mock<IWriter>() { CallSequence = new LooseSequence() }; //执行必要的调用 mockWriter.VerifyInSequence(x => x.Write(expectedType)); mockWriter.VerifyInSequence(x => x.Write(expectedId)); mockWriter.VerifyInSequence(x => x.Write(expectedSender));
(请注意,您可以根据需要使用其他两个序列。松散序列将允许您要验证的序列之间的任何调用。StrictSequence 不允许这样做,而 StrictAnytimeSequence 与 StrictSequence 类似(已验证的调用之间没有方法调用),但允许前面有任意数量的任意调用的序列。
如果您决定尝试此实验性功能,请评论您的想法: https: //github.com/Moq/moq4/issues/21
谢谢!
我刚刚遇到了类似的情况,并受到已接受答案的启发,我使用了以下方法:
//arrange
var someServiceToTest = new SomeService();
var expectedCallOrder = new List<string>
{
"WriteA",
"WriteB",
"WriteC"
};
var actualCallOrder = new List<string>();
var mockWriter = new Mock<IWriter>();
mockWriter.Setup(x => x.Write("A")).Callback(() => { actualCallOrder.Add("WriteA"); });
mockWriter.Setup(x => x.Write("B")).Callback(() => { actualCallOrder.Add("WriteB"); });
mockWriter.Setup(x => x.Write("C")).Callback(() => { actualCallOrder.Add("WriteC"); });
//act
someServiceToTest.CreateOutput(_mockWriter.Object);
//assert
Assert.AreEqual(expectedCallOrder, actualCallOrder);
Run Code Online (Sandbox Code Playgroud)
最简单的解决方案是使用Queue:
var expectedParameters = new Queue<string>(new[]{expectedType,expectedId,expectedSender});
mockWriter.Setup(x => x.Write(expectedType))
.Callback((string s) => Assert.AreEqual(expectedParameters.Dequeue(), s));
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
24221 次 |
最近记录: |