我正在使用Moq.Sequences而我在测试异步方法时遇到问题。 当我这样做时:
[Test]
public async Task Demo()
{
using (Sequence.Create())
{
_fooMock.Setup(f => f.Fooxiate()).InSequence();
_barMock.Setup(b => b.Baronize()).InSequence();
var result = await _cut.DoMyStuffAsync();
Assert.AreEqual("someString", result);
}
}
当calling _foo.Fooxiate()
说:
Moq.Sequences.SequenceUsageException:'模拟调用只能使用MockSequence.Create()创建的活动MockSequence调用
以下是完整的演示代码,包括上述生产代码:
using System.Threading.Tasks;
using Moq;
using Moq.Sequences;
using NUnit.Framework;
namespace TestingAsync.Tests
{
[TestFixture]
public class SomeClassTests
{
private SomeClass _cut;
private Mock<IFoo> _fooMock;
private Mock<IBar> _barMock;
[SetUp]
public void Setup()
{
_fooMock = new Mock<IFoo>();
_barMock = new Mock<IBar>();
_cut = new SomeClass(_fooMock.Object, _barMock.Object);
}
[Test]
public async Task Demo()
{
using (Sequence.Create())
{
_fooMock.Setup(f => f.Fooxiate()).InSequence();
_barMock.Setup(b => b.Baronize()).InSequence();
var result = await _cut.DoMyStuffAsync();
Assert.AreEqual("someString", result);
}
}
}
public class SomeClass
{
private readonly IFoo _foo;
private readonly IBar _bar;
public SomeClass(IFoo foo, IBar bar)
{
_bar = bar;
_foo = foo;
}
public async Task<string> DoMyStuffAsync()
{
return await Task.Run(() => DoMyStuff());
}
private string DoMyStuff()
{
_foo.Fooxiate();
_bar.Baronize();
return "someString";
}
}
public interface IBar
{
void Baronize();
}
public interface IFoo
{
void Fooxiate();
}
}
答案 0 :(得分:4)
This other answer正确解释了由于使用了{async
/ await
,Moq.Sequences 没有没有正确支持[ThreadStatic]
/ Task
{1}}。
根据OP的要求,我更新了该库,以便为现代并发编程模式提供更好的支持。 (希望人们最近使用Thread
进行编程,而不是AsyncLocal<Sequence>
s。)
从版本2.1.0开始,您可以使用[ThreadStatic]
而不是await
变量使Moq.Sequences跟踪环境序列。这意味着环境序列可以&#34;流动&#34;跨越异步边界,例如Sequence.ContextMode = SequenceContextMode.Async;
,并且在延续中仍然可见(可能在不同的线程上运行)。
出于向后兼容性的原因,您目前需要在运行任何测试之前执行以下操作来选择新行为:
{{1}}
在撰写本文时,新行为尚未经过广泛测试so issue and bug reports are welcome。
答案 1 :(得分:2)
Moq.Sequences不会写成多线程,因为它使用[ThreadStatic]
属性来跟踪环境序列。
[ThreadStatic]
private static Sequence instance;
结果是环境序列仅存储在当前线程中。然后你调用Task.Run
产生一个后台线程来做工作。这会导致抛出异常,因为该线程的实例为null。
if (Instance == null)
throw new SequenceUsageException(context + " can only be called with an active MockSequence created with MockSequence.Create()");
https://github.com/dwhelan/Moq-Sequences → src/Moq.Sequences/Sequence.cs
Moq.Sequences没有一种方法可以保证异步代码中的调用顺序,因为: