Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Implementing TDD Workflows in OpenHarmony: Framework, Build Config, and Execution

Tech 1

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. Level0 represents mandatory gate checks, while Level1 through Level4 indicate 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

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.