Introduction
- Now that I have officially released my first app, which can be found HERE on the Google Play store. I want to add more features to my app, however, I think the next best step is to add some tests. So, this series is going to be a practical series dedicated to understanding testing in the Android framework. All the information I found for this blog post can be found HERE
Why write tests?
- If you are still developing a small application like me, you might think that tests are a waste of time. However, as our apps grow manual testing just can't keep up. Tests give us rapid feed back on failures and failures spotted earlier on are far easier to fix then ones that arise in production. With a good suite of testing we are free to refactor and optimize code to our hearts delight. Also, to create a stable and mature code base tests are mandatory
Types of testing
- When testing in Android our tests will fall into one of 3 categories:
1) Unit tests
2) Integrated tests
3) End-to-end tests
- When writing tests the Google testing team recommends the
Pyramid Principle
. Which means 70% of our tests should beunit
, 20% should beintegrated
and 10%end-to-end
. Of course these numbers do not have to be exact.
Unit tests
- A
unit test
should be highly focused and fast, they are tests that exhaustively validate functionality and contracts of each class within our app. As we add and change methods within a particular class, we create and run unit tests against them. Unit tests are recommended to be run aslocal unit tests
. Which means the tests are going to run on our local machine. So no emulator or physical device required. The down side to these types of tests is since we are not running them in a realistic environment the tests havelow fidelity
. Meaning there is still a small chance the code could fail in production. This is because we are usually creating a lot of mocks and fakes for System or third party APIs when using unit tests. do not worry, what one type of test lacks the others make up for.
Integrated Tests
- In addition to testing each unit of our app by running unit tests, we also validate our app's behaviour from the module level. To do so we write
integration tests
that validate the collaboration and interaction of a group of units. Integrated tests are consideredInstrumented tests
which are meant to be run on a physical device. These tests are less focused so a failure in one might take longer to track down the reason for failing. Since we are using a physical device, less mocks and fakes these tests are consideredhigher fidelity
than unit tests. Integrated tests also have slower execution speed . Since our app is assembling multiple components, building them, packaging and shipping them to a device where the tests are run. This obviously is going to take extra time. - Some examples of when to use integration testing are:
1) Interactions between a view and a view model, like testing a Fragment object, validating layout XML, or evaluating the data-binding logic of a View Model object.
2) Tests in the app's repository layer, which verify that your different data sources and data access objects(DAO, Room database stuff) interact as expected.
3) Testing interactions on a particular screen
4) Multi fragment tests that evaluates a specific area of our app. For this type of test we require a real device because the interactions under test involves multiple UI elements.
End-to-end Tests
- While it is important to tests each class of our app in isolation, it is just as important to validate end-to-end workflows that guide users through multiple modules and features. These types of tests form unavoidable bottlenecks in your code but they offer us the closes validation that our app is production ready. End-to-end tests are also considered
Instrumented
tests, so they will take a longer time to run and also be run on a physical device. It is also recommended that for each end-to-end test we write, we should also write integration tests to verify that each module in the end-to-end tests works as expected.
Where to write tests?
- A typical project in Android Studio contains two directories for writing tests:
1) androidTest : This directory should contain the test that run on a real or virtual device (Instrumented tests)
1) test : This directory should contain the test that run on your local machine (local unit tests).
Test Driven Development (TTD)
- With test driven development the idea is that you start by writing your tests, then implement the code to make those tests pass. Finally, when the tests pass we can put that code into production. TTD forces us to think about the design of our application up front, it makes us think about APIs and the structure of your code. With TTD we are actually write less code because we only write the code to make tests pass. Which makes us create more reliable code more frequently.
Google recommends the testing mantra of
red, green, refactor
. which means we start off with a failing test, we implement the code to make it pass and then refactor the code.That is it for this blog post, next post we will dive deeper and actually write some unit tests
Conclusion
- Thank you for taking the time out of your day to read this blog post of mine. If you have any questions or concerns please comment below or reach out to me on Twitter .
Top comments (3)
Great article! About integration tests, when do you consider that is ok to use roboelectric instead of running the tests in a real/emulated device?
Thank you for reading my article :) For more information about testing in Android I would recommend this codelab created by Google: developer.android.com/codelabs/adv...
Thanks, I've completed that codelab before, it's a great source! However, I am currently looking for advanced or more complex cases, I don't know if you could recommend me another source. Also, I've realized that you wrote another article, I will read it later! :)