Unit Tests

Unit Tests

“The Testing Pyramid,” unit tests verify how isolated parts of your application work. Before checking how things work together, you need to make sure the units of your application behave as expected.

Article content

you will Learn what unit tests are and what are the best places to use them.

• Write unit tests using the test-driven development (TDD) pattern to learn these concepts in the context of TDD.

When to use unit tests

Unit tests are the fastest and easiest tests to write. They also are the quickest to run. When you want to ensure that a class or method is working as intended in isolation — this means with no other dependent classes — you write unit tests.

Before writing any feature code, you should first write a unit test for one of the classes that will compose your feature. Afterwards, you write the class that will pass the test. After repeating this procedure, you’ll have a completed, testable feature

Creating unit tests

Article content
assertEquals verifies that the previous execution modified the game.currentScore property to be equal to one. It’s important to understand that the first parameter is the expected value, and the second parameter is the actual value
Article content
create the Game class

Running the test

Article content
Notice how the expected value is one and the actual value is zero. If we had reversed the order of our expected and actual values in our assertion, this would show up incorrectly

the test will be failed.

Making the test pass

Article content


Now run the test again and see that it passes.

Article content
test is pass

Creating more tests

The game will show a highest score. So, you should add a test that checks that when the current score is above the highest score, it increments the highest score:

@Test
fun whenIncrementingScore_aboveHighScore_shouldAlsoIncrementHighScore() {
 val game = Game()
 game.incrementScore()
 Assert.assertEquals(1, game.highestScore)
}        

Again if you try to compile it’ll fail because the highestScore property is missing. So, add the following property to the Game class:

var highestScore = 0
 private set        

Now the test will compile, so run it and watch it fail.

Article content

To make it pass, open the Game class and modify the incrementScore() method as follows:

Article content

Run the test and you’ll see that it passes

Article content

However, you should also test that, when the highest score is greater than the current score, incrementing the current score won’t also increment the highest score, so add the following test:

Article content

Here, the intention is to create a Game with a highscore of 10. The test won’t compile because you need to modify the constructor to allow a parameter. Because you need to start with a highest score greater than the default, which is 0, you need to alter the constructor like this:

Article content

Now, run all the tests and see that the last one doesn’t pass. You can use the green arrow button on the left-side of the class definition.

Article content

The last one doesn’t pass because you’re incrementing both the current score and highest score regardless of their values. Fix that by replacing the incrementScore() function with the following:

Article content

Build and run the last test to see the satisfying green checkmark.

Now I will add questions to make trivia game:

so you’ll now create unit tests that model a question with two possible answers. The question also has an “answered” option to model what the user has answered to the question

@Test
 fun whenCreatingQuestion_shouldNotHaveAnsweredOption() {
 val question = Question("CORRECT", "INCORRECT")
 Assert.assertNull(question.answeredOption)
 }        

Here, you used assertNull to check if question.answeredOption is null. If you try to run this test it won’t compile because the Question class doesn’t exist.

Create the Question class:

class Question(val correctOption: String,
 val incorrectOption: String) {
 var answeredOption: String? = null
 private set
}        

Run the test again and watch that it now passes.

Now, you can add another test:

 @Test
 fun whenAnswering_shouldHaveAnsweredOption() {
 val question = Question("CORRECT", "INCORRECT")
 question.answer("INCORRECT")
 Assert.assertEquals("INCORRECT", question.answeredOption)
 }        

This test will check that, when you add the user’s answer to a question, the user’s answer is saved in the answeredOption property. You’ll get a compilation error since you haven’t written the answer() method yet.

Add the following to the Question class to make it compile:

fun answer(option: String) { // No implementation yet }        

Now run the test and you’ll see that it doesn’t pass.

fun answer(option: String) {
 answeredOption = option
 }        

Because you’ll need to know if the question was answered correctly, imagine that the answer() method now returns a Boolean. The result would be true when the user answered correctly. Now, add this test.

 @Test
 fun whenAnswering_withCorrectOption_shouldReturnTrue() {
 val question = Question("CORRECT", "INCORRECT")
 val result = question.answer("CORRECT")
 Assert.assertTrue(result)
 }        

Notice, here, that you’re using assertTrue. It checks for a Boolean result. Running this test will get you a compilation error since the answer() method doesn’t return a Boolean. Update the Question class so that the answer() method returns a Boolean.For now, always return false

fun answer(option: String): Boolean {
 answeredOption = option
 return false
 }        

Run it and watch it fail.

Fix it temporarily by always returning true:

 fun answer(option: String): Boolean {
 answeredOption = option
 return true
 }        

Run it and watch it pass.

Add the following test:

 @Test fun whenAnswering_withIncorrectOption_shouldReturnFalse() {
 val question = Question("CORRECT", "INCORRECT") 
val result = question.answer("INCORRECT") 
Assert.assertFalse(result) 
}        

Run it and see that it fails.

Now that we have tests for when the answer is correct and when the answer is not correct, we can fix the code:

 fun answer(option: String): Boolean {
 answeredOption = option
 return correctOption == answeredOption
 }        

Run all the Question tests and verify they all pass correctly.


Finally, you should ensure that the answer() method only allows valid options. Add this test

@Test(expected = IllegalArgumentException::class)
 fun whenAnswering_withInvalidOption_shouldThrowException() {
 val question = Question("CORRECT", "INCORRECT")
 question.answer("INVALID")
 }
Notice, here, that the @Test annotation allows to expect an exception. If that exception occurs, the test will pass. This will save you from writing try/catch. If you run the test now, it will fail because the answer() method doesn’t throw the exception.        

To fix this, modify the Question class as follows:

Article content

Run the test and watch that it now passes.

Refactoring the unit tests:

*)QuestionClass

Article content

*)QuestionClassUnitTests

Article content



Mohamed Osama

Senior Android Developer at Awqaf

1y

عااااااااش يا هندسة

Mohamed Nabil

Android Developer @Confidential - NDA | Software Engineer | Junior Android Developer Kotlin & Java

1y

عااش جدا فعلا مجهود جبار ❤️

Omar Refaat

Senior Customer Service Engineer

1y

Good job 👏

Mahdi Abd El-Mageed

Mobile Developer ( Native Android & Flutter )

1y

تسلم ايدك مجهود محترم ❤️

youssef badway

Senior Android Engineer @Clicks Egypt

1y

باقي ال unit test class مش عارف مش راضي يضيفه في ال article ليه بس عموما هو هنا

  • No alternative text description for this image
Like
Reply

To view or add a comment, sign in

More articles by youssef badway

  • Network security configuration

    The Network Security Configuration in Android is used to customize the behavior of your app's network stack. It allows…

    1 Comment
  • Writing Swift-Friendly Kotlin Multiplatform APIs — Part 1

    *This list contains some important notes and solutions for common issues in KMP projects So I will sum up these…

  • Unit tests in TDD part 1

    Unit tests Unit tests are the quickest, easiest to write and cheapest to run. They generally test one outcome of one…

    8 Comments
  • Practicing Red-Green-Refactor

    In this article, you will learn the basics of the TDD process while walking through the Red-Green-Refactor steps…

    2 Comments
  • Compose and the View system can work together side by side.

    Introduction Compose and the View system can work together side by side. By the end of this article, you'll be able to…

    4 Comments
  • Understand Recomposition

    jetpack Compose is the newest thing in town. It is still in Alpha, but it shows promising signs.

    4 Comments
  • Dependency Injection pattern

    Most of the classes have some dependency that is needed for the proper functioning of the class. In the general case…

    5 Comments

Insights from the community

Others also viewed

Explore topics