Glide is a widely used Android library for efficient image loading and caching, optimized for smooth scrolling. Ensuring high-quality unit tests is essential for maintaining reliability—especially as test logic grows in complexity. This guide presents five practical strategies to reduce cyclomatic complexity in Glide’s test suite, leading to more maintainable and robust code.
Why High Cyclomatic Complexity Hurts Glide Tests
Cyclomatic complexity measures the number of linearly independent paths through a piece of code. In Glide’s test modules—such as those under library/test/src/test/java/com/bumptech/glide—excessive branching (e.g., nested conditionals for image format, size, or EXIF handling) leads to:
- Increased maintenance burden: Tests with complexity scores above 10 become hard to modify safely.
- Incomplete coverage: Complex logic may miss edge cases like center-crop transformations on rectangular images (e.g., test assets such as
ccrt_centercrop_withrectanglelargerthanimage_returnsupscaledrectangularimage_16_armeabi_v7a.png). - Risk during refactoring: Core components like disk caching or bitmap decoding depend on clear, simple test contracts.
Five Effective Techniques to Simplify Test Logic
1. Use a Test Data Factory
Avoid inline resource loading by centralizing test image creation:
public class TestImageProvider {
public static InputStream loadRotatedJpeg() {
return getClass().getClassLoader()
.getResourceAsStream("test_images/issue387_rotated_jpeg.jpg");
}
public static Stream<Arguments> allSampleImages() {
return Stream.of(
Arguments.of("pixel3a_exif_rotated.jpg"),
Arguments.of("small_gainmap_image.jpg")
);
}
}
This pattern reduces boilerplate and isolates data setup from test assertions.
2. Leverage Parameterized Tests
Replace multiple similar test methods with a single parameterized one using JUnit 5:
@ParameterizedTest
@MethodSource("com.example.TestImageProvider#allSampleImages")
void decodesImageCorrectly(String imageName) {
InputStream input = TestImageProvider.loadByName(imageName);
Bitmap result = decodeBitmap(input);
assertThat(result).isNotNull();
}
This minimizes conditional logic and ensures consistent validation across diverse inputs.
3. Prefer Behavior Verification Over State Inspection
When testing caching behavior, verify interactions rather than inspecting file system state:
// Good: verify interaction
verify(diskCache).store(eq(cacheKey), any(Bitmap.class));
// Avoid: fragile state checks
assertTrue(new File(cacheDir, cacheKey.toString()).exists());
This approach makes tests less brittle and more focused on intended behavior.
4. Isolate Dependencies with Test Doubles
For network-dependent tests, replace real HTTP calls with fakes:
FakeHttpUrlConnection fakeConnection = new FakeHttpUrlConnection();
fakeConnection.setResponseData(loadTestImageBytes("sample.jpg"));
GlideApp.with(context)
.load(fakeConnection.getURL())
.into(target);
Such fakes—managed in dedicated mock packages—elmiinate error-handling branches from test logic.
5. Encapsulate Common Assertions
Bundle repetitive checks into custom assertion helpers:
public static void assertBitmapSize(Bitmap bitmap, int width, int height) {
assertThat(bitmap.getWidth()).isEqualTo(width);
assertThat(bitmap.getHeight()).isEqualTo(height);
assertThat(bitmap.getConfig()).isEqualTo(Bitmap.Config.ARGB_8888);
}
These utilities, placed in shared test utility modules, keep individual tests concise and readable.
Enforce Complexity Limits via Static Analysis
Integrate cyclomatic complexity checks into the build pipeline using Checkstyle:
<module name="CyclomaticComplexity">
<property name="max" value="8"/>
<property name="tokens" value="LITERAL_IF,LITERAL_FOR,LITERAL_WHILE,LITERAL_CASE"/>
</module>
Run these checks during CI (e.g., via scripts/run_instrumentation_tests.sh) to prevent regressions in test quality.
Applying these practices to Glide’s test codebase—starting with modules like com.bumptech.glide.load—can significantly reduce redundancy, improve coverage, and accelerate test development. Clean, low-complexity tests act as reliable safety nets, enabling confident evolution of the image-loading pipeline.