JUnit is a standard framework for unit testing in the Java and Kotlin ecosystem, used in Android development to verify business logic at the level of isolated components. The framework provides a set of annotations, assertion methods, and a Test Runner for automatic discovery and execution of tests. According to JUnit.org, the library remains the most popular solution for unit testing in the JVM ecosystem: more than 70% of Java projects use JUnit in any version.
Key Takeaways
JUnit is an open-source framework for writing and running repeatable unit tests in Java and Kotlin. It is part of the xUnit ecosystem — a family of frameworks based on the architecture by Kent Beck and Erich Gamma — and is the standard testing tool in Android Studio and IntelliJ IDEA.
The main task of JUnit is to isolate a small piece of code (method, class) and verify its behavior under controlled conditions. Tests are written as regular Java/Kotlin classes with annotations, and the framework handles test discovery, lifecycle management, and execution statistics collection.
The first version of JUnit appeared in 1997 and radically changed the development approach by popularizing Test-Driven Development (TDD) practices. Today, JUnit 5 (Jupiter) is the current version, completely redesigned with a modular architecture supporting Java 8+ and extensions.
The mechanism of JUnit execution is based on the Test Runner pattern, which scans the classpath, finds methods with the @Test annotation, creates a test class instance, and runs methods in a specific order. The Test Runner manages the lifecycle: BeforeAll → BeforeEach → Test → AfterEach → AfterAll.
JUnit wraps each test method in a separate class instance, ensuring isolation between tests. This means that class fields do not retain state between different @Test methods — each test starts with a clean object.
The simplest JUnit test is a method with the @Test annotation that calls the code under test and verifies the result using an assertion. If the assertion fails, the test is considered failed, and JUnit reports the error.
@Test
void additionShouldReturnCorrectSum() {
var calculator = new Calculator();
var result = calculator.add(2, 3);
assertEquals(5, result);
}
The Test Runner automatically finds such a method, runs it, and reports the result. If the method does not throw an exception — the test passes (green). If an assertion fails — the test fails (red).
Annotations in JUnit define when and how test code should execute. In JUnit 5, annotations are located in the org.junit.jupiter.api package and cover all stages: data preparation, test execution, resource cleanup.
| Annotation | Purpose | Executes |
|---|---|---|
| @Test | Marks a test method | Once per call |
| @BeforeEach | Setup before each test | Before each @Test |
| @BeforeAll | One-time class initialization | Once before all tests |
| @AfterEach | Cleanup after each test | After each @Test |
| @AfterAll | One-time class teardown | Once after all tests |
| @DisplayName | Human-readable test name | Decoration |
Consider a complete test class with a proper lifecycle. The setUp method creates a fresh Calculator instance before each test, and tearDown releases resources — for example, closing file descriptors or database connections.
class CalculatorTest {
private Calculator calculator;
@BeforeEach
void setUp() {
calculator = new Calculator();
}
@Test
void subtractionShouldReturnCorrectResult() {
int result = calculator.subtract(10, 4);
assertEquals(6, result);
}
@AfterEach
void tearDown() {
calculator.reset();
}
}
Assertions are static methods that compare the actual result with the expected one and throw an exception if they do not match. JUnit 5 provides the org.junit.jupiter.api.Assertions class with over 25 methods for various verification scenarios — from simple value comparison to timeout checks and grouped assertions.
Special attention deserves the assertThrows method, which allows verifying that code correctly handles error situations — division by zero, null passing, limit exceeding.
@Test
void divisionByZeroShouldThrowException() {
Calculator calc = new Calculator();
ArithmeticException exception = assertThrows(
ArithmeticException.class,
() -> calc.divide(10, 0)
);
assertEquals("Cannot divide by zero", exception.getMessage());
}
Parameterized tests allow running the same test method with different sets of arguments. In JUnit 5, this is done using the @ParameterizedTest annotation combined with a data source — @ValueSource, @CsvSource, @MethodSource, or @EnumSource.
This approach radically reduces code duplication: instead of ten identical tests for different values, you write one parameterized method. According to the Google Testing Blog, parameterization reduces test code volume by 40–60% without losing coverage.
The @CsvSource annotation passes multiple comma-separated rows to the test. Each row corresponds to one test run. JUnit automatically converts string values to the required types: int, long, String, and others.
@ParameterizedTest
@CsvSource({
"1, 1, 2",
"2, 3, 5",
"10, 20, 30",
"-1, 1, 0"
})
void additionWithMultipleInputs(int a, int b, int expected) {
assertEquals(expected, a + b);
}
When input data is more complex than simple numbers or strings, @MethodSource is used. It references a static method that returns a Stream of arguments — for example, a Stream of Arguments or a Stream of User objects for testing the DAO layer.
In Android development, JUnit is used alongside AndroidX Test, which provides extensions for testing Activity, Content Provider, and other Android components. JUnit handles unit tests executed on the JVM without an emulator, while AndroidX Test handles instrumented tests on the device.
To add JUnit 5 to an Android project, simply include the org.junit.jupiter:junit-jupiter dependency in the module-level build.gradle. The AGP Gradle plugin supports running JUnit 5 tests on the JVM through the standard Test Runner.
// build.gradle.kts
android {
testOptions {
unitTests.isIncludeAndroidResources = true
}
}
dependencies {
testImplementation("org.junit.jupiter:junit-jupiter:5.11.0")
testImplementation("androidx.test:core-ktx:1.6.1")
}
In practice, JUnit is most often used for testing ViewModel and repositories — layers that do not require an Android context or emulator. Such a test runs in milliseconds and can be executed hundreds of times without wasting time.
JUnit 5 (Jupiter) is not just a new version, but a completely redesigned platform divided into three modules: JUnit Platform (test execution on JVM), JUnit Jupiter (API for writing tests), and JUnit Vintage (backward compatibility with JUnit 4). This modularity allows connecting different Test Engines — for example, Spek for Kotlin or TestNG.
| Feature | JUnit 4 | JUnit 5 |
|---|---|---|
| Package | org.junit | org.junit.jupiter |
| Base Annotation | @Test (from junit.framework) | @Test (from org.junit.jupiter.api) |
| Before/After | @Before, @After, @BeforeClass | @BeforeEach, @AfterEach, @BeforeAll |
| Parameterized | @RunWith(Parameterized.class) | @ParameterizedTest + @ValueSource |
| Extension | @Rule, @ClassRule | @ExtendWith, more flexible API |
| Java min | Java 5 | Java 8+ |
Migrating from JUnit 4 to JUnit 5 does not require rewriting all tests — just add JUnit Vintage Engine and old tests will continue to work. New tests are recommended to be written with JUnit 5 to take advantage of extensions, lambda assertions, and built-in parameterization support.
Frequently Asked Questions
JUnit is a framework for writing and running tests, while Mockito is a library for creating mock objects. They are used together: JUnit manages test execution, and Mockito replaces dependencies of the class under test.
Yes, JUnit 5 is fully compatible with Android projects. For unit tests, simply add the junit-jupiter dependency to build.gradle. Instrumented tests still work through the AndroidX Test Runner.
At minimum, a single @Test annotation before the method is sufficient. @BeforeEach and @AfterEach are recommended for initialization and cleanup, but they are not required.
The Test Runner is a JUnit component that scans the classpath, finds methods with the @Test annotation, creates test class instances, and runs the tests. In JUnit 5, this role is performed by the JUnit Platform with connected Test Engines.
Use assertThrows(Class, Executable) — it takes the expected exception type and a lambda with the method call. JUnit verifies that the exception was actually thrown and returns it for further inspection.
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