Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing a nonblocking method (asynchronous testing)

I have a very simple Checkclass which has a blocking waitForCondition() method. This method is blocking. I want to create some unit tests for this method. First, the method should return when the condition is met. Second, the method should return when it is interrupted.

Internally the Check class has an ArrayBlockingQueue and calls its take() method, so my test is really about having coded the logic for the condition correctly (as it should be). In the application, data for the Check class is fed by another thread via an InputData method. The InputData method executes the logic on incoming data and places a dummy object in the ArrayBlockingQueue when the condition is met. This should cause waitForCondition() to return.

So my first thought is I can test the InputData by mocking and check to see the dummy object is added to the queue when the condition is met. This would require changing the design of the class, since the queue is a private data member (unless it is possible to mock private data). Instead of InputData adding directly to the queue when the condition is met, it would have to call something which could be mocked.

But then there is the problem of checking the waitForCondition() methods themselves, given that InputData is functioning correctly. It's really simply code:

try {
        myArrayBlockingQueue.take();
        return true;
    } catch (InterruptedException ex) {
        return false;
    }

So I'm wondering if it's worth the imagined trouble: a test which creates another thread with a Check, calls its waitForCondition(), then returns something when it's done. Perhaps, using an Executor service. The fuzzy part is how to synchronize the assertTrue(...). I found this article on asynchronous testing which looks like it might do the trick.

Summary of question:

  1. Should I change the design to test the logic in InputData() and if so, how?
  2. Should I leave out the test of waitForCondition() as long as InputData() is tested?
  3. Or is it better to just do what needs to be done (a somewhat complicated unit test) and test waitForCondition() directly?
like image 728
Pete Avatar asked Oct 08 '22 06:10

Pete


2 Answers

If you inject the instance of ArrayBlockingQueue in the constructor of the Check class, then your test can inject the appropriate value in the middle of the test.

Then you can run the unit test with a timeout, and fail if it doesn't return within 100ms or so.

like image 74
Sean Reilly Avatar answered Oct 13 '22 05:10

Sean Reilly


Thanks for the nice link! I faced some similar problems, and maybe that link is a better way to go than what I did. (I'm also interested to see what other answers appear to this question - it's a good question)

If you don't mind changing (at least temporarily) your actual code (yes, this is not a usual unit-test practice!) you can do something I called "Error Injection".

In my implementation, you create a class that reads from properties (or a Map) to "do something funny" at a specific unique point. e.g. your properties might say

myClass.myMethod.blockingQueueTake = interrupt:
myClass.myLongCalculation = throw: java.lang.ArithmeticException(Failed to converge)

In your code, you add testing lines, e.g. right before your queue.take(), add

TestSimulator.doCommand("myClass.myMethod.blockingQueueTake");

The advantage is that everything is happening in real-real code, not some Mock, which can get really hairy. (In my case, the SW was older, not written/designed for unit-testing, so making a Mock was very difficult) The disadvantage is that you will probably want to remove or comment out the code afterwards. So it really isn't a continuous-integration type unit test, it's more of a one-time really serious reality debug. So, I admit, it's far far from ideal, but, it did find a bunch of bugs for me!

like image 23
user949300 Avatar answered Oct 13 '22 05:10

user949300