Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Robolectric test needs to wait for something on a thread

My class does this:

public void doThing() {
    Doer doer = new Doer();
    Thread thread = new Thread(doer);
    thread.start();
}

The 'Doer' class is an inner class:

private class Doer implements Runnable {
    public void run() {
        Intent myIntent = new Intent(mContext, MyService.class);
        mContext.startService(myIntent);

        ...Some more stuff...
    }

This works fine.

I need to test this using Robolectric. Naturally doThing() returns immediately and I need to give the thread a chance to run before I do

ShadowApplication.getInstance().getNextStartedService()

How can I wait for the thread to run?

I have tried:

Robolectric.flushForegroundThreadScheduler();
Robolectric.flushBackgroundThreadScheduler();

and neither has the desired effect: they both return before my Intent has been sent.

At the moment I have worked around it by putting a sleep into my test:

Thread.sleep(10);

and it does the trick, but it's clearly horrible - it's a race condition waiting to cause me grief.

like image 824
Mark Smith Avatar asked Oct 31 '22 18:10

Mark Smith


1 Answers

I had this problem before and I used a different approach to fix it. I created a shadow object of my Runnable class and invoked run in the shadow constructor. This way, the code will execute straight away making it synchronous.

Using your code as basis, the end result should be something like.

@Implements(Doer.class)
private class ShadowDoer{
    @RealObject
    private Doer doer;

    // Execute after Doer constructor
    public void __constructor__(<Doer construtor arguments>) {
        doer.run();
    }
}

Then annotate your test with @Config(shadows=ShadowDoer.class)

What this does is when you create a new object Doer, the shadow constructor will execute and invoke run directly in the main thread.

I used Robolectric 3.2.

like image 95
Laranjeiro Avatar answered Nov 15 '22 05:11

Laranjeiro