1

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.

5
  • One approach is to use mocks selectively, to test situations that you cannot readily test using the real code under test. For example, you have a something() method on a Whatever class that could return a Frobozz instance that contains no Swot objects, but you cannot readily create a Whatever that will do this (as it only occurs in the rain on leap days). So, you create a mock Whatever that has something() return this specific result for a test, and you test Whatever normally otherwise. Commented Nov 27, 2017 at 18:29
  • I feel stupid and I don't understand. When I use mock objects I get 0 % code coverage. Commented Nov 27, 2017 at 18:31
  • You get 0% coverage of the thing being mocked. You create the mock to test other code that needs an object in a certain state, and the mock is the simplest way to get such an object. Or, you create the mock to record the results of other code calling methods on that mock. Your objective with the mock is to help you write tests for other code and improve coverage of that other code. Commented Nov 27, 2017 at 18:37
  • Another reason for mocks, in Android, is to be able to run unit tests (which run on the JVM) instead of instrumentation tests (which run in Android), as in a unit test, there is no Context or TextView to use. So, you mock those, so your own test code (and production code) can run on the JVM, while interacting with the mocks. You aren't trying for test coverage of Context, as you did not write Context. You are using a mock Context to test other code that happens to use a Context. Commented Nov 27, 2017 at 18:38
  • The more I know the less I know. I still don't understand how to get test coverage when I use mock objects. Commented Nov 27, 2017 at 18:39

1 Answer 1

1

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
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.