-
Notifications
You must be signed in to change notification settings - Fork 36
Client Testing Strategy
The Buendia client has a suite of both unit and functional tests that should undergo continued refinement and growth. This document describes our testing methodology and where gaps still exist.
Unit tests and functional tests share a common source tree in src/androidTest. There are a number of tools, utilities and base classes that make further test development simpler, including:
- Espresso -- used to run functional tests
- Mockito -- used to mock out objects that aren't under test
- Fakes
-
FakeAppLocationTreeFactory
(for constructing fakeAppLocationTree
objects) FakeAsyncTaskRunner
FakeSyncManager
-
FakeTypedCursor
(populates aTypedCursor
with specified data) FakeEventBus
-
FunctionalTestCase
-- base class for functional tests, providing some helpful functionality: - Greatly increases timeouts for UI and data operations to eliminate some sources of flakiness
- Optionally waits on the user list to load (controlled by
mWaitForUserSync
, default:true
) - Closes all activities when the test ends
- Provides a means to determine the current activity (
getCurrentActivity()
) - Provides a way to take a screenshot during the test if the tests are being run with Spoon (
screenshot(String tag)
) - Provides a means to wait for
ProgressFragments
, aFragment
which tracks and displays loading state, to be in theLOADED
state before continuing (waitForProgressFragment()
) - Provides a means to wait for the initial sync process, which occurs when the client database is unpopulated (
waitForInitialSync()
) - Provides means to "sleep", which otherwise doesn't necessarily work as expected during Espresso tests (
checkViewDisplayedSoon()
,checkViewDisplayedWithin(int ms)
) - Idling resources (classes which can cause Espresso to delay its assertions until some condition is met)
-
WifiStateIdlingResource
- busy until Wifi is turned on -
EventBusIdlingResource
- busy until a particularEventBus
event fires -
ProgressFragmentIdlingResource
- busy until aProgressFragment
is in theLOADED
state -
SyncTestCase
-- aFunctionalTestCase
that clears user data at the beginning of every test and provides functionality for modifying Wifi state -
Matchers
-- custom matchers for selectingObjects
inViewAssertions
For new classes, every nontrivial, non-view (Activity
, Fragment
) class should be checked in with a set of unit tests covering all nontrivial public functions. This helps ensure the system's stability moving forward and maintains a baseline of test coverage.
At time of writing, most existing classes do not have unit tests, but there is some coverage, particularly for controller classes, which are the most user-facing of the non-view classes. The remaining gaps should be filled in over time.
For each activity, major user-facing flows should be granted at least one functional test. These tests are far more time-consuming to run and can be less reliable than unit tests, however, so developers are encouraged to use functional tests primarily in cases where coverage cannot be achieved via unit tests.
Currently, there are some small gaps in the functional tests, but coverage is significantly better than unit test coverage. Tests exist for virtually every major flow, with some exceptions where limitations of Espresso or flakiness have prevented proper test operation. In these cases, TODO's have been left in the code to be addressed later on.
There are some common pitfalls to be aware of when writing new tests for the client app or modifying existing tests.
In Espresso 1.1, IdlingResources
persist until the end of a test suite and cannot be unregistered. Furthermore, IdlingResources
with the same name are considered to be the same, and any duplicate IdlingResources
are ignored. This can cause problems for IdlingResources
based on one-time events (such as EventBusIdlingResource
), as the IdlingResource
remains even after the event fires. This is currently being mitigated by giving each EventBusIdlingResource
a unique name, but it means that there could be dozens of idling resources remaining at the end of a test suite.
The long-term solution to this problem will be upgrading to Espresso 2.0, which allows for IdlingResource
unregistration.
Some app operations, particularly initial sync, can take a very long time given enough data. Even with the current timeouts in FunctionalTestCase
, which are set to 5 minutes, it's possible for an operation to timeout, causing the test to fail. The solution to this problem should ultimately be to fix the performance issue and not the tests, but for now, there is a chance of test flakiness given a sufficient amount of data.
There are many cases within the app that elements change their displayed state after being loaded to match their expected value--for example, when a patient chart is opened, the patient chart appears before any observations, which actually includes the admission and symptoms onset dates. Unless Espresso is instructed to wait for these events, it will fail if the test asserts that these are displayed. This is commonly mitigated in the tests by using the FunctionalTestCase.checkViewDisplayedSoon()
and/or FunctionalTestCase.checkViewDisplayedWithin(int ms)
methods, which repeatedly check if a view is displayed until a specified timeout.
In the past, many of the existing client tests were flaky. Through a concerted effort, this flakiness has been more or less eliminated, but it's possible that some tests may be flaky in rare occasions. Any common flakes or consistent failures likely point to a real issue, however.
About the software
System Overview
Client Application
Server Application
Server Platform
Development practices
GitHub Usage
Java Style
Testing
Releases
For field users and testers
Software Install and Configuration
Upon Receiving Your Gear
Setting Up a Tablet
Setting Up a Server
Setting Up an Access Point
Reference Configuration