← 返回 Skills 市场
iskysun96

Generate Tests

作者 iskysun96 · GitHub ↗ · v1.0.0 · MIT-0
cross-platform ⚠ suspicious
207
总下载
0
收藏
0
当前安装
1
版本数
在 OpenClaw 中安装
/install generate-tests
功能描述
Creates comprehensive test suites for Move contracts with 100% coverage requirement. Triggers on: 'generate tests', 'create tests', 'write test suite', 'test...
使用说明 (SKILL.md)

Generate Tests Skill

Overview

This skill generates comprehensive test suites for Move contracts with 100% line coverage requirement. Tests verify:

  • ✅ Happy paths (functionality works)
  • ✅ Access control (unauthorized users blocked)
  • ✅ Input validation (invalid inputs rejected)
  • ✅ Edge cases (boundaries, limits, empty states)

Critical Rule: NEVER deploy without 100% test coverage.

Core Workflow

Step 1: Create Test Module

#[test_only]
module my_addr::my_module_tests {
    use my_addr::my_module::{Self, MyObject};
    use aptos_framework::object::{Self, Object};
    use std::string;
    use std::signer;

    // Test constants
    const ADMIN_ADDR: address = @0x100;
    const USER_ADDR: address = @0x200;
    const ATTACKER_ADDR: address = @0x300;

    // ========== Setup Helpers ==========
    // (Reusable setup functions)

    // ========== Happy Path Tests ==========
    // (Basic functionality)

    // ========== Access Control Tests ==========
    // (Unauthorized access blocked)

    // ========== Input Validation Tests ==========
    // (Invalid inputs rejected)

    // ========== Edge Case Tests ==========
    // (Boundaries and limits)
}

Step 2: Write Happy Path Tests

Test basic functionality works correctly:

#[test(creator = @0x1)]
public fun test_create_object_succeeds(creator: &signer) {
    // Execute
    let obj = my_module::create_my_object(
        creator,
        string::utf8(b"Test Object")
    );

    // Verify
    assert!(object::owner(obj) == signer::address_of(creator), 0);
}

#[test(owner = @0x1)]
public fun test_update_object_succeeds(owner: &signer) {
    // Setup
    let obj = my_module::create_my_object(owner, string::utf8(b"Old Name"));

    // Execute
    let new_name = string::utf8(b"New Name");
    my_module::update_object(owner, obj, new_name);

    // Verify (if you have view functions)
    // assert!(my_module::get_object_name(obj) == new_name, 0);
}

#[test(owner = @0x1, recipient = @0x2)]
public fun test_transfer_object_succeeds(
    owner: &signer,
    recipient: &signer
) {
    let recipient_addr = signer::address_of(recipient);

    // Setup
    let obj = my_module::create_my_object(owner, string::utf8(b"Object"));
    assert!(object::owner(obj) == signer::address_of(owner), 0);

    // Execute
    my_module::transfer_object(owner, obj, recipient_addr);

    // Verify
    assert!(object::owner(obj) == recipient_addr, 1);
}

Step 3: Write Access Control Tests

Test unauthorized access is blocked:

#[test(owner = @0x1, attacker = @0x2)]
#[expected_failure(abort_code = my_module::E_NOT_OWNER)]
public fun test_non_owner_cannot_update(
    owner: &signer,
    attacker: &signer
) {
    let obj = my_module::create_my_object(owner, string::utf8(b"Object"));

    // Attacker tries to update (should abort)
    my_module::update_object(attacker, obj, string::utf8(b"Hacked"));
}

#[test(owner = @0x1, attacker = @0x2)]
#[expected_failure(abort_code = my_module::E_NOT_OWNER)]
public fun test_non_owner_cannot_transfer(
    owner: &signer,
    attacker: &signer
) {
    let obj = my_module::create_my_object(owner, string::utf8(b"Object"));

    // Attacker tries to transfer (should abort)
    my_module::transfer_object(attacker, obj, @0x3);
}

#[test(admin = @0x1, user = @0x2)]
#[expected_failure(abort_code = my_module::E_NOT_ADMIN)]
public fun test_non_admin_cannot_configure(
    admin: &signer,
    user: &signer
) {
    my_module::init_module(admin);

    // Regular user tries admin function (should abort)
    my_module::update_config(user, 100);
}

Step 4: Write Input Validation Tests

Test invalid inputs are rejected:

