How Huolala Built a Scalable Unit Testing Platform with Tekton, SonarQube, and GitLab
This article describes Huolala's journey from a legacy PHP codebase to a Java‑centric microservice architecture, the common misconceptions and difficulties of unit testing, and the design of an automated CI/CD pipeline that integrates Tekton, SonarQube, GitLab Runner, JUnit5, and Mockito to enforce coverage gates and improve code quality.
Background
As Huolala's technology team expanded and migrated its stack from PHP to Java, core business logic grew more complex and code changed frequently, increasing the demand for engineering standards. Because the logistics platform runs 24/7, bugs can cause huge financial loss and brand damage, especially in services that calculate fees and coordinate drivers.
Misconceptions and Challenges of Unit Testing
Why is unit testing often called the "lice on a bald head" yet also the "elephant in the room"? Adding a single test case feels like a burden, but writing thousands of tests and meeting 80% coverage standards seems equally daunting.
Although unit testing has been advocated for years, best practices remain hard to adopt. Common pitfalls include the belief that 100% coverage is required, treating tests only as bug detectors rather than correctness validators, and assuming tests never need to be updated.
Common Misconceptions
Full‑coverage myth: Not every line needs a test; focus on core services.
Goal misunderstanding: Tests verify correctness, especially after refactoring.
One‑time effort fallacy: Tests must evolve with the code.
Key Difficulties
Code coupling: Highly coupled code is hard to test.
Time pressure: Teams view testing as a time sink.
Cost concerns: Maintaining tests can increase development cost.
To unlock the value of unit testing, teams must clarify its purpose, choose appropriate scenarios, and address these challenges.
Unit‑Testing Capability Thinking and Construction
Java provides mature testing frameworks. Using Tekton, we automated CI/CD where unit tests form the first quality gate. SonarQube monitors coverage, and GitLab Merge Requests enforce that every change passes the test suite.
System Overview
Technology Selection
We preferred open‑source solutions and reused existing infrastructure.
Tekton: Cloud‑native pipeline tool used to run unit‑test jobs and collect coverage.
GitLab Runner: Triggers Tekton tasks and evaluates gate results.
SonarQube: Performs static analysis, tracks coverage, and provides a gateway gate.
Test framework: JUnit5 was chosen for its parameterized tests and extensions such as JSON handling.
Mock framework: Mockito offers elegant syntax for mocking container‑type classes and asserting null returns.
Unit‑Test Process
Initial Step
Developers create a feature branch for a new feature or bug fix.
Code Development
All work happens on the feature branch.
After coding, developers compile locally and run unit tests to ensure no obvious defects.
Code Review
Developers push the branch and open a Merge Request (MR).
The MR triggers the unit‑test pipeline; if tests fail, developers fix the code and resubmit.
Merge
Only when all tests pass is the code merged into the release branch, then eventually into the master branch.
Huolala‑Specific Features
Three‑Layer Architecture
Platform Highlights
Pipeline Management: One pipeline runs unit tests and aggregates coverage; another triggers tests and evaluates gate compliance.
Configuration Management: Supports coverage thresholds, pre‑execution scripts, and exclude directories.
Coverage Management: Summarizes reports, tracks new coverage, and updates metrics automatically.
Quality Gate: All unit tests must pass and new‑line coverage must reach 80%.
Platform Implementation
GitLab Pipeline Configuration
filter-job:
only:
- merge_requests
tags:
- qaci
stage: test
image: harbor.xxx.cn/basic/unit-test:latest
script:
- python /workspace/trigger.py $CI_COMMIT_REF_NAME $CI_COMMIT_SHORT_SHA $GITLAB_USER_LOGIN $CI_REPOSITORY_URL $CI_PROJECT_NAME master $CI_PROJECT_ID $CI_MERGE_REQUEST_IID
- python /workspace/filter.py $CI_COMMIT_REF_NAME only:Run only on merge‑request events. tags: Use runners labeled qaci. stage: Test stage. script: Executes Python scripts; environment variables are injected by GitLab.
Coverage Collection and Comparison
mvn install -Dmaven.test.failure.ignore=true sonar:sonar \
-Dsonar.host.url=http://xxxx.huolala.com \
-Dsonar.login=xxxxx \
-Dsonar.projectKey=${params.projectKey} \
-Dsonar.branch.name=${params.revision} \
-Dsonar.analysis.tektonId=${params.tektonId} \
-Dsonar.analysis.branch=${params.revision} \
-Dsonar.branch.target=$TARGET -DskipTestsAdditional SonarQube settings control exclusions, target branch, and webhook notifications.
Custom Quality Gate
Standard SonarQube gates ignore low‑volume new code, causing false‑positive passes. We modified the gate to require at least 80% new‑line coverage, preventing accumulation of uncovered code.
String metricKeys = "new_line_coverage,alert_status,bugs,new_bugs,vulnerabilities,code_smells,coverage,new_coverage,new_lines,new_branch_coverage,duplicated_lines_density,duplicated_blocks,projects";
HttpRequest.get(host + "/api/qualitygates/project_status?projectKey=" + sonarProjectKey + "&branch=" + branch)
.basicAuth(user, password)
.form("login", user)
.form("password", password)
.form("component", sonarProjectKey)
.form("metricKeys", metricKeys)
.form("branch", branch)
.execute().body();The response is parsed to extract newLineCoverage and compare it against the 80% threshold.
Results and Benefits
Overall Data Overview
Best‑Project Implementation Comparison
Reduced Development Cost
GitLab Test Result Visualization Plugin
Conclusion and Outlook
All components reuse existing platform tools without introducing new services. SonarQube, traditionally used for static analysis, is extended to manage coverage and enforce quality gates. GitLab is leveraged for code management, while MR and GitLab Runner orchestrate the end‑to‑end testing workflow.
Future work includes exploring AI‑driven test generation to reduce the manual effort of writing unit tests and further refining the pipeline for a smoother developer experience.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
