Niklas Rosencrantz
Niklas Rosencrantz

Reputation: 26661

Testing with Mockito for Android

Can you please explain how I can get test coverage? I'm looking for help because Mockito is totally counter-intuitive. If I use only mock objects, then how could I ever get test coverage? I don't understand.

Here are my tests.

package dev.game.adventure;


import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.CoreMatchers.*;
import static org.mockito.Mockito.*;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.widget.TextView;

import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class AdventureTest {

    private static final String FAKE_STRING = "HELLO WORLD";

    @Mock
    Simulation engine;

    @Mock
    AdventureWorld mWorld;

    @Mock
    Adventure mworld;

    @Mock
    Context aContext;

    @Mock
    FullscreenActivity mActivity;

    @Mock
    Drawable mDrawable;

    @Mock
    Resources mResources;

    @Mock
    Place mPlace;

    @Mock
    AdventureGame ag;


    @Test
    @Ignore
    public void Tedye() {
        //when(mWorld.defaultPlace()).thenReturn(mPlace);
        // myObjectUnderTest.wakeMeAfter(new WalkingPerson(myObjectUnderTest, mWorld, "new", 2, mActivity), 10);
        //String result2 = myObjectUnderTest.getHelloWorldString();
        //assertThat(result2, is(FAKE_STRING));}  createPlace("Heaven", target, R.mipmap.dungeon2);
        //    Adventure a = new Adventure(textview, mactivity, ag);

    }


    @Test
    @Ignore
    public void testd() {
//           Textview scrollable = ''''''''''''(R.id.textView1);

        when(mWorld.defaultPlace()).thenReturn(mPlace);
        Context mCont;

    }


    @Test
    @Ignore
    public void adventureWorld() {
        // Simulation myObjecUnderTest = new Simulation();
        Adventure a = new Adventure(new TextView(aContext), mActivity, ag);
        Player p = a.getPlayer();
        p.say("foobar", mActivity);
        p.say("Hello my name is " + a.getPlayer().getMe().name, mActivity);

    }
    //@Ignore
    @Test
    public void adventureTest() {
        Simulation myObjectUnderTest = new Simulation();
        when(mWorld.defaultPlace()).thenReturn(mPlace);
        myObjectUnderTest.wakeMeAfter(new WalkingPerson(myObjectUnderTest, mWorld, "new", 2, mActivity), 10);
        String result2 = myObjectUnderTest.getHelloWorldString();
        assertThat(result2, is(FAKE_STRING));
    }
    //@Ignore
    @Test
    public void personTest() {
        Simulation myObjectUnderTest = new Simulation();
        when(mResources.getDrawable(R.mipmap.dungeon)).thenReturn(mDrawable);
        when(mActivity.getResources()).thenReturn(mResources);
        when(mActivity.getResources().getDrawable(R.mipmap.dungeon)).thenReturn(mDrawable);
        when(mWorld.defaultPlace()).thenReturn(mPlace);
        WalkingPerson myObjectUnderTest2 = new WalkingPerson(myObjectUnderTest, mWorld, "blaha", 2, mActivity);
        String result2 = myObjectUnderTest2.getHelloWorldString();
        myObjectUnderTest2.getThings();
        myObjectUnderTest2.getWorld();
        assertThat(result2, is(FAKE_STRING));
    }
    //@Ignore
    @Test
    public void trollTest() {
        Simulation myObjectUnderTest = new Simulation();
        AdventureWorld ag;
        when(mResources.getDrawable(R.mipmap.dungeon)).thenReturn(mDrawable);
        when(mActivity.getResources()).thenReturn(mResources);
        when(mActivity.getResources().getDrawable(R.mipmap.dungeon)).thenReturn(mDrawable);
        when(mWorld.defaultPlace()).thenReturn(mPlace);
        WalkingPerson myObjectUnderTest2 = new Troll(myObjectUnderTest, mWorld, "Loki", mActivity);
        String result2 = myObjectUnderTest2.getHelloWorldString();
        myObjectUnderTest2.getThings();
        AdventureWorld adv = (AdventureWorld) myObjectUnderTest2.getWorld();
        //assertThat(adv.defaultPlace().toString().equals(mWorld.defaultPlace().toString()));
        // assertThat(adv.defaultPlace(), is(FAKE_STRING));
        assertThat(myObjectUnderTest2.getName(), is("Loki"));
        //assertThat(adv.messsage, is(FAKE_STRING));
    }
    //@Ignore
    @Test
    public void cokeTest() {
        when(mWorld.getPlace("Dungeon")).thenReturn(mPlace);
        mWorld.getPlace("Dungeon").addThing(new CocaCola("Ljummen cola"));
        Simulation myObjectUnderTest = new Simulation();
        when(mResources.getDrawable(R.mipmap.dungeon)).thenReturn(mDrawable);
        when(mActivity.getResources()).thenReturn(mResources);
        when(mActivity.getResources().getDrawable(R.mipmap.dungeon)).thenReturn(mDrawable);
        when(mWorld.defaultPlace()).thenReturn(mPlace);
        WalkingPerson myObjectUnderTest2 = new Troll(myObjectUnderTest, mWorld, "blaha", mActivity);
        String result2 = myObjectUnderTest2.getHelloWorldString();
        myObjectUnderTest2.getThings();
        myObjectUnderTest2.getWorld();
        assertThat(result2, is(FAKE_STRING));
    }
    @Ignore
    @Test
    public void testPlace() {
        Simulation myObjectUnderTest = new Simulation();
        when(mResources.getDrawable(R.mipmap.dungeon)).thenReturn(mDrawable);
        mWorld.createPlace("Heaven", mActivity, R.mipmap.dungeon2);
        mWorld.createPlace("Hell", mActivity, R.mipmap.dungeon2);
        mWorld.connect("Heaven", "Hell", "Down", "Up");
        mWorld.randomPlace();
        assertTrue(false);

    }
    @Ignore
    @Test
    public void useAppContext() throws Exception {
        // Context of the app under test.
      //  Context appContext = InstrumentationRegistry.getTargetContext();
       // assertEquals("dev.game.adventure", appContext.getPackageName());
    }
}

