Integration testing verifies the correctness of interaction between mobile application components — modules, services, databases, and external APIs. Unlike unit tests that isolate each component, integration tests detect errors at the junctions: data format incompatibility, parameter transmission failures, and incorrect processing of server responses. According to Martin Fowler, 2018, integration tests cover up to 40% of critical defects missed by unit checks and provide confidence in system stability before release.
Key Takeaways
Integration testing is a software verification stage that evaluates the correctness of interaction between individual modules or subsystems of an application. While unit tests check each component in isolation, integration tests bring these components together and verify how they work as a unit. Typical scenarios include data transfer between the network layer and repository, writing to a database via ORM, and processing responses from third-party APIs.
In the context of mobile development, integration tests cover interactions between the UI layer, business logic, and data sources. For example, a test may verify that after clicking the “Login” button, the application sends a request to the server, receives a token, and saves it to local storage. Such verification confirms that the component chain works without failures.
According to the World Quality Report 2023, companies that regularly apply integration testing reduce the number of production incidents by 35% compared to projects relying only on unit tests. This makes integration checks a mandatory element of the quality assurance strategy in commercial development.
Mobile applications consist of many interconnected components: network requests, local databases, push notifications, system services, and third-party SDKs. Each of these components is developed separately, but at runtime they exchange data in real time. Integration testing detects defects that cannot be found through isolated module verification.
Typical problems discovered by integration tests include data type mismatches between the API and the application model, JSON serialization errors, incorrect handling of network timeouts, and failures during concurrent database access via Room or Core Data. Without integration checks, such defects reach production and only manifest with real users.
Research from the Google Testing Blog (2021) shows that the cost of fixing a defect found during integration testing is 5 times lower than after release. This is because at early stages the developer has full context of the error and can fix it without an urgent hotfix cycle. Investing time in writing integration tests pays off through reduced maintenance costs and increased user trust.
There are three main approaches to organizing integration tests: Big Bang, Bottom-Up, and Top-Down. The choice of strategy depends on project size, application architecture, and component availability at the time of writing tests. Each approach has its advantages and limitations that are important to consider when planning test coverage.
Big Bang — an approach where all system components are connected simultaneously, after which a general test run is executed. This method is simple to implement: no need to write stubs or emulate individual modules. However, when an error is detected, it is difficult to determine which component caused it. Big Bang is justified in small projects with simple architecture where the number of modules does not exceed five.
Bottom-Up — a strategy where integration testing starts with low-level components: database, network layer, system services. After verifying each level, tests gradually connect higher-level modules — repositories, Use Case classes, and ViewModels. The main advantage is early detection of defects in fundamental application layers, reducing the risk of cascading errors in later stages of development.
Top-Down — an approach where testing starts with top-level components — UI screens and navigation, while lower-level modules are simulated using stubs or mocks. This allows checking user scenarios before the server side or database is fully implemented. Top-Down is especially useful during parallel development of client and server parts when the backend is not yet ready for real integration.
For integration testing of mobile applications, a range of specialized tools is used, divided into three categories: server emulation libraries, database frameworks, and system service verification tools. The choice of a specific tool depends on the platform — Android or iOS — and the project’s technology stack.
Let’s look at practical examples of integration tests for Android and iOS. For the Android platform, we use MockWebServer together with JUnit, for iOS — XCTest with the OHHTTPStubs library. Both examples verify the scenario of receiving data from an API and saving it in a local repository.
This test verifies that a Retrofit request to the emulated server returns correct JSON, and the repository converts the response into a domain model. MockWebServer intercepts the request and returns the specified JSON, after which the test compares the expected result with the actual one.
class UserRepositoryTest {
private val mockServer = MockWebServer()
@Before
fun setup() {
mockServer.start()
}
@Test
fun fetchUser_returnsCorrectData() {
val json = "{ \`"id\`": 1, \`"name\`": \`"Alice\`" }"
mockServer.enqueue(MockResponse()
.setBody(json)
.setResponseCode(200))
val repository = UserRepository(
createRetrofit(mockServer.url("/").toString()))
val user = repository.fetchUser(1)
assertEquals(1, user.id)
assertEquals("Alice", user.name)
}
@After
fun tearDown() {
mockServer.shutdown()
}
}
For iOS, a similar test uses OHHTTPStubs to intercept URL requests. The library replaces the server response at the system framework level of URL Loading System, allowing testing of any networking library — URLSession, Alamofire, or Moya.
import XCTest
import OHHTTPStubs
import OHHTTPStubsSwift
class UserRepositoryTests: XCTestCase {
func testFetchUser_returnsCorrectData() {
stub(condition: isPath("/users/1")) { _ in
return HTTPStubsResponse(
jsonObject: ["id": 1, "name": "Alice"],
statusCode: 200,
headers: nil
)
}
let repository = UserRepository()
let expectation = expectation(description: "fetch user")
repository.fetchUser(id: 1) { user in
XCTAssertEqual(user.id, 1)
XCTAssertEqual(user.name, "Alice")
expectation.fulfill()
}
waitForExpectations(timeout: 2.0)
}
}
Effective integration testing requires following a set of practices that increase test stability and reduce maintenance costs. Isolate external dependencies: use in-memory databases instead of production instances and emulate third-party APIs using stub libraries. This eliminates non-deterministic failures caused by network availability or external service state.
Maintain test independence: each integration test should work in isolation, without depending on results of other tests. Use @Before and @After annotations in JUnit or setUp and tearDown in XCTest to prepare and clean up the test environment. This prevents mutual test influence and simplifies error diagnosis.
Cover edge cases: integration tests should verify not only successful scenarios (happy path) but also error handling — timeouts, HTTP 4xx and 5xx codes, empty responses, malformed JSON. According to the Google Testing Blog (2022), 60% of production incidents are related to incorrect handling of edge cases that were not covered by tests.
Frequently Asked Questions
Unit tests check a single class or function in isolation, replacing dependencies with stubs. Integration tests verify the interaction of multiple real components — for example, a network connection and a database simultaneously.
Running integration tests usually takes from 2 to 15 minutes depending on the number of tests and environment complexity. For large projects, it is recommended to split tests into parallel jobs in a CI system to reduce the total verification time before merging.
First of all, integration tests are written for the network layer, database, and system services — notifications, camera, geolocation. API requests to the backend and local storage operations provide the highest ROI since these components most often become sources of regressions.
For a single screen, unit tests for ViewModel and UI tests are sufficient. Integration tests for a single screen are justified only if the screen interacts with multiple data sources — for example, combines responses from two different APIs or writes data simultaneously to the network and local database.
Integration tests should be run on every pull request in the CI pipeline and before major releases. It is also recommended to run the full set of integration tests at night (nightly build) to detect defects related to changes in dependencies or the test environment.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also