Implementing TDD Workflows in OpenHarmony: Framework, Build Config, and Execution
Configuring C++ Test Suites and Thread Management
To initialize a C++ unit test in the OpenHarmony framework, begin by including the necessary header files for the module under evaluation. Define a test fixture class that inherits from the stendard testing base. The framework provides dedicated macros for concurrent execution, allowing developers to specify thread allocation dynamically or statically.
Macro Definitions and Parameters
The core testing macros accept four positional parameters:
- Suite Identifier: The name of the test fixture class.
- Case Identifier: Formatted as
[FeatureName]_[Index], where the index is a three-digit number starting from 001. - Execution Level: Determines priority in CI/CD pipelines.
Level0represents mandatory gate checks, whileLevel1throughLevel4indicate decreasing criticality. - Thread Count (Optional): Defines the parallelism degree for concurrent macro variants.
Code Implementation Example
#include "data_processor.h"
#include <iostream>
#include <thread>
#include <sstream>
// Target function for validation
int aggregateValues(const std::vector<int>& dataSet) {
int total = 0;
for (const auto& val : dataSet) {
total += val * val;
}
std::cout << "Processed dataset size: " << dataSet.size() << " | Result: " << total << std::endl;
return total;
}
// Worker logic executed across threads
void executeWorkerTask() {
std::vector<int> sample = {2, 3, 5};
int output = aggregateValues(sample);
auto tid = std::this_thread::get_id();
std::stringstream converter;
converter << tid;
long numericId = std::stol(converter.str().back(5));
std::cout << "Active Thread ID: " << numericId << std::endl;
EXPECT_EQ(output, 38); // 4 + 9 + 25
}
// Dynamic thread allocation within a fixture
HWTEST_F(ProcessorTestSuite, ValidateSum_001, TestSize.Level1) {
SET_THREAD_NUM(4); // Adjusts worker pool size for this specific case
std::cout << "[Init] Parallel Execution\n";
GTEST_RUN_TASK(executeWorkerTask);
std::cout << "[Done] Parallel Execution\n";
}
// Static thread declaration at compile time
HWMTEST_F(ProcessorTestSuite, ValidateSum_002, TestSize.Level2, 8) {
std::cout << "[Init] Fixed Pool Execution\n";
executeWorkerTask();
std::cout << "[Done] Fixed Pool Execution\n";
}
For batch registration without immediate execution, MTEST_ADD_TASK(threadIndex, callback) queues operations. Subsequently calling MTEST_POST_RUN() triggers all registered callbacks simultaneously, optimizing complex dependency scenarios.
JavaScript and ETS Unit Testing Standards
Frontend testing utilizes the deccjsunit library. Source files must follow PascalCase naming and terminate with TEST (e.g., NetworkManagerTest.js). The testing lifecycle revolves around describe blocks for suite isolation and it blocks for individual assertions.
Standard Structure
import network from '@ohos.net.connection';
import { describe, beforeAll, afterAll, beforeEach, afterEach, it, expect } from 'deccjsunit/index';
describe('NetworkStateTest', function () {
beforeAll(() => console.info('Suite initialization complete'));
afterAll(() => console.info('Suite teardown complete'));
beforeEach(() => console.info('Case setup triggered'));
afterEach(() => console.info('Case cleanup triggered'));
/*
* @tc.name: CheckConnectivity_001
* @tc.desc: Verify network state object is successfully instantiated
* @tc.type: FUNC
* @tc.require: issueNET5092
*/
it('CheckConnectivity_001', 0, function () {
const state = network.getDefaultConnection();
// Assertion 1: Object existence
expect(state !== undefined).assertEqual(true);
// Assertion 2: State property validation
expect(state.hasDefault).assertEqual(true);
});
});
Annotation compliance is mandatory. The @tc.require field must prefix with AR/, SR/, or issue. The @tc.type maps to FUNC, PERF, RELI, SECU, or FUZZ.
GN Build System Integrasion for Test Modules
Compilation directives reside in BUILD.gn files and import //build/test.gni. Output directories typically mirror the component hierarchy.
C++ Compilation Template
import("//build/test.gni")
output_target_dir = "dev_test/network_core"
config("private_deps") {
visibility = [ "*" ]
include_dirs = [ "../../../include" ]
}
ohos_unittest("NetCoreSubTest") {
module_out_path = output_target_dir
sources = [
"../../../include/net_core.h",
"../../../src/net_impl.cpp",
"net_sub_test.cpp"
]
configs = [ ":private_deps" ]
deps = [ "//third_party/googletest:gtest_main" ]
}
group("unittest") {
testonly = true
deps = [ ":NetCoreSubTest" ]
}
Stage Model ETS Compilation Template
import("//build/test.gni")
stage_output_dir = "dev_test/ets_framework"
ohos_js_stage_unittest("ActsBundleMgrStageTest") {
hap_profile = "entry/src/main/module.json"
certificate_profile = "signature/openharmony_dev.p7b"
deps = [
":stage_test_assets",
":stage_test_res",
]
ets2abc = true
hap_name = "ActsBundleMgrStageTest"
subsystem_name = "dev_test"
part_name = "ets_framework"
module_out_path = stage_output_dir
}
ohos_js_assets("stage_test_assets") {
source_dir = "entry/src/main/ets"
}
ohos_resources("stage_test_res") {
sources = [ "entry/src/main/resources" ]
hap_profile = "entry/src/main/module.json"
}
group("unittest") {
testonly = true
deps = [ ":ActsBundleMgrStageTest" ]
}
Component Registration and Static Resource Injection
After defining build targets, register them in the component's bundle.json under the test array:
{
"build": {
"test": [
"//test/testfwk/examples/network:unittest",
"//test/testfwk/examples/calculator:fuzztest"
]
}
}
External assets (images, configs, binaries) require an ohos_test.xml descriptor placed in test/resource/[module]/. The GN file must reference this manifest:
ohos_unittest("NetCoreSubTest") {
resource_config_file = "//subsystem/partA/test/resource/network/ohos_test.xml"
}
XML attributes define target_name, preparer actions, and src location (res for local test directory, out for build artifacts).
Test Execution Workflows
Execution relies on device connectivity profiles defined in user_config.xml. For standard HDC connections, only IP and port mappings are required.
Windows Environment
Compilation must occur in a Linux environment first using:
./build.sh --product-name {product} --build-target make_test
Transfer the developer_test framework, xdevice, and compiled outputs to a Windows Test/testcase directory. Update user_config.xml <testcase> to true and point <dir> to the local path.
Linux Remote Execution
Configure HDC port forwarding on the remote host:
hdc kill
hdc -m -s 0.0.0.0:8710
On the client machine, verify connectivity:
hdc -s {remote_ip}:8710 list targets
Initialize the runner via ./start.sh. Product profiles are auto-detected or manually appended in config/framework_config.xml.
Command Reference for TDD, ACTS, and HATS
TDD (Unit Tests)
| Parameter | Description |
|---|---|
-t UT |
Mandatory test type selector |
-tp [Part] |
Target componant |
-tm [Module] |
Target sub-module (requires -tp) |
-ts [Suite] |
Target test suite class |
-tc [Class.Case] |
Specific test method |
-ra random |
Shuffle execution order |
--repeat N |
Iteration count |
Examples:
run -t UT -tp framework_base -ts NetworkStateTest
run -t UT -tp framework_base -tm network -tc NetworkStateTest.CheckConnectivity_001
run -t UT -cov coverage
ACTS & HATS (System Integration)
| Parameter | Description |
|---|---|
-t ACTS/HATS |
Integration test type |
-ss [Subsys] |
Target subsystem (comma-separated) |
-ta class:Class#Method |
Target specific class method |
Examples:
run -t ACTS -ss arkui,telephony
run -t HATS -ss telephony -ts HatsRilServiceTest
run -t ACTS -ss arkui -ts ActsUIComponentTest --repeat 2
run --retry
Report Generation and Coverage Configuration
Post-execution artifacts are stored in test/developer_test/reports/[timestamp]/. Key outputs include summary_report.html, details_report.html, structured result/ JSONs, and log/plan_log*.log.
For code coverage, inject compiler flags before building:
cflags += [ "--coverage" ]
cflags_cc += [ "--coverage" ]
ldflags += [ "--coverage" ]
To exclude boilerplate noise, execute the preprocessing script within /test/testfwk/developer_test/localCoverage/restore_comment prior to compilation:
python3 build_before_generate.py
Filter results by component during execution:
run -tp core_framework
run -tp audio_driver camera_driver