The repository is available online.

Upvotes: 1

Views: 1207

Answers (1)

Nick DeFazio
Nick DeFazio

Reputation: 2432

I took a shot at writing a quick example of how a mock can be applied to help with unit tests. Hopefully this can help clear things up.

In this example, I have two primary classes named SampleClass and Validator, with the idea being that SampleClass relies on an instance of Validator to help perform its actions.

SampleClass:

//This class's only function is to return one string if the username it's given is valid, and another string if its not valid. It does this with the help of the Validator class

public class SampleClass {

    private final Validator validator;

    public SampleClass(Validator v){
        this.validator = v;
    }

    public String createWelcomeMessage(final String username){

        if(this.validator.isValid(username)){
            return "Welcome, " + username + "!";
        }

        return username + " is not a valid username.";

    }

}

Validator:

//This class's only job is to validate strings. Right now, all it does is check the length, and fail if its > 15 characters.
public class Validator {

    public boolean isValid(final String str) {

        if(str.length() > 15){
            return false;
        }

        return true;
    }

}

So let's say I want to write some unit tests for SampleClass. Unit tests should typically be testing small, focused units of code, and I don't want the specific details of Validator to bleed into these tests. I really just care about how the Validator class's output affects SampleClass, and the functionality that SampleClass implements. This is where mocking(and Mockito) comes in, as I can mock the interactions with Validator to test the behavior of SampleClass in isolation.

SampleClassTest:

public class SampleClassTest {

    //Mock the validator class, which SampleClass depends on.
    private Validator validator = Mockito.mock(Validator.class);

    //Do not mock the SampleClass class. Create a new one, and use the mocked validator within it.
    private SampleClass sampleClass = new SampleClass(validator);


    //Test that a valid username will print out the message "Welcome, username!"
    @Test
    public void testValidUsername(){

        Mockito.when(validator.isValid(Mockito.anyString())).thenReturn(true);
        final String message = this.sampleClass.createWelcomeMessage("testuser");

        Assert.assertEquals("Welcome, testuser!", message);

    }


    //Test that an invalid username will print out the message "username is not a valid username"
    @Test
    public void testInvalidUsername(){

        Mockito.when(validator.isValid(Mockito.anyString())).thenReturn(false);

        final String message = this.sampleClass.createWelcomeMessage("thisiswaywaywaytoolong");
        Assert.assertEquals("thisiswaywaywaytoolong is not a valid username.", message);
    }

}

Using jacoco to look at unit test coverage, SampleClass is fully covered, and Validator is completely uncovered because right now it's mocked and untested. I should write additional unit tests focused on Validator(unmocked!) in isolation.

enter image description here

Applying it to your project:

For an example, consider the go method on your Person class. It seems like you could write some unit tests for this method and use Mocks for the FullscreenActivity and World objects, and verify that the method correctly manipulates the World object under various conditions. That would cover Person->go with some unit tests, and do so in a way where the Person tests are isolated from your other classes. This can also be very useful when dealing with Android widgets and such (as CommonsWare mentioned), since you can just mock it, instead of dealing with a complex or otherwise impractical class in your tests.

TL;DR

  • Use mocks to mock dependencies on other classes/objects that are not the focus of a particular unit test. Especially complex or otherwise impractical classes (CommonsWare really explained it well in the comments)
  • You shouldn't only use mocks, but instead, you should use mocks to help you write unit tests for unmocked classes/objects

Upvotes: 1

Related Questions