Testing Presenters

Chuck Greb
Android Testing
Published in
2 min readSep 4, 2017

In the last post we discussed the single responsibility principle when implementing Model-View-Presenter (MVP) on Android, and specifically the benefits of naming the interface the presenter uses when interacting with the Activity the Controller.

Using the Controller interface when communicating from the presenter to the Activity also has the advantage of making the presenter more testable.

To illustrate this point let’s revisit the simple finance calculator example we created when exploring the topic of separated presentation.

MainController.kt

interface MainController {
fun setResultText(text: String)
fun setResultColor(color: Color)
}

MainActivity.kt

class MainActivity : AppCompatActivity(), MainController {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
val presenter = MainPresenter(this)
add_button.setOnClickListener {
presenter.onAddButtonClick(
input_text_1.text.toString(),
input_text_2.text.toString())
}
}
override fun setResultText(text: String) {
result_text.text = text
}
override fun setResultColor(color: Color) {
val colorInt = if (color == Color.GREEN)
getColor(R.color.green) else getColor(R.color.red)
result_text.setTextColor(colorInt)
}
}

MainPresenter.kt

class MainPresenter(private val controller: MainController) {
fun onAddButtonClick(input_1: String, input_2: String) {
val calculator = Calculator()
val result = calculator.add(
input_1.toInt(),
input_2.toInt())
controller.setResultText(result.toString())
val category = calculator.getResultCategory(result)
val color = if (category == Category.HIGH)
Color.GREEN else Color.RED
controller
.setResultColor(color)
}
}

Since all our Android framework dependencies are encapsulated in the view layer, the presenter can be tested using pure JUnit.

When writing the tests we create a fake controller called TestMainController that implements the Controller interface. This is the same interface implemented by the Activity in the production code.

TestMainController.kt

class TestMainController : MainController {
var text: String? = null
var color
: Color? = null
override fun setResultText(text: String) {
this.text = text
}
override fun setResultColor(color: Color) {
this.color = color
}
}

There is no need to create test double for the domain model Calculator since it is also written in pure Java.

Calculator.kt

class Calculator {
fun add(addend1: Int, addend2: Int): Int {
return addend1 + addend2
}
fun getResultCategory(result: Int): Category {
return if (result < 0) Category.LOW else
Category.HIGH
}
}

Now writing presenter tests is trivial since we can just verify the values set on the TestController.

MainPresenterTest.kt

class MainPresenterTest {
private val controller = TestMainController()
private val presenter = MainPresenter(controller)
@Test
fun onAddButtonClick_shouldSetResultTextPositive() {
presenter.onAddButtonClick("3", "4")
assertThat(controller.text).isEqualTo("7")
}
@Test
fun onAddButtonClick_shouldSetResultTextNegative() {
presenter.onAddButtonClick("2", "-3")
assertThat(controller.text).isEqualTo("-1")
}
@Test
fun onAddButtonClick_shouldSetResultColorPositive() {
presenter.onAddButtonClick("3", "4")
assertThat(controller.color).isEqualTo(Color.GREEN)
}
@Test
fun onAddButtonClick_shouldSetResultColorNegative() {
presenter.onAddButtonClick("2", "-3")
assertThat(controller.color).isEqualTo(Color.RED)
}
}

Now we have clean and concise tests that run wicked fast to verify our presentation logic. As an added bonus we don’t have to worry about the complexity of a mocking framework.

Happy testing!

This post is part of a series on clean architecture that explores how classic software design principles can be applied to modern Android development.

If you found this article helpful, please give it some applause 👏 to help others find it. Feel free to leave a comment below.

--

--

Chuck Greb
Android Testing

Mission-driven engineering leader. Community organizer. Digital minimalist.