Salam!

I'm

Saad

Today,

We'll be

talking

about

Unit

Testing

on

Android

Contents

  • Testing on Android
    • Instrumented Test
    • Local Test
  • Local Test Class
  • Instrumented Test Class
  • Assertions
  • Gradle Configs
  • Testing Mnemonic
  • Naming Conventions & Best Practices
  • TDD: Dos & Donts
  • LiveData, ViewModel & AndroidX Test in Action
  • Testing Aspects
  • The Testing Pyramid
  • Test Doubles
  • Mockito In Action

Writing Tests: Why and When

Source Sets

In Android Studio, if you open any Android project, you'll find these 3 default Source Sets :

Instrumented Test

  1. Runs on real or emulated Android devices
  2. Hence, reflect what will happen in the real world
  3. Hence, slower

Local Test

  1. Runs locally on your development machine's JVM - emulator or physical device not required
  2. Hence, they act less like they would in the real world
  3. Hence, faster

Instrumented VS Local

Instrumented Local
real/emulated devices local JVM
reflects real world doesn't reflect real world
slower faster

Let's Start Digging!

  • A test class is just a normal class
  • In the class, each function is a single test
  • Each test is annotated with @Test
  • Tests contain assertion statements

Let's Keep Digging!

  • An assertion is the core of your test. It's a code statement that checks that your code or app behaved as expected
  • @Test is a JUnit annotation, assertions are also from JUnit
  • JUnit is a simple framework to write repeatable tests. It is an instance of the xUnit architecture for unit testing frameworks.

Things to note!

  • A single failed assertion fails the entire test
  • You are directed to the line of the failed assertion directly from console's Run tab so no worries

Let's test locally, first

  • Right click on any method (say, MyMethod) and select Generate > Test
  • Change the Class name: to MyMethodTest (instead of MyMethodKtTest)
  • Keep the rest of the defaults. JUnit 4 is the appropriate testing library
  • The destination package is correct (it mirrors the location of the StatisticsUtils class) and you don't need to check any of the check boxes

Let's test locally (contd.)

  • You'll be making a local test because your function is doing math calculations and won't include any Android specific code
  • So, there's no need to run it on a real or emulated device
  • Select the test directory (NOT androidTest) because you'll be writing local tests

Generate a Test Class

Let's right click on that method & hit Generate > Test

Choose test, not androidTest! Remember why?

Naming Conventions & Best Practices!

  • How do you think you should name a test method?
  • What should I EXACTLY do in my method?
  • How to do the ACTUAL testing?
  • Is there a all-time-go-to format?
  • Or, even better - a mnemonic or something?!

Tip 01: Naming Conventions

subjectUnderTest _actionOrInput _resultState
  • Subject under test is the method or class that is being tested (maybe, testItems).
  • Next is the action or input (maybe, _itemsNotNull).
  • Finally you have the expected result (maybe, _returnsCount).

Tip 02: A Testing Mnemonic

"Given, When, Then" (GWT)
  • Given: a list
  • When: the method
  • Then: an expected output
"Arrange, Act, Assert" (AAA)

Let's run our Test Class

All Tests are Passed!

But what if this error comes up?

Do this. Thank me later.

TDD: The True Essense

  • Test Driven Development (TDD) is a school of programming thought that says instead of writing your feature code first, you write your tests first.
  • ONLY then you write your feature code with the goal of passing your tests.
  • TDD follows these steps:
    • Write the GIVEN-WHEN-THEN test
    • Confirm the test fails
    • Write the minimal code to get the test to pass
    • Repeat for all tests!

TDD in Action

Tip 03: Dr Jekyll & Mr Hide

  • Be honest, Doctor & Kill Mr Hyde!
  • The test that always passes without you writing any code to make it pass/fail is PROBABLY a USELESS test
  • Wear the hat of a QA, not just a DEV
  • A helpful method for me is actually take that function as a competitive programming small problem only with numbers, then try to use that skill to generate the edge test cases

Don't be this guy, please!

Also, not this guy too!

Writing tests can be tough. True.

But there's always a QA

... to ruin every feature we ship ...

A bit on Gradle Configs

  • implementation—The dependency is available in all source sets, including the test source sets.
  • testImplementation—The dependency is only available in the test source set.
  • androidTestImplementation—The dependency is only available in the androidTest source set.

LiveData, ViewModel & AndroidX Test

  • Open the ViewModel class you wish to test, right-click on the class name, then click Generate -> Test
  • On the Create Test screen, click OK to accept (no need to change any of the default settings)
  • On the Choose Destination Directory dialog, choose the test directory

LiveData, ViewModel & AndroidX Test

But wait, is it a local or an instrumented test?

The clue is in the last slide, and in your memory

Okay but why is it Local though?

  • Pure view model tests usually go in the test source set because the view model code shouldn't rely on the Android framework or OS
  • As a local test, it will also run faster because you run the tests on your local machine and not on an emulator or device.

What if I need to test a constructor with an Application Context?

  • You need AndroidX Test libraries
  • AndroidX Test library includes the method ApplicationProvider.getApplicationContext (Click here to know more)
  • The AndroidX Test libraries include classes and methods that provide you with versions of components like Applications and Activities that are meant for tests

LiveData, ViewModel & AndroidX Test (Contd.)

  • We need to do two things to test LiveData:
    • Use InstantTaskExecutorRule
    • Ensure LiveData observation
  • InstantTaskExecutorRule is a JUnit Rule
  • This rule runs all Architecture Components-related background jobs in the same thread so that the test results happen synchronously, and in a repeatable order

Testing Aspects

  1. Scope
  2. Speed
  3. Fidelity

Automated Tests

  1. Unit tests: Testing single methods in view models and repositories.
  2. Integration tests: Testing all the functionality of a single fragment and view model pair.
  3. End to end tests (E2E): Starting up the entire app and testing a few features together.

The Testing Pyramid

Test Doubles

  • A test double replaces the real version of a class in tests.
  • It's similar to how a stunt double is an actor who specializes in stunts, and replaces the real actor for dangerous actions.

Types of Test Doubles

  1. Fake
  2. Mock
  3. Stub
  4. Dummy
  5. Spy

Mockito

  • Mockito is a mocking framework
  • It's a framework for making test doubles. It can also make stubs and spies.
  • Readable & clean verification errors
  • There's a very popular library with helper functions in Kotlin: Mockito-Kotlin

Mockito in Action

  1. Mock any class using @Mock annotation
  2. Use @Before to setup/initialize the dependency/viewmodel before performing any test
  3. Use verify() method mto check if a method of a mock object has been called or not

I'm tired already! What's next?

Happy New year! Let's welcome 2021

That's

All

for

Today