How to use TDD effectively to write android integration tests — Part 1 of 3

Jimmy Sanghani
Canopas
Published in
5 min readDec 9, 2021

--

Photo by wu yi on Unsplash

I’ve been busy coding without integration tests until I realized that not writing the integration tests was the reason I was busy.

This is part 1 of the ongoing series of articles where we keep adding new screens with more complexity.

Today we are going to write a simple TODO app, so simple that it will just have a TODO list but with 100% integration test coverage. As we are going to follow the TDD principle, we will write failing tests first and then actual code!

We will use Jetpack compose for views with MVVM architecture. For the sake of simplicity, we will not use HILT but it can be added anytime and that should not affect the things we are going to learn in this post.

If you haven’t read our previous articles about jetpack compose with dagger/hilt, MVVM, Navcontroller, and MVVM state management in a simple way using jetpack compose, I suggest checking them out.

Let’s get started…

We will divide this post into small chunks to make sure we stay focused.

TODO app — Show a blank screen with “TODOs” title

Open android studio and create a new empty jetpack compose activity project. That will leave you with an app that has a “Hello android” greeting.

Let’s add a jetpack compose function to display an empty screen with “TODOs” title and replace the function in MainActivity to use it.

@Composable
fun TodoList() {

}

MainActivity onCreate

setContent {
TodoTheme {
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colors.background) {
TodoList()
}
}
}

Simple isn’t it? Now before we write code to display the title, let’s first write a test to verify that title is shown and make sure it fails.

Add TodoListTest file in androidTest directory. Make sure you don’t add it in test directory as we are not writing unit tests.

The initial state of the test file should look like this

class TodoListTest {    @Before
fun setUp() {

}

@After
fun tearDown() {}
}

Make sure you have the following dependencies added to your app module’s build.gradle file.

androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version"

To test the screen on an actual emulator, we will need to use createComposeRule() as a rule that will launch the app with the compose view and will close the app once the test is done running.

Add this to the top of the class

@get:Rule
val composeTestRule = createComposeRule()

and then update setUp function to set compose view as compose rule content

public override fun setUp() {
super.setUp()
composeTestRule.setContent { TodoList() }
composeTestRule.waitForIdle()
}

We are done with the basic setup, now it’s time to write the test.

Add the first test

@Test
fun testScreenTitleIsShown() {
composeTestRule.onNodeWithText("TODOs").assertIsDisplayed()
}

Well, that’s it! The test is pretty self-explanatory. We are trying to find a node (View) with the text “TODOs” and verifying that it’s actually displayed on the screen.

You can right-click on the test and select Run 'testScreenTitleIsShown from the context menu, wait for the test to finish, and verify that it fails!

Now let’s update the code to make the test green!

@Composable
fun TodoList() {
Column(modifier = Modifier.padding(16.dp)) {
Text(text = "TODOs", fontSize = 22.sp,
modifier = Modifier.padding(vertical = 8.dp))
}
}

Now run tests again and verify that it passes. This is one iteration of the TDD cycle. Every iteration should add a failing test first and later actual code to make that test pass.

TODO app — Show 3 TODO items

For simplicity, we will add a repository to provide 3 to-do items and use it here instead of fetching todos from API or DB.

Let’s start by adding Task class

data class Task(val title: String)

and a repository interface that ViewModel will use to fetch tasks

interface TaskRepository {
fun getTasks(): List<Task>
}

Let’s create an implementation that will provide 3 static tasks

class TaskRepositoryImpl : TaskRepository {
override fun getTasks(): List<Task> {
return listOf(
Task("Hello TDD"),
Task("This is fun"),
Task("I can not live without TDD")
)
}
}

and TodoViewModel to manage the state for the view

class TodoViewModel(
private val taskRepository: TaskRepository
) : ViewModel() {
val tasks = MutableStateFlow(taskRepository.getTasks())
}

Now let’s update the view and activity to take ViewModel as an argument

val viewModel by viewModels<TodoViewModel>()

setContent {
TodoTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
TodoList(viewModel)
}
}
}
@Composable
fun TodoList(viewModel: TodoViewModel) {
val tasks by viewModel.tasks.collectAsState()
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = "TODOs", fontSize = 22.sp,
modifier = Modifier.padding(vertical = 8.dp)
)
TaskView(tasks = tasks )
}
}

@Composable
fun TaskView(tasks: List<Task>) {

}

It’s time to write a failing test now, remember we need to write it before the actual code.

Add viewModel variable and update setUp function

private val viewModel = TodoViewModel(TaskRepositoryImpl())

@Before
fun setUp() {
composeTestRule.setContent { TodoList(viewModel) }
composeTestRule.waitForIdle()
}

and add the test

@Test
fun testTaskItemsAreShown() {
composeTestRule.onNodeWithText("Hello TDD").assertIsDisplayed()
composeTestRule.onNodeWithText("This is fun").assertIsDisplayed()
composeTestRule.onNodeWithText("I can not live without TDD").assertIsDisplayed()
}

If you run the test, it will fail as expected. Now let’s update the code to show the task list.

@Composable
fun TaskView(tasks: List<Task>) {
tasks.forEach {
Text(text = it.title, fontSize = 16.sp, modifier = Modifier.padding(16.dp))
}
}

That’s it. Run the tests again and verify that they all pass.

Hope you learned something useful!

Thanks for your support!
If you like what you read, be sure to 👏 it below — as a writer it means the world!
Follow Canopas Software to get updates on interesting tech articles!

Happy coding!

--

--