Unit Testing and UI Testing
# CHAPTER 26
Unit Testing and UI Testing
1. Introduction
Imagine building a 50-screen e-commerce application. You change a single line of code in the "Cart" logic. How do you know you didn't accidentally break the "Checkout" screen? Do you manually click through the entire app every time you save a file? No. Professional engineers rely on Automated Testing. You write code that tests your code. If you make a mistake, your tests will instantly fail and warn you *before* you ship the bug to millions of users. In this chapter, we will master Unit Testing (testing isolated logic) using JUnit and Mockito, and UI Testing (testing button clicks and screens) using Espresso.2. Learning Objectives
By the end of this chapter, you will be able to:-
Differentiate between the
testdirectory (Unit Tests) andandroidTestdirectory (UI Tests).
- Write fundamental Unit Tests using the JUnit framework.
- Understand the concept of Mocking utilizing the Mockito library.
- Automate user interactions (clicks, typing) utilizing the Espresso UI framework.
- Execute test suites natively within Android Studio.
3. The Testing Pyramid
Automated testing is traditionally visualized as a pyramid:- Unit Tests (Base): Fast, isolated tests targeting single functions. You should have hundreds of these. They run locally on your laptop's JVM (Java Virtual Machine), not on a phone emulator, so they execute in milliseconds.
- Integration Tests (Middle): Testing how two components work together (e.g., Does the Repository correctly parse the Retrofit JSON?).
- UI / End-to-End Tests (Top): Slow, heavy tests that actually boot up an Android Emulator, launch the app, click buttons, and verify the screen output. You should have fewer of these.
4. Directory Structure
Open your Android Studio Project pane. Insidesrc, you will see three folders:
-
1.
main: Your actual app code.
-
2.
test: Local Unit Tests (Runs on your computer's CPU).
-
3.
androidTest: Instrumented UI Tests (Requires an Android Emulator or physical device).
5. Step 1: Writing a Unit Test (JUnit)
Let's test a simple helper function. Imagine a class that validates passwords. *(Create this insidesrc/main/java/com/example/myapp/PasswordValidator.kt)*
Now, let's write the test! Right-click PasswordValidator, select Generate -> Test, and place it in the test directory.
*(Inside src/test/java/.../PasswordValidatorTest.kt)*
*Run the test:* Click the green "Play" button next to the class name. It will execute in 100 milliseconds and show a beautiful green checkmark!
6. Mocking (Mockito)
Unit tests must be *isolated*. If you are testing aViewModel, you don't want it to actually connect to the real internet or the real Room database!
We use Mockito to create "Fake" (Mock) versions of our Repositories.
*(Mockito configuration is complex and often requires additional Coroutine Test dispatchers for MVVM).*
7. Step 2: Writing a UI Test (Espresso)
UI Tests live in theandroidTest folder. They boot the app and simulate a human finger clicking the screen.
Espresso relies on three core actions: ViewMatchers (Find the button), ViewActions (Click the button), and ViewAssertions (Check the screen).
*(Inside src/androidTest/java/.../MainActivityTest.kt)*
*Run the test:* It will launch the Android Emulator, the app will open, you will see a "Ghost" type the text and click the button automatically, and the test will pass!
8. Common Mistakes
-
Putting UI Tests in the
testfolder: If you accidentally put an Espresso test in the localtestfolder, it will crash with aRuntimeException: Method not mocked. The local JVM does not have an Android UI!
- Flaky UI Tests: UI tests are notoriously "flaky" (sometimes they pass, sometimes they fail). This happens if a network animation takes 2 seconds, but Espresso checks the screen after 1 second. You must use specialized Espresso "Idling Resources" to tell the test to wait for the animation to finish.
9. Best Practices
- Test-Driven Development (TDD): A popular engineering philosophy where you write the Test *before* you write the actual code. The test fails initially. Then, you write the minimal code required to make the test pass. This ensures 100% test coverage.
10. Exercises
-
1.
Create a
MathHelper.ktfile with anadd(a: Int, b: Int)andmultiply(a: Int, b: Int)function.
-
2.
Generate a JUnit test class in the
testdirectory. Write three tests asserting that the math logic is perfectly accurate.
11. Coding Challenges
Challenge: Build a basic UI with aTextView showing "0" and a Button saying "Increment". Write an Espresso UI test that launches the Activity, clicks the button 5 times in a row, and asserts that the TextView text exactly matches "5".
12. MCQ Quiz with Answers
What is the fundamental operational difference between tests residing in the src/test/ directory versus the src/androidTest/ directory?
Within the Espresso UI Testing framework, what is the sequence of the core testing API required to simulate a user typing into an input field?
13. Interview Questions
- Q: Explain the structural concept of "Mocking" utilizing frameworks like Mockito. Why is it architecturally dangerous to allow a Unit Test to execute live Retrofit network calls?
- Q: Detail the anatomy of a JUnit Unit Test. Explain the operational flow of the "Given, When, Then" (Arrange, Act, Assert) testing paradigm.
- Q: Contrast the execution speed and maintenance overhead of Unit Tests versus Instrumented End-to-End (UI) Tests. Why does the "Testing Pyramid" recommend a vast foundation of Unit Tests and a minimal capstone of UI Tests?