Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/264.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在测试异步方法时如何使用序列?_C#_Unit Testing_Asynchronous_Async Await_Moq - Fatal编程技术网

C# 在测试异步方法时如何使用序列?

C# 在测试异步方法时如何使用序列?,c#,unit-testing,asynchronous,async-await,moq,C#,Unit Testing,Asynchronous,Async Await,Moq,我正在使用并且在测试异步方法时遇到问题。 当我这样做时: [Test] public async Task Demo() { using (Sequence.Create()) { _fooMock.Setup(f => f.Fooxiate()).InSequence(); _barMock.Setup(b => b.Baronize()).InSequence(); var result = aw

我正在使用并且在测试异步方法时遇到问题。 当我这样做时:

[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);
    }
}
调用_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();
    }
}
使用System.Threading.Tasks;
使用最小起订量;
使用Moq.序列;
使用NUnit.Framework;
命名空间TestingAsync.Tests
{
[测试夹具]
公共类测试
{
二等兵;;
私人模拟(fooMock),;
私人模拟(barMock),;
[设置]
公共作废设置()
{
_fooMock=新Mock();
_barMock=新Mock();
_cut=新的SomeClass(\u fooMock.Object,\u barMock.Object);
}
[测试]
公共异步任务演示()
{
使用(Sequence.Create())
{
_fooMock.Setup(f=>f.Fooxiate()).InSequence();
_Setup(b=>b.Baronize()).InSequence();
var result=await_cut.domysuffasync();
AreEqual(“someString”,result);
}
}
}
公共类
{
私有只读iFooFoo;
专用只读IBar\u条;
公共SomeClass(IFoo-foo、IBar)
{
_巴=巴;
_foo=foo;
}
公共异步任务domysuffasync()
{
返回等待任务。运行(()=>domysuff());
}
私有字符串domysuff()
{
_foo.Fooxiate();
_巴罗尼兹();
返回“someString”;
}
}
公共接口IBar
{
void Baronize();
}
公共接口IFoo
{
void Fooxiate();
}
}

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()");

Moq.Sequences无法保证异步代码中调用的顺序,因为:

  • 并发代码通常没有确定的执行顺序
  • 异步是对线程的抽象,因此比线程更难预测。有许多技术会导致调用序列的不确定性,例如在后台线程中使用Task.Run、使用Parallel.For/ForEach、使用TPL数据流、使用Task.WhenAll等
  • 正确解释了Moq.Sequences由于使用了
    [ThreadStatic]
    而无法正确支持
    异步
    /
    等待

    根据OP的请求,我更新了该库,以更好地支持现代并发编程模式。(希望现在人们使用
    任务
    s编程,而不是
    线程
    s。)

    从2.1.0版开始,您可以使用
    AsyncLocal
    而不是
    [ThreadStatic]
    变量使Moq.Sequences跟踪环境序列。这意味着环境序列可以跨异步边界“流动”,例如
    等待
    ,并且在延续中仍然可见(可能在不同的线程上运行)

    出于向后兼容性的原因,您当前需要在运行任何测试之前通过执行以下操作来选择新行为:

    Sequence.ContextMode = SequenceContextMode.Async;
    

    在撰写本文时,新的行为还没有经过广泛的测试。

    感谢您添加此stakx!令人印象深刻:)我以前不知道AsyncLocal,谢谢你教我@约翰宾德尔:这是一个相当新的添加到.NET(4.6)中,我只是在不久前从令人敬畏的Castle DynamicProxy团队那里了解到的。在.NET4.6之前,还有一种更为神秘的东西:
    CallContext
    。玩得开心。:)再次感谢斯塔克斯@我把这个答案标为(现在)正确答案。