TDD pour Méthodes de Composants NG

VérifiéSûr

Guide complet pour écrire des tests unitaires TDD pour les méthodes de pattern NG. Couvre les principes fondamentaux, la réinitialisation d'état, la vérification d'API, la couverture de branches et les bonnes pratiques d'ace_engine.

Spar Skills Guide Bot
TestingIntermédiaire
6002/06/2026
Claude CodeCursorWindsurfCopilotCodex
#tdd#unit-testing#ng-component#test-guidelines#branch-coverage

Recommandé pour

Notre avis

Fournit des directives pour écrire des tests unitaires TDD pour les méthodes de modèle de composant NG, en se concentrant sur l'invocation directe, la réinitialisation d'état, la vérification d'API, la couverture de branches et l'évitement des pièges courants.

Points forts

  • Met l'accent sur l'invocation directe des méthodes pour éviter les déclencheurs indirects
  • Inclut des pratiques spécifiques comme la réinitialisation d'état et la vérification d'API avec grep
  • Couvre la couverture de branches avec des tests appariés
  • Avertit contre les tests de documentation uniquement et les nombres magiques

Limites

  • Spécifique à un framework particulier (motifs de composant NG dans ace_engine)
  • Suppose une familiarité avec C++ et le framework gtest
  • Peut ne pas couvrir tous les aspects du TDD en dehors de ce périmètre
Quand l'utiliser

Utilisez-le lors de l'écriture ou de la révision de tests unitaires pour les méthodes de modèle de composant NG dans ace_engine, en particulier pour des méthodes comme OnModifyDone ou OnDirtyLayoutWrapperSwap.

Quand l'éviter

Ne l'utilisez pas pour des pratiques TDD générales en dehors de ce framework spécifique ou pour des tests d'intégration ou de bout en bout.

Analyse de sécurité

Sûr
Score qualité92/100

This skill provides TDD writing guidelines with code examples and grep commands for looking up API definitions. No executable payloads, destructive commands, or data exfiltration risks. The grep commands are read-only and standard in development.

Aucun point d'attention détecté

Exemples

Write TDD test for OnModifyDone
I need to write a TDD unit test for the OnModifyDone method in an NG component pattern. Follow the guidelines: use direct invocation, reset state, verify APIs with grep, and cover branches. Include proper assertions and avoid magic numbers.
Review test for branch coverage
Review the following test for the OnDirtyLayoutWrapperSwap method. Ensure it follows TDD guidelines: no documentation-only tests, no magic numbers, correct branch coverage, and proper state reset.
Fix test with indirect invocation
My unit test for OnModifyDone is using FireBuilder to trigger the method, which doesn't work. How should I correct it to directly invoke OnModifyDone and reset state properly?

name: tdd description: This skill should be used when writing unit tests, TDD test cases, test coverage analysis, or test-driven development for NG component Pattern methods (e.g., OnModifyDone, OnDirtyLayoutWrapperSwap). Covers test principles, templates, common pitfalls, branch coverage, API verification, state reset, and mandatory self-checklist for ace_engine testing. version: 1.0.0

TDD Writing Guidelines for NG Component Pattern Methods

When writing TDD tests for NG component Pattern methods (e.g., OnModifyDone(), OnDirtyLayoutWrapperSwap()), follow these guidelines to avoid common pitfalls.

Core Principles

1. Direct Method Invocation

