【问题标题】:Testing multithreading service methods in mocks在模拟中测试多线程服务方法
【发布时间】:2011-11-06 17:46:52
【问题描述】:

例如,如果我给他 n 个来自不同线程的数据块,我想测试我的多线程方法调用存储库方法 n 次的事实。当然,模拟不是线程安全的,甚至不应该是。

[Test]
public void CanSaveCustomersInParallel()
{
    var customers = new List<List<Customer>>
                        {
                            new List<Customer>
                                {
                                    new Customer {FirstName = "FirstName1"},
                                    new Customer {FirstName = "FirstName2"}
                                },
                            new List<Customer>
                                {
                                    new Customer {FirstName = "FirstName3"},
                                    new Customer {FirstName = "FirstName4"}
                                }
                        };
    _serviceCustomers.ParallelSaveBatch(customers);
    _repoCustomers
        .Verify(x => x.SaveBatch(It.IsAny<List<Customer>>()), Times.Exactly(2));
}

当然,这个测试有时会失败,有时不会。但它的本质是不正确的。你能告诉我如何重写它吗?

【问题讨论】:

    标签: c# unit-testing moq


    【解决方案1】:

    嗯,下一个存根成功了:

    internal class ServiceStub: Service<DummyEntity>
    {
        private int _count;
    
        public int Count
        {
            get { return _count; }
        }
    
        public override void SaveBatch(IEnumerable<object> entities)
        {
           lock(this)
           {
               _count++;
           }
        }
    
        public ServiceStub(IRepository<DummyEntity> repository):base(repository)
        {
            _count = 0;
        }
    }
    

    单元测试看起来是下一个方式:

        [Test]
        public void CanSaveCustomersInParallel()
        {
            var service = new ServiceStub(new DummyRepository());
            var customers = new List<List<Customer>>
                                {
                                    new List<Customer>
                                        {
                                            new Customer {FirstName = "FirstName1"},
                                            new Customer {FirstName = "FirstName2"}
                                        },
                                    new List<Customer>
                                        {
                                            new Customer {FirstName = "FirstName3"},
                                            new Customer {FirstName = "FirstName4"}
                                        }
                                };
            service.ParallelSaveBatch(customers);
            Assert.AreEqual(service.Count, customers.Count);
        }
    

    【讨论】:

      【解决方案2】:

      您可能有兴趣知道,从 v 4.1, Moq 开始,线程安全性有了很大提高,您应该会发现像您这样的测试,在并行运行时,现在应该可以按预期进行验证。

      更多关于不稳定的起订量行为here

      NullReferenceException 或IndexOutOfRangeExceptionmock.Verify(&lt;&gt;, Times.Exactly(N)) 失败(通常是计数不足)。从 4.1 开始,这些问题现在似乎已修复 MacGyvered,~感谢社区!

      编辑根据@Danny 下面的评论,请注意在 4.1 中所做的更改包括 locking the Mock,如果您需要测试代码的并行性,这并没有多大用处。

      【讨论】:

      • 我们刚刚发现这不是真的...... 4.1 添加了lock (Mock) 周围的东西,使模拟对多线程代码完全无用。这似乎没有“改进”......我们不得不回滚到 4.0,因为它破坏了我们的多线程/并发测试! :(
      • 哎哟 - 我没有注意到这一点。 Here is one such example.
      • 我叫丹尼,不是大卫 ;-)
      • 我提出了一个问题,希望他们关心这个重大变化:github.com/Moq/moq4/issues/62
      【解决方案3】:

      设计更改可以简化此测试。创建一个执行实际工作的 SaveWorker,以及一个在另一个线程上执行 SaveWork 工作的代理(相同的抽象)。然后是一个 SaveWorkerFactory,它返回一个 ThreadedSaveWorker,给定一个客户。最后,将 SaveWorkerFactory 的 Mock 注入 _serviceCustomers,并验证它是否执行了 2 次调用。

      【讨论】:

        猜你喜欢
        • 2018-09-11
        • 1970-01-01
        • 2011-01-28
        • 2023-03-31
        • 1970-01-01
        • 1970-01-01
        • 2013-04-07
        • 1970-01-01
        • 2017-03-14
        相关资源
        最近更新 更多