#[test(user = @0x1)]
#[expected_failure(abort_code = my_module::E_ZERO_AMOUNT)]
public fun test_zero_amount_rejected(user: &signer) {
    my_module::deposit(user, 0); // Should abort
}

#[test(user = @0x1)]
#[expected_failure(abort_code = my_module::E_AMOUNT_TOO_HIGH)]
public fun test_excessive_amount_rejected(user: &signer) {
    my_module::deposit(user, my_module::MAX_DEPOSIT_AMOUNT + 1); // Should abort
}

#[test(owner = @0x1)]
#[expected_failure(abort_code = my_module::E_EMPTY_NAME)]
public fun test_empty_string_rejected(owner: &signer) {
    let obj = my_module::create_my_object(owner, string::utf8(b"Initial"));
    my_module::update_object(owner, obj, string::utf8(b"")); // Empty - should abort
}

#[test(owner = @0x1)]
#[expected_failure(abort_code = my_module::E_NAME_TOO_LONG)]
public fun test_string_too_long_rejected(owner: &signer) {
    let obj = my_module::create_my_object(owner, string::utf8(b"Initial"));

    // String exceeding MAX_NAME_LENGTH
    let long_name = string::utf8(b"This is an extremely long name that exceeds the maximum allowed length");

    my_module::update_object(owner, obj, long_name); // Should abort
}

#[test(owner = @0x1)]
#[expected_failure(abort_code = my_module::E_ZERO_ADDRESS)]
public fun test_zero_address_rejected(owner: &signer) {
    let obj = my_module::create_my_object(owner, string::utf8(b"Object"));
    my_module::transfer_object(owner, obj, @0x0); // Should abort
}

Step 5: Write Edge Case Tests

Test boundary conditions:

#[test(user = @0x1)]
public fun test_max_amount_allowed(user: &signer) {
    my_module::init_account(user);

    // Exactly MAX_DEPOSIT_AMOUNT should work
    my_module::deposit(user, my_module::MAX_DEPOSIT_AMOUNT);

    // Verify
    assert!(my_module::get_balance(signer::address_of(user)) == my_module::MAX_DEPOSIT_AMOUNT, 0);
}

#[test(user = @0x1)]
public fun test_max_name_length_allowed(user: &signer) {
    // Create string exactly MAX_NAME_LENGTH long
    let max_name = string::utf8(b"12345678901234567890123456789012"); // 32 chars if MAX = 32

    // Should succeed
    let obj = my_module::create_my_object(user, max_name);
}

#[test(user = @0x1)]
public fun test_empty_collection_operations(user: &signer) {
    let collection = my_module::create_collection(user, string::utf8(b"Collection"));

    // Should handle empty collection gracefully
    assert!(my_module::get_collection_size(collection) == 0, 0);
}

Step 6: Verify Coverage

Run tests with coverage:

# Run all tests
aptos move test

# Run with coverage
aptos move test --coverage

# Generate detailed coverage report
aptos move coverage source --module \x3Cmodule_name>

# Verify 100% coverage
aptos move coverage summary

Coverage report example:

module: my_module
coverage: 100.0% (150/150 lines covered)

If coverage \x3C 100%:

  1. Check uncovered lines in report
  2. Write tests for missing paths
  3. Repeat until 100%

Test Template Structure

#[test_only]
module my_addr::module_tests {
    use my_addr::module::{Self, Type};

    // ========== Setup Helpers ==========

    fun setup_default(): Object\x3CType> {
        // Common setup code
    }

    // ========== Happy Path Tests ==========

    #[test(user = @0x1)]
    public fun test_basic_operation_succeeds(user: &signer) {
        // Test happy path
    }

    // ========== Access Control Tests ==========

    #[test(owner = @0x1, attacker = @0x2)]
    #[expected_failure(abort_code = E_NOT_OWNER)]
    public fun test_unauthorized_access_fails(
        owner: &signer,
        attacker: &signer
    ) {
        // Test access control
    }

    // ========== Input Validation Tests ==========

    #[test(user = @0x1)]
    #[expected_failure(abort_code = E_INVALID_INPUT)]
    public fun test_invalid_input_rejected(user: &signer) {
        // Test input validation
    }

    // ========== Edge Case Tests ==========

    #[test(user = @0x1)]
    public fun test_boundary_condition(user: &signer) {
        // Test edge cases
    }
}

Testing Checklist

