简体   繁体   English

两种不能同时运行的方法,如何为这种情况编写单元测试,或使其可测试?

[英]Two methods which can't run concurrently, how to write unit tests for this case, or make it testable?

Let's say I have two methods in my class MethodA and MethodB . 假设我在类MethodAMethodB有两个方法。 Those methods are run in async, and I don't want that they run at the same time. 这些方法是异步运行的,我不希望它们同时运行。 In the app, MethodA is called by the user, but MethodB is run by a Timer in a background thread. 在应用程序中, MethodA由用户调用,而MethodB由计时器在后台线程中运行。

How I see the implementation in C#ish pseudocode: 我如何看待C#ish伪代码中的实现:

class MyClass 
{
     private static object _lock = new object();

     public async Task MethodA()
     {
          lock(_lock)
          {
              await DoSomeStuff();
          }
     }

     public async Task MethodB()
     {
          if(Monitor.IsEntered(_lock)
                return;
          lock(_lock)
          {
              await DoSomeStuff();
          }
     }
}

So first question is - is above approach correct? 所以第一个问题是-以上方法正确吗? I guess that's more a question for https://codereview.stackexchange.com/ . 我想这是https://codereview.stackexchange.com/的另一个问题。

So the second questions is - assuming that the approach is correct, how I can unit test, that MethodA waits for MethodB , and that MethodB doesn't run when MethodA is running? 因此,第二个问题是-假设的做法是正确的,我怎么能单元测试,即MethodA等待MethodB ,而MethodB时不运行MethodA运行? Or how can I refactor it so that's testable? 还是我可以重构它以便进行测试?

EDIT: accordingly to comments, changed from using flags to a lock. 编辑:根据注释,从使用标志更改为锁定。

Boolean flags are the obsolete way to synchronize two threads. 布尔标志是使两个线程同步的过时方法。 It causes race conditions when one thread can read a value of false while other thread is updating the value to true ; 当一个线程可以读取false值而另一个线程将其值更新为true时,则会导致竞争。

Since your case it not straightforward (B shouldn't way for B to end, while A should wait), then I would change the class use a Semaphore like this: 由于您的情况并不简单(B不应结束B,而A应该等待),因此我将使用Semaphore来更改类,如下所示:

public class MyClass
{
    private SemaphoreSlim semaphore = new SemaphoreSlim(1);

    public async Task MethodA()
    {
        await semaphore.WaitAsync();

        await DoSomeStuff();

        semaphore.Release();
    }

    public async Task MethodB()
    {
        bool success = await semaphore.WaitAsync(1);

        if (!success)
            return;

        await DoSomeStuff();

        await Task.Delay(TimeSpan.FromSeconds(5));
        semaphore.Release();
    }
}

I would consider putting all that in try..catch..finally block and release the semaphore in the finally block, but i'm trying to keep it simple while you can add that yourself. 我会考虑将所有内容都放入try..catch..finally块中,并在finally块中释放该信号量,但是我试图将其简化,而您可以自己添加。

Unit testing: 单元测试:

This is not straight forward to test. 这不是直接测试。 Taking threads into account, you might need to repeat the test multiple times to reach a case of failure. 考虑到线程,您可能需要多次重复测试才能达到失败的情况。 You might need to introduce an overload for method A that waits for some times, which might prove that method B is waiting for it. 您可能需要为方法A引入等待一段时间的重载,这可能证明方法B正在等待它。 Here is the test. 这是测试。 To test the case of failure, change new SemaphoreSlim(1); 要测试失败的情况,请更改new SemaphoreSlim(1); to new SemaphoreSlim(2); new SemaphoreSlim(2); and the test would fail because MethodB would start before MethodA ends. 并且测试将失败,因为MethodB将在MethodA结束之前开始。

[TestMethod]
public async Task TestingMyClassThreadSync()
{
    int repeatTimes = 100;

    int counter = 0;
    while (counter++ <= repeatTimes)
    {
        MyClass myClass = new MyClass();

        Task tA = myClass.MethodA();

        Task tB = myClass.MethodB();

        Task finishedTask = await Task.WhenAny(tA, tB);

        bool bFinishedBeforeA = finishedTask == tA;

        if (bFinishedBeforeA)
            Assert.Fail();
    }

}

I would introduce an overload: 我会介绍一个重载:

public async Task MethodA(long waitMilliseconds = 0)
{
    await semaphore.WaitAsync();

    await DoSomeStuff();
    await Task.Delay(waitMilliseconds);
    semaphore.Release();
}

Then Call it from unit testing as MethodA(5000); 然后从单元测试中将其称为MethodA(5000);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM