Writing Tests

Now that your setUp() method is written, you are ready to write your tests. A test is a method in your test class annotated with @Test.

Start by writing a test that asserts existing behavior in SoundViewModel: The getTitle() property is connected to the Sound’s getName() property. Write a method that tests this.

Listing 21.10  Testing the title property (SoundViewModelTest.java)

@Before
public void setUp() throws Exception {
    mBeatBox = mock(BeatBox.class);
    mSound = new Sound("assetPath");
    mSubject = new SoundViewModel(mBeatBox);
    mSubject.setSound(mSound);
}

@Test
public void exposesSoundNameAsTitle() {
    assertThat(mSubject.getTitle(), is(mSound.getName()));
}

Two methods will show up red: the assertThat(…) method and the is(…) method. Key in Option+Return (Alt+Enter) on assertThat(…) and select Static import method..., then MatcherAssert.assertThat(…) from hamcrest-core-1.3. Do the same for the is(…) method, selecting Is.is from hamcrest-core-1.3.

This test uses the is(…) Hamcrest matcher with JUnit’s assertThat(…) method. The code reads almost like a sentence: Assert that subject’s get title method is the same value as sound’s get name method. If those two methods returned different values, the test would fail.

To run all your unit tests, right-click app/java/com.bignerdranch.android.beatbox (test) and select Run 'Tests in 'beatbox''. A display will pop up (Figure 21.5).

Figure 21.5  Passing tests

Screenshot shows Beatbox in app window.

By default, the test display only shows failing tests, since those are the only tests that are interesting. So this output means that everything is A-OK – your tests ran, and they passed.

Testing object interactions

Now for the real work: building out the integration between SoundViewModel and your new BeatBox.play(Sound) method. A common way to go about this is to write a test that shows what you expect a new method to do before you have written the method. You are going to write a new method on SoundViewModel called onButtonClicked() that calls BeatBox.play(Sound). Write a test method that calls onButtonClicked().

Listing 21.11  Writing test for onButtonClicked() (SoundViewModelTest.java)

    @Test
    public void exposesSoundNameAsTitle() {
        assertThat(mSubject.getTitle(), is(mSound.getName()));
    }

    @Test
    public void callsBeatBoxPlayOnButtonClicked() {
        mSubject.onButtonClicked();
    }
}

That method does not exist yet, so it shows up in red. Put your cursor over it and key in Option+Return (Alt+Enter). Then select Create method 'onButtonClicked' and the method will be created for you.

Listing 21.12  Creating onButtonClicked() (SoundViewModel.java)

    public void setSound(Sound sound) {
        mSound = sound;
        notifyChange();
    }

    public void onButtonClicked() {

    }
}

For now, leave it empty and key in Command+Shift+T (Ctrl+Shift+T) to return to SoundViewModelTest.

Your test calls the method, but it should also verify that the method does what you say it does: calls BeatBox.play(Sound). Mockito can help you do this odd-sounding job. All Mockito mock objects keep track of which of their methods have been called as well as what parameters were passed in for each call. Mockito’s verify(Object) method can then check to see whether those methods were called the way you expected them to be called.

Call verify(Object) to ensure that onButtonClicked() calls BeatBox.play(Sound) with the Sound object you hooked up to your SoundViewModel.

Listing 21.13  Verifying that BeatBox.play(Sound) is called (SoundViewModelTest.java)

        assertThat(mSubject.getTitle(), is(mSound.getName()));
    }

    @Test
    public void callsBeatBoxPlayOnButtonClicked() {
        mSubject.onButtonClicked();

        verify(mBeatBox).play(mSound);
    }
}

The verify(Object) uses a fluent interface, much like the AlertDialog.Builder class you used earlier in this book. It is an abbreviation for the following code:

verify(mBeatBox);
mBeatBox.play(mSound);

Calling verify(mBeatBox) says, I am about to verify that a method was called on mBeatBox. The next method call after that is then interpreted as, Verify that this method was called like this. So your call to verify(…) here means, Verify that the play(…) method was called on mBeatBox with mSound as a parameter.

No such thing has happened, of course. SoundViewModel.onButtonClicked() is empty, so mBeatBox.play(Sound) has not been called. This means that your test should fail. Because you are writing the test first, that is a good thing – if your test does not fail at first, it must not be testing anything.

Run your test to see it fail. You can follow the same steps from earlier, or key in Command+R (Ctrl+R) to repeat the last Run command you performed. The result is shown in Figure 21.6.

Figure 21.6  Failing test output

Screenshot shows Beatbox in app window.

The output says that your test expected a call to mBeatBox.play(Sound) but did not receive it:

Wanted but not invoked:
beatBox.play(
    com.bignerdranch.android.beatbox.Sound@64cd705f
);
-> at ….callsBeatBoxPlayOnButtonClicked(SoundViewModelTest.java:28)
Actually, there were zero interactions with this mock.

Under the hood, verify(Object) made an assertion, just like assertThat(…) did. When that assertion failed, it caused the test to fail and logged out this output describing what went wrong.

Now to fix your test. Implement onButtonClicked() to do what the test expects.

Listing 21.14  Implementing onButtonClicked() (SoundViewModel.java)

    public void setSound(Sound sound) {
        mSound = sound;
        notifyChange();
    }

    public void onButtonClicked() {
        mBeatBox.play(mSound);
    }
}

Rerun your test. This time you should see green, indicating that all your tests passed (Figure 21.7).

Figure 21.7  All green, all good

Screenshot shows Beatbox in app window.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.221.154.151