For each contract, verify you have tests for:

Happy Paths:

  • Object creation works
  • State updates work
  • Transfers work
  • All main features work

Access Control:

  • Non-owners cannot modify objects
  • Non-admins cannot call admin functions
  • Unauthorized users blocked

Input Validation:

  • Zero amounts rejected
  • Excessive amounts rejected
  • Empty strings rejected
  • Strings too long rejected
  • Zero addresses rejected

Edge Cases:

  • Maximum values work
  • Minimum values work
  • Empty states handled

Coverage:

  • 100% line coverage achieved
  • All error codes tested
  • All functions tested

ALWAYS Rules

  • ✅ ALWAYS achieve 100% test coverage
  • ✅ ALWAYS test error paths with #[expected_failure(abort_code = E_CODE)]
  • ✅ ALWAYS test access control with multiple signers
  • ✅ ALWAYS test input validation with invalid inputs
  • ✅ ALWAYS test edge cases (boundaries, limits, empty states)
  • ✅ ALWAYS use clear test names: test_feature_scenario
  • ✅ ALWAYS verify all state changes in tests
  • ✅ ALWAYS run aptos move test --coverage before deployment

NEVER Rules

  • ❌ NEVER deploy without 100% coverage
  • ❌ NEVER skip testing error paths
  • ❌ NEVER skip access control tests
  • ❌ NEVER use unclear test names
  • ❌ NEVER batch tests without verifying each case
  • ❌ NEVER hardcode real private keys or account addresses in test code — use test addresses like @0x1, @0x100, @0xCAFE
  • ❌ NEVER read .env or ~/.aptos/config.yaml to get test addresses

Common Pitfalls

Struct Field Access Across Modules

Problem: Test modules cannot access struct fields from other modules directly.

// ❌ WRONG - Will NOT compile
let listing = marketplace::get_listing(nft_addr);
assert!(listing.price == 1000, 0);  // ERROR: field access not allowed

Solution: Use public view accessor functions from the main module.

// ✅ CORRECT - Use accessor function
let (seller, price, timestamp) = marketplace::get_listing_details(nft_addr);
assert!(price == 1000, 0);

If the module doesn't have accessors, add them:

// In main module
#[view]
public fun get_listing_details(nft_addr: address): (address, u64, u64) acquires Listings {
    let listing = table::borrow(&listings.items, nft_addr);
    (listing.seller, listing.price, listing.listed_at)
}

Escrow Pattern Error Expectations

Problem: After listing an NFT to escrow, the seller no longer owns it.

// ❌ WRONG expectation
#[expected_failure(abort_code = marketplace::E_ALREADY_LISTED)]
public fun test_cannot_list_twice(seller: &signer) {
    list_nft(seller, nft, 1000);  // NFT transfers to marketplace
    list_nft(seller, nft, 2000);  // Fails with E_NOT_OWNER, not E_ALREADY_LISTED!
}

Solution: Understand validation order - ownership is checked before listing status.

// ✅ CORRECT expectation
#[expected_failure(abort_code = marketplace::E_NOT_OWNER)]
public fun test_cannot_list_twice(seller: &signer) {
    list_nft(seller, nft, 1000);  // NFT transfers to marketplace
    list_nft(seller, nft, 2000);  // Seller doesn't own it -> E_NOT_OWNER
}

Acquires Annotation Errors

Problem: Adding acquires for resources borrowed by framework functions causes errors.

// ❌ WRONG - framework handles its own acquires
public entry fun stake(...) acquires VaultConfig, Stakes, StakeTokenRefs {
    primary_fungible_store::transfer(...);  // Don't list what framework borrows
}

Solution: Only list resources YOUR code borrows.

// ✅ CORRECT
public entry fun stake(...) acquires VaultConfig, Stakes {
    let config = borrow_global\x3CVaultConfig>(...);  // You borrow this
    primary_fungible_store::transfer(...);          // Framework handles its own
}

References

Pattern Documentation:

  • ../../../patterns/move/TESTING.md - Comprehensive testing guide (see Pattern 8 for cross-module issues)
  • ../../../patterns/move/SECURITY.md - Security testing requirements

Official Documentation:

Related Skills:

  • write-contracts - Generate code to test
  • security-audit - Verify security after testing

Remember: 100% coverage is mandatory. Test happy paths, error paths, access control, and edge cases.

安全使用建议
This skill contains useful Move test templates and is instruction-only (no binaries, no credentials). However: (1) it claims a 100% coverage requirement but provides no tooling or steps to run tests or measure coverage—do not assume the skill will produce measurable 100% coverage automatically; (2) the source is unknown (metadata lists 'aptos-labs' as author in the file but the package origin is not verified)—treat that as possible impersonation and verify before trusting; (3) when using the skill, supply your contract source only through the agent if you trust it, and review every generated test before running, especially to ensure no sensitive keys/addresses are embedded; (4) run tests and coverage measurement locally with your known Move/Aptos toolchain (move/aptos CLI or trusted coverage tools) rather than relying on the skill to enforce coverage; and (5) avoid auto-deploying based on the skill's assertion—manually validate test results and coverage with your CI/tooling.
功能分析
Type: OpenClaw Skill Name: generate-tests Version: 1.0.0 The 'generate-tests' skill is a legitimate tool designed to help developers create comprehensive unit tests for Move smart contracts on the Aptos blockchain. It provides structured templates and best practices for testing happy paths, access control, and edge cases, while explicitly forbidding malicious practices such as hardcoding private keys or accessing sensitive local configuration files (SKILL.md).
能力评估
Purpose & Capability
The name and description (generate tests for Move contracts) match the SKILL.md content: the document contains Move test module templates and many example test cases. The templates reference Aptos/Move constructs (aptos_framework, signer, object) which are expected for this purpose. However, the metadata 'priority: critical' and the strong directive 'NEVER deploy without 100% test coverage' are prescriptive rather than technical capabilities.
Instruction Scope
The SKILL.md provides many concrete test templates but does not instruct how to: (a) obtain or inspect the user's Move contract source code, (b) run the tests (no mention of move/aptos CLI or test runners), or (c) measure/verify coverage. The skill asserts a 100% coverage requirement but gives no mechanism, tooling, or commands to calculate or enforce coverage—this is a mismatch between claimed capability and provided instructions. The instructions do not request secrets or external endpoints, and they do not explicitly tell the agent to read arbitrary system files, but they are vague about how the agent should gather context from the user's code to produce the promised tests.
Install Mechanism
Instruction-only skill with no install spec and no code files. Nothing is written to disk by an installer—this is the lowest install risk.
Credentials
The skill requires no environment variables, no credentials, and no config paths. There are no requests for unrelated secrets or system credentials in SKILL.md.
Persistence & Privilege
always is false and the skill does not request any persistent special privileges or modify other skills' configurations. Autonomous invocation is allowed (platform default) but not combined with other red flags here.
如何使用
  1. 确保已安装 OpenClaw(本地或 Docker 部署)
  2. 在对话框中输入安装命令:/install generate-tests
  3. 安装完成后,直接呼叫该 Skill 的名称或使用 /generate-tests 触发
  4. 根据 Skill 的参数说明提供必要输入,即可获得结构化输出
版本历史
v1.0.0
Initial release of "generate-tests": quickly generate Move contract test suites with full coverage. - Provides templates and detailed guidance for testing Move contracts with 100% line coverage. - Covers happy paths, access control, input validation, and edge cases. - Enforces a strict rule: never deploy without 100% test coverage. - Includes Move test code examples and coverage verification commands. - Supports prompts like "generate tests", "add test coverage", and similar triggers.
元数据
Slug generate-tests
版本 1.0.0
许可证 MIT-0
累计安装 0
当前安装数 0
历史版本数 1
常见问题

Generate Tests 是什么?

Creates comprehensive test suites for Move contracts with 100% coverage requirement. Triggers on: 'generate tests', 'create tests', 'write test suite', 'test... 它是一个面向 Claude Code / OpenClaw 的 AI Agent Skill 插件,目前累计下载 207 次。

如何安装 Generate Tests?

在 OpenClaw 或 Claude Code 对话框中运行命令「/install generate-tests」即可一键安装,无需额外配置。

Generate Tests 是免费的吗?

是的,Generate Tests 完全免费,采用 MIT-0 许可证,可自由下载、安装和使用。

Generate Tests 支持哪些平台?

Generate Tests 跨平台运行,可在任意部署了 OpenClaw / Claude Code 的环境中使用(cross-platform)。

谁开发了 Generate Tests?

由 iskysun96(@iskysun96)开发并维护,当前版本 v1.0.0。

💬 留言讨论