在模拟中测试多线程服务方法

时间:2011-08-30 11:43:15

标签: c# unit-testing moq

例如,我想测试一下这样一个事实:如果我从不同的线程中给出 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));
}

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

3 个答案:

答案 0 :(得分:4)

好吧,下一个存根就是这个伎俩:

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);
    }

答案 1 :(得分:4)

您可能有兴趣知道,从v 4.1, Moq起,现在确实有很多改进的线程安全性,并且您应该找到像你这样的测试,当并行运行时,现在应该按预期验证。

更多关于不稳定的Moq行为here

版本&lt; = 4.0中遇到的典型问题包括随机NullReferenceExceptionIndexOutOfRangeExceptionmock.Verify(<>, Times.Exactly(N))失败(通常带有欠数)。从4.1开始,这些问题现在似乎是 fixed MacGyvered,感谢社区!

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

答案 2 :(得分:-1)

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