Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unit test methods posted by Event Bus in android?

I am using Otto's event bus in my application. In one of my classes I am posting the event.

MyEvent myevent = new MyEvent();
uiBus.post(myEvent);

I am able to test the post method.

Now there is another class which is receiving the event.

//ReceiverClass.java
@Subscribe
public void onEventReceived(MyEvent myevent) {
    callAMethod();
}

How do I unit test that this method was invoked. I tried with the following test code

@Mock
Bus uiBus;

@Test
public void testBusReceviedEvent() {
    ReceiverClass instance = new ReceiverClass();
    mockBus.register(instance);
    MyEvent myevent = new MyEvent();
    mockBus.post(myEvent);
    //Test
    verify(instance, times(1)).callAMethod();
}

But this code doesn't work.

like image 637
thedarkpassenger Avatar asked May 13 '16 05:05

thedarkpassenger


2 Answers

It does not work because instance is not a mock. You will have to verify the effects of callAMethod or put that method in another class and inject a mock of this new class into your ReceiverClass class.

For example...

private class ReceiverClass {
    private MyNewClass theNewClassIWasTalkingAbout;

    // Stick in a setter for that ^

    @Subscribe
    public void onEventReceived(MyEvent myevent) {
        theNewClassIWasTalkingAbout.callAMethod();
    }
}

Then your test will have to change slightly...

@Mock
private MyNewClass mockNewClass;

@InjectMocks // This will be the "solid" implementation of the thing you are trying to test, it is not a mock...
private ReceiverClass instance;

@Test
public void testBusReceivedEvent() {
    mockBus.register(instance);
    MyEvent myevent = new MyEvent();

    mockBus.post(myevent);

    verify(mockNewClass, times(1)).callAMethod();
}

Hope this helps.

like image 180
BretC Avatar answered Sep 30 '22 17:09

BretC


I'm a little late to the party but here is an example of a class which works and accounts for async calls. Instead of Mocking EventBus we simply let it do it's thing and register it in the TestDriver class below.

The thing that makes this work is the CountDownLatch which, with the help of the abstract DataTransferCallback class, waits for latch.countDown() to be called or 5 seconds to go by.

Just register your test class and in the @Subscribe method, pass it back to the method that created the DataTransferCallback and do your assertions there.

@RunWith(AndroidJUnit4.class)
public class TestDriver {

    private final CountDownLatch latch = new CountDownLatch(1);
    private EventBus eventBus;

    private DataTransferCallback transferCallback;

    public abstract class DataTransferCallback {
        abstract void onSuccess(DataTransfer event);
    }

    @Before
    public void setUp() {
        EventBus.getDefault().register(this);
        eventBus = spy(EventBus.getDefault());
    }


    @SuppressWarnings("unchecked")
    @Test
    public void test200Resposne() throws InterruptedException {
    // Get known good JSON
    final String json = TestJSON.get200Response();
    // Class under test
    final Driver driver = new Driver(InstrumentationRegistry.getTargetContext());
    final JsonParser jsonParser = new JsonParser();
    //boolean to hold our test result
    final boolean[] testPassed = new boolean[]{false};

    transferCallback = new DataTransferCallback() {

        @Override
        public void onSuccess(DataTransfer event) {
            assertNotNull(event);
            verify(eventBus).post(event);
            assertThat(event.getStatus(), is("OK"));
            assertTrue(event.getData() != null);
            testPassed[0] = true;
        }
    };

    //Set our test EventBus object
    driver.setEventBus(eventBus);
    // The actual method under test
    driver.parseData(jsonParser.parse(json));

    // Set a countdown latch to wait for the result (5s)
    latch.await(5000, TimeUnit.MILLISECONDS);
    // will wait here until 5s or the @Subscrube method is hit
    assertTrue(testPassed[0]);
}

//Because we want to examine EventBus Output, register it 
//to this class and pass the event back through our custom abstract class
@Subscribe
public void onReceiveEventBusEvent(DataTransfer event) {
    assertNotNull(transferCallback);
    transferCallback.onSuccess(event);
    //notify latch so that we can proceed
    latch.countDown();
    }
}
like image 27
tricknology Avatar answered Sep 30 '22 15:09

tricknology