C# - 如何在生产者/消费者场景中对私有方法进行单元测试?

时间:2017-07-18 07:05:27

标签: c# multithreading unit-testing

我想弄清楚是否有一些好的做法如何在生产者/消费者场景中对业务逻辑进行单元测试?

我们在应用程序中使用的设计是,几个公共方法接受来自外部系统的请求,将它们放在一个“任务队列”中,然后有另一个线程负责处理队列中的任务。问题是public方法没有做任何复杂的事情,只是将新任务排入队列并设置手动重置事件(以便其他线程可以开始处理新项目)并且所有应该测试的复杂代码都在私人方法。

我知道我可以将这些私有方法更改为内部但我不喜欢这样,因为每个开发人员都可以直接调用这些方法而不是公共方法,从而完全绕过任务队列。

那么有什么方法可以测试这些私有方法吗?也许小型重构或重新设计?感谢

我们使用的设计的骨架:

OrderService:

1)公共方法

public void OrderReceived(OrderDto orderDto, Action<Exception> callback)
{
    try
    {
        taskManager.ProcessWorkItem(() => OrderReceivedImpl(orderDto, callback));
    }
    catch (Exception ex)
    {
        Logger.Error(ex);

        callback(ex);
    }
}

2)我想测试的私有方法:

private void OrderReceivedImpl(OrderDto orderDto, Action<Exception> callback)
{
    try
    {
        // some business logic

        callback(null);
    }
    catch (Exception ex)
    {
        Logger.Error(ex);

        callback(ex);
    }
}

TaskManager类:

1)排队任务的方法

public void ProcessWorkItem(Action action)
{
    taskQueue.Enqueue(action);      

    newWorkItemReceived.Set();
}

2)分离线程处理任务的方法:

private void ProcessWorkItemQueue()
{
    while (true)
    {
        var waitResult = WaitHandle.WaitAny(new WaitHandle[] { newWorkItemReceived, stopEventReceived });

        if (waitResult == 0)
        {
            while (true)
            {
                if (taskQueue.Count == 0) break;

                Action action;

                taskQueue.TryDequeue(out action);                   

                try
                {
                    action.Invoke();
                }
                catch (Exception ex)
                {
                    Logger.Error(ex);
                }

                if (stopEventReceived.WaitOne(1)) return;
            }
        }
        else
        {
            return;
        }
    }
}

4 个答案:

答案 0 :(得分:1)

如果您不想更改当前实现,请使用模拟框架(如Moq),并创建taskManager的模拟。您的模拟对象可以设置为调用ProcessWorkItem只是立即调用操作并允许私有方法保持私有。

答案 1 :(得分:1)

也许将您的业务逻辑注入OrderService ala战略模式。类似的东西:

public interface IOrderReceiverStrategy
{
    void OrderReceived(OrderDto orderDto, Action<Exception> callback);
}

public class OrderReceiverStrategy : IOrderReceiverStrategy
{
    public void OrderReceived(OrderDto orderDto, Action<Exception> callback)
    {
        try
        {
            // some business logic

            callback(null);
        }
        catch (Exception ex)
        {
            Logger.Error(ex);

            callback(ex);
        }
    }
}

public class OrderService
{
    public OrderService(IOrderReceiverStrategy strategy) { }


    public void OrderReceived(OrderDto orderDto, Action<Exception> callback)
    {
        try
        {
            taskManager.ProcessWorkItem(() => _strategy.OrderReceived(orderDto, callback));
        }
        catch (Exception ex)
        {
            Logger.Error(ex);

            callback(ex);
        }
    }
}

然后,您只需通过单元测试OrderReceiverStrategy验证您的业务逻辑。

答案 2 :(得分:0)

您应该根据给定的参数测试该私有方法将更改的状态。

如果使用外部资源(数据库,文件系统,其他API:s)的私有方法,你应该抽象它们并作为依赖项传递给类,在单元测试中你可以模拟它们并对模拟的依赖项断言逻辑。 / p>

另一种方法是遵循“单一责任原则”。有了这个原则,班级应该只有一个改变的理由 此刻我看到了下一个原因

  1. 当您更改业务逻辑以处理收到的订单时,您需要更改您的班级。
  2. 当您更改处理不同订单的方式时(在队列或集合中或以其他方式),您需要更改类。
  3. 因此,您可以将订单处理移到类外部,并将其作为依赖项传递给队列类。

答案 3 :(得分:0)

我会将它们更改为内部并移动到独立库。之后我会做一个可以看到内部的TestClass,并在那里实现一些代理调用这些方法的公共“methodsExtensionsTesting”,并在你的unitTests中调用它们,这样你就可以直接检查它们。

相关问题