Call the target method directly, not through indirect triggers.

  • menuPattern->OnModifyDone()
  • menuPattern->FireBuilder() (doesn't trigger OnModifyDone())

2. State Reset

Always reset potentially interfering state before calling target method.

// Reset to clean state before testing
auto renderContext = menuNode->GetRenderContext();
renderContext->ResetOuterBorder();  // Reset entire property group

// Then call target method
menuPattern->OnModifyDone();

3. API Verification

Use Grep/Read tools to verify method names, never guess.

# Search for method definition
grep -rn "ResetOuterBorder\|Reset.*Outer" frameworks/core/components_ng/render/render_context.h

# Understand macro-generated methods
grep -A 5 "ACE_DEFINE_PROPERTY_GROUP" frameworks/core/components_ng/property/property.h
  • ACE_DEFINE_PROPERTY_GROUP(OuterBorder, OuterBorderProperty) generates:
    • GetOrCreateOuterBorder(), GetOuterBorder(), CloneOuterBorder(), ResetOuterBorder()
    • NOT ResetOuterBorderRadius() (common mistake)

4. Branch Coverage

Write paired tests for if/else branches.

// Branch 1: Condition is true
HWTEST_F(Xxx, Test_BranchTrue, TestSize.Level1) {
    borderRadiusVP.SetRadius(Dimension(10.0_vp));  // No PERCENT unit
    renderContext->ResetOuterBorder();
    pattern->OnModifyDone();
    EXPECT_TRUE(outerRadius.has_value());
}

// Branch 2: Condition is false
HWTEST_F(Xxx, Test_BranchFalse, TestSize.Level1) {
    borderRadiusPercent.SetRadius(Dimension(50.0f, DimensionUnit::PERCENT));
    renderContext->ResetOuterBorder();
    pattern->OnModifyDone();
    EXPECT_FALSE(outerRadius.has_value());
}

5. No Line Numbers in Comments

Avoid hardcoding line numbers in test comments.

  • OnModifyDone should skip UpdateBorderRadius at line 299
  • OnModifyDone should skip UpdateBorderRadius when borderRadius has percent unit
  • Reason: Line numbers change as code evolves, making comments outdated
  • Alternative: Describe the logical condition being tested rather than the physical line location

6. No Documentation-Only Tests

Every test case must have actual test logic.

  • ❌ Documentation-only test with only comments and EXPECT_TRUE(true)
  • ✅ Test with real assertions and verification logic
  • Reason: Tests without executable logic don't verify behavior and waste maintenance resources
  • Alternative: Put branch documentation in separate design documents or code comments, not in test cases
  • Example of what NOT to do:
    // ❌ BAD: Documentation-only test
    HWTEST_F(ComponentPatternTestNg, ComponentPattern_BranchDocumentation, TestSize.Level1) {
        /**
         * @tc.steps: step1. Document branches
         * Branch 1: condition is true
         * Branch 2: condition is false
         */
        EXPECT_TRUE(true);  // No actual testing!
    }
    
  • Correct approach: Write separate test cases for each branch with real logic

7. No Magic Numbers

Use named constants instead of hardcoded numbers.

  • ❌ Hardcoded numbers like 720, 1280, 0, 3.0, -1, 100000
  • ✅ Named constants with clear semantic meaning
  • Reason: Magic numbers make code hard to understand and maintain. Constants provide context and make changes easier
  • Where to define: Place constants in anonymous namespace at the top of the test file
  • Example:
    // ❌ BAD: Magic numbers
    HWTEST_F(ComponentPatternTestNg, ComponentPattern_TestBranch, TestSize.Level1) {
        SystemProperties::InitDeviceInfo(720, 1280, 0, 3.0, false);
        auto size = GetSubWindowSize(100000, 0);
        EXPECT_GT(size.Width(), 0);
    }
    
    // ✅ GOOD: Named constants
    namespace {
        const int32_t TEST_DEVICE_WIDTH = 720;
        const int32_t TEST_DEVICE_HEIGHT = 1280;
        const int32_t TEST_DEVICE_ORIENTATION = 0;
        const double TEST_DEVICE_RESOLUTION = 3.0;
        const bool TEST_DEVICE_IS_ROUND = false;
        const int32_t TEST_PARENT_CONTAINER_ID = 100000;
        const uint32_t DEFAULT_DISPLAY_ID = 0;
    }
    HWTEST_F(ComponentPatternTestNg, ComponentPattern_TestBranch, TestSize.Level1) {
        SystemProperties::InitDeviceInfo(
            TEST_DEVICE_WIDTH, TEST_DEVICE_HEIGHT, TEST_DEVICE_ORIENTATION,
            TEST_DEVICE_RESOLUTION, TEST_DEVICE_IS_ROUND);
        auto size = GetSubWindowSize(TEST_PARENT_CONTAINER_ID, DEFAULT_DISPLAY_ID);
        EXPECT_GT(size.Width(), 0);
    }
    

Test Template

/**
 * @tc.name: PatternMethod_TestBranchName
 * @tc.desc: Test Description covering specific behavior (e.g., "when X happens, Y should occur")
 * @tc.type: FUNC
 */
HWTEST_F(ComponentPatternTestNg, ComponentPattern_TestBranchName, TestSize.Level1)
{
    // 1. Environment setup
    MockPipelineContext::GetCurrent()->SetMinPlatformVersion(static_cast<int32_t>(PlatformVersion::VERSION_ELEVEN));
    ScreenSystemManager::GetInstance().dipScale_ = DIP_SCALE;

    // 2. Create nodes (using existing helper methods like GetPreviewMenuWrapper)
    auto menuWrapperNode = GetPreviewMenuWrapper();
    ASSERT_NE(menuWrapperNode, nullptr);
    auto menuNode = AceType::DynamicCast<FrameNode>(menuWrapperNode->GetChildAtIndex(0));
    ASSERT_NE(menuNode, nullptr);
    auto menuPattern = menuNode->GetPattern<MenuPattern>();
    ASSERT_NE(menuPattern, nullptr);
    auto menuLayoutProperty = menuNode->GetLayoutProperty<MenuLayoutProperty>();
    ASSERT_NE(menuLayoutProperty, nullptr);
    auto renderContext = menuNode->GetRenderContext();
    ASSERT_NE(renderContext, nullptr);

    // 3. Set test data to trigger target branch
    TestDataType testData;
    testData.SetValue(...);  // Configure to trigger branch
    menuLayoutProperty->UpdateProperty(testData);

    // 4. Reset interfering state (CRITICAL!)
    renderContext->ResetTargetProperty();

    // 5. Call target method directly
    menuPattern->TargetMethod();

    // 6. Verify results match branch expectation
    auto result = renderContext->GetTargetProperty();
    EXPECT_TRUE(result.has_value());  // Or false based on branch
}

Common Pitfalls

| Pitfall | Symptom | Solution | |:---|:---|:---| | Indirect method call | Test doesn't trigger target code path | Call target method directly | | No state reset | Test fails with unexpected values | Reset all potentially interfering properties before calling method | | Guessed API name | Compilation error or method not found | Use Grep to verify exact method name from source | | Unclean test data | Tests interfere with each other | Each test should be independent, reset all state | | Missing branch coverage | Code coverage shows gaps | Write tests for both true and false branches | | Line numbers in comments | Comments become outdated when code changes | Describe logical conditions instead of physical locations | | Documentation-only tests | Test contains only comments with EXPECT_TRUE(true) | Every test must have real assertions and verification logic | | Magic numbers | Hardcoded numbers like 720, 1280, 100000 without meaning | Use named constants with clear semantic meaning in anonymous namespace |

Mandatory Self-Checklist

After generating test code, verify:

  • [ ] All called methods exist (verified via Grep/Read)
  • [ ] State is reset before calling target method
  • [ ] Test data triggers the correct branch (HasPercentUnit returns expected value)
  • [ ] Assertions match branch behavior
  • [ ] All API signatures match source definitions
  • [ ] Test follows existing test file structure and naming conventions
  • [ ] Comments describe logical behavior, not line numbers (e.g., use "when X happens" instead of "at line 299")
  • [ ] Every test case has real test logic (not just documentation with EXPECT_TRUE(true))
  • [ ] No magic numbers - all numeric literals replaced with named constants

Reference Implementation

See working example at: test/unittest/core/pattern/menu/menu_pattern_test_ng.cpp:1482-1574

  • MenuPatternTest_OnModifyDone_UpdateBorderRadius - Tests when border has no PERCENT unit
  • MenuPatternTest_OnModifyDone_PercentBorderRadius - Tests when border has PERCENT unit
Skills similaires