Test-Driven Development Tutorial for Android: Getting Started

Learn the basics of test-driven development, or TDD, and discover how to use TDD effectively when developing your Android apps! By Victoria Gonda.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 4 of 4 of this article. Click here to view the first page.

Writing a Test to Fix a Bug

TDD is not only used for adding new features, but also for fixing bugs and code refactoring. Before you go to fix a bug, you should write a test for what the behavior should be after you’ve fixed it. Then, you can be sure that you fixed it and that it won’t come back again in the future.

If you’ve been playing around with the app or looking closely at the code, you may have spotted a bug. Whenever you change the title, it changes the count to zero, and it doesn’t correct itself until the next time the count increments.

Gif of changing title resetting count

This is the bug you’re going to fix here. Start by writing a test in the MainActivityTest to make sure the count is not updated when the title changes.

@Test
fun editingTitleDoesntChangeCount() {
  // 1
  onView(withId(R.id.fab))
     .perform(click())
  // 2
  onView(withId(R.id.textVictoryTitle))
     .perform(click())
  val newTitle = "Made the bed"
  onView(instanceOf(EditText::class.java))
     .perform(clearText())
     .perform(typeText(newTitle))
  onView(withText(R.string.dialog_ok))
     .perform(click())

  // 3
  onView(allOf(withId(R.id.textVictoryCount), withText("0")))
     .check(doesNotExist())
}

Here, you are:

  1. Performing a click to the increment victories fab to make sure the count is non-zero to start.
  2. Updating the title.
  3. Making sure that the count did not reset to zero.

Running the Failing Test

Now run the test and watch it fail:

Test results with newly written bug test failing

Making the Test Pass

Take a look at the setVictoryTitle() method in VictoryViewModel. There’s one extra line that is the cause of this bug. Maybe a previous developer who was unsure about what the behavior should be left it there. He or she should have written a test!

Remove this line from the setVictoryTitle() method:

viewState.setValue(VictoryUiModel.CountUpdated(0))

Run the test again, and this time it should be green.

Test results with newly written bug test passing

Run the app again. You’ll see that you fixed the bug. Yay! You also know this bug won’t show up again in the future when you forget, er, some other developer forgets what this method should do.

Writing a Test Before Refactoring

Many times in your career as an Android developer, you’ll inherit code that does not have existing tests but that needs some refactoring.

While not TDD in the “Red-Green-Refactor” sense, it’s still great to write tests before you refactor existing code, especially if that bit of code does not have tests yet. You’re going to refactor the implementation of the initialize() method in the VictoryViewModel.

Take a look at the current method:

fun initialize() {
  viewState.value = VictoryUiModel.TitleUpdated(repository.getVictoryTitle())
  viewState.value = VictoryUiModel.CountUpdated(repository.getVictoryCount())
}

You know that it should pass both the victory title and the victory count along to the viewState LiveData instance. Right now, it is making two calls to the repository to do this, but there is also a repository method, getVictoryTitleAndCount(), that will return both.

For the sake of this exercise, you will only write the tests for verifying that the view updates. You can add tests for the repository call on your own, if you like.

Add these two tests to the VictoryViewModelTest; they are similar to the other tests you’ve added:

@Test
fun initializeReturnsTitle() {
   val title = "New title"
   val count = 5
   stubVictoryRepositoryGetVictoryTitleAndCount(Pair(title, count))

   viewModel.initialize()

   verify(viewStateObserver).onChanged(VictoryUiModel.TitleUpdated(title))
}

@Test
fun initializeReturnsCount() {
   val title = "New title"
   val count = 5
   stubVictoryRepositoryGetVictoryTitleAndCount(Pair(title, count))

   viewModel.initialize()

   verify(viewStateObserver).onChanged(VictoryUiModel.CountUpdated(count))
}

Running the Tests

When you run these tests, make sure they pass. This time, you’re verifying existing behavior to make sure that it doesn’t change with your refactor.

Passing initialize test

Refactoring Your Code

Since there is a repository method to get both the count and title, you should use that one instead. Replace the contents of the initialize() method in VictoryViewModel with the following code:

val (title, count) = repository.getVictoryTitleAndCount()
viewState.value = VictoryUiModel.TitleUpdated(title)
viewState.value = VictoryUiModel.CountUpdated(count)

Here, you’re using the repository method to get both the title and the count, then setting the values returned for the view to display.

Run the tests in VictoryViewModelTest and see that they all still pass.

Passing initialize test

Where to Go From Here

You can download the finished version of this project using the Download materials button at the top or bottom of this tutorial.

You now know what it means to practice TDD in Android app development, and you can start using TDD on your own. That’s no small victory, but a pretty large one!

Through including the tests you write with TDD from the start, you can have more confidence in your code throughout the lifetime of your app, especially when you add new features and release new versions of the app.

Resources

As you continue to practice the TDD process, here are some more resources to help you along the way:

For even more TDD learning, check out our book, Android Test-Driven Development by Tutorials.

Challenge

Why not get started practicing TDD on your own right away? As a challenge, try implementing the “Reset” functionality found in the overflow menu. You can find the completed challenge in the same download from the Download materials button found at the top or bottom of this tutorial. Remember to write your tests first and run them first to see them fail (even if you’re feeling lazy)!

You’ve learned a lot in this tutorial, and we hope you enjoyed it! If you have any questions or comments, feel free to join the discussion forum below. :]