Croupier C++ SDKCroupier C++ SDK
指南
API 参考
示例
配置
Croupier 主项目
  • Go SDK
  • Java SDK
  • JavaScript SDK
  • Python SDK
GitHub
指南
API 参考
示例
配置
Croupier 主项目
  • Go SDK
  • Java SDK
  • JavaScript SDK
  • Python SDK
GitHub
  • 入门指南

    • 入门指南
    • 安装指南
    • 快速开始
    • 构建指南
  • 核心概念

    • 架构设计
    • 虚拟对象
    • 函数注册
  • 高级主题

    • 插件系统
    • 部署指南
    • 测试指南

测试指南

本指南介绍如何测试使用 Croupier C++ SDK 的应用程序。

单元测试

使用 Google Test

#include <gtest/gtest.h>
#include "croupier/sdk/croupier_client.h"

class CroupierClientTest : public ::testing::Test {
protected:
    void SetUp() override {
        config_.game_id = "test-game";
        config_.env = "test";
        config_.agent_addr = "127.0.0.1:19090";
        config_.insecure = true;
    }

    ClientConfig config_;
};

TEST_F(CroupierClientTest, RegisterFunction) {
    CroupierClient client(config_);

    FunctionDescriptor desc;
    desc.id = "test.function";
    desc.version = "0.1.0";

    bool result = client.RegisterFunction(desc, [](auto ctx, auto payload) {
        return R"({"success": true})";
    });

    EXPECT_TRUE(result);
}

TEST_F(CroupierClientTest, ParseJSON) {
    std::string json = R"({"key": "value"})";
    auto result = utils::ParseJSON(json);

    EXPECT_TRUE(result.contains("key"));
    EXPECT_EQ(result["key"], "value");
}

TEST_F(CroupierClientTest, HandlerFunction) {
    auto handler = [](const std::string& ctx, const std::string& payload) -> std::string {
        auto data = utils::ParseJSON(payload);
        int a = data["a"];
        int b = data["b"];
        return utils::ToJSON({{"result", a + b}});
    };

    std::string result = handler("", R"({"a": 2, "b": 3})");
    auto response = utils::ParseJSON(result);

    EXPECT_EQ(response["result"], 5);
}

运行测试

# 启用测试构建
./scripts/build.sh --tests ON

# 运行所有测试
cd build
ctest

# 运行特定测试
./tests/unit_tests --gtest_filter="CroupierClientTest.*"

# 详细输出
./tests/unit_tests --gtest_filter="*" --gtest_print_time=1

Mock 测试

Mock Agent

class MockAgent {
public:
    MockAgent(int port) : port_(port) {}

    void Start() {
        server_thread_ = std::thread([this]() {
            // 启动简单的 gRPC 测试服务器
            // ...
        });
    }

    void Stop() {
        // 停止服务器
        server_thread_.join();
    }

    void SetResponse(const std::string& response) {
        mock_response_ = response;
    }

private:
    int port_;
    std::thread server_thread_;
    std::string mock_response_;
};

// 使用 Mock
TEST_F(CroupierClientTest, ConnectToMockAgent) {
    MockAgent mock(19090);
    mock.Start();

    CroupierClient client(config_);
    EXPECT_TRUE(client.Connect());

    mock.Stop();
}

函数 Mock

class MockFunctionHandler {
public:
    MOCK_METHOD(std::string, Handle, (const std::string&, const std::string&));

    std::string operator()(const std::string& ctx, const std::string& payload) {
        return Handle(ctx, payload);
    }
};

using ::testing::Return;
using ::testing::_;

TEST_F(CroupierClientTest, MockHandler) {
    MockFunctionHandler mock_handler;
    EXPECT_CALL(mock_handler, Handle(_, _))
        .WillOnce(Return(R"({"result": "mocked"})"));

    std::string result = mock_handler("", "{}");
    auto response = utils::ParseJSON(result);

    EXPECT_EQ(response["result"], "mocked");
}

集成测试

测试场景

class IntegrationTest : public ::testing::Test {
protected:
    void SetUp() override {
        // 启动测试 Agent
        StartTestAgent();

        // 配置客户端
        config_.game_id = "integration-test";
        config_.env = "test";
        config_.agent_addr = "127.0.0.1:19090";
        config_.insecure = true;
    }

    void TearDown() override {
        StopTestAgent();
    }

    void StartTestAgent() {
        // 启动实际的 Agent 进程
        agent_process_ = std::make_unique<Process>("croupier-agent", "-f", "test-config.yaml");
        agent_process_->Start();
        std::this_thread::sleep_for(std::chrono::seconds(2));
    }

    void StopTestAgent() {
        if (agent_process_) {
            agent_process_->Stop();
        }
    }

    ClientConfig config_;
    std::unique_ptr<Process> agent_process_;
};

TEST_F(IntegrationTest, FullWorkflow) {
    CroupierClient client(config_);

    // 注册函数
    FunctionDescriptor desc;
    desc.id = "test.workflow";
    desc.version = "0.1.0";

    bool registered = client.RegisterFunction(desc, [](auto ctx, auto payload) {
        return R"({"status": "completed"})";
    });
    ASSERT_TRUE(registered);

    // 连接
    ASSERT_TRUE(client.Connect());

    // 等待注册完成
    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    // 这里可以通过 Agent 调用函数验证...

    client.Close();
}

性能测试

基准测试

#include <benchmark/benchmark.h>

static void BM_FunctionCall(benchmark::State& state) {
    auto handler = [](const std::string& ctx, const std::string& payload) {
        return utils::ToJSON({{"result", 42}});
    };

    std::string payload = R"({"value": 100})";

    for (auto _ : state) {
        std::string result = handler("", payload);
        benchmark::DoNotOptimize(result);
    }
}

BENCHMARK(BM_FunctionCall);

static void BM_JSONParsing(benchmark::State& state) {
    std::string json = R"({"key": "value", "number": 42})";

    for (auto _ : state) {
        auto result = utils::ParseJSON(json);
        benchmark::DoNotOptimize(result);
    }
}

BENCHMARK(BM_JSONParsing);

BENCHMARK_MAIN();

运行性能测试

# 构建 benchmark
cmake -DBUILD_BENCHMARKS=ON ..
make benchmark

# 运行
./benchmark --benchmark_filter=.*

端到端测试

测试脚本

#!/bin/bash
# e2e_test.sh

set -e

echo "Starting end-to-end tests..."

# 启动 Server
croupier-server -f test-config/server.yaml &
SERVER_PID=$!
sleep 2

# 启动 Agent
croupier-agent -f test-config/agent.yaml &
AGENT_PID=$!
sleep 2

# 启动测试客户端
./build/test-client &
CLIENT_PID=$!
sleep 2

# 运行测试用例
python tests/e2e/run_tests.py

# 清理
kill $CLIENT_PID $AGENT_PID $SERVER_PID

echo "E2E tests completed!"

Python 测试客户端

import requests
import unittest

class E2ETest(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.base_url = "http://localhost:8080"

    def test_invoke_function(self):
        response = requests.post(
            f"{self.base_url}/api/v1/functions/test.workflow/invoke",
            json={"player_id": "test123", "amount": 100},
            headers={"X-Game-ID": "integration-test"}
        )
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json()["status"], "completed")

    def test_virtual_object_get(self):
        response = requests.get(
            f"{self.base_url}/api/v1/objects/wallet.entity/test123",
            headers={"X-Game-ID": "integration-test"}
        )
        self.assertEqual(response.status_code, 200)

if __name__ == "__main__":
    unittest.main()

测试配置

测试配置文件

{
  "game_id": "test-game",
  "env": "test",
  "service_id": "test-service",
  "agent_addr": "127.0.0.1:19090",
  "insecure": true,
  "timeout_seconds": 5,
  "auto_reconnect": false
}

CMake 测试配置

# CMakeLists.txt
include(CTest)
include(GoogleTest)

enable_testing()

# 测试可执行文件
add_executable(unit_tests
    tests/unit_tests.cpp
    tests/mock_agent.cpp
)

target_link_libraries(unit_tests
    PRIVATE
    croupier-sdk::croupier-sdk
    GTest::gtest_main
)

# 注册测试
discover_tests(unit_tests)

# 集成测试
add_executable(integration_tests
    tests/integration_tests.cpp
)

target_link_libraries(integration_tests
    PRIVATE
    croupier-sdk::croupier-sdk
    GTest::gtest_main
)

add_test(NAME Integration COMMAND integration_tests)

覆盖率报告

启用代码覆盖率

# GCC/Clang
cmake -DCMAKE_BUILD_TYPE=Debug \
      -DCMAKE_CXX_FLAGS="--coverage" \
      -DCMAKE_EXE_LINKER_FLAGS="--coverage" \
      -B build

# 运行测试
cd build
ctest

# 生成覆盖率报告
lcov --capture --directory . --output-file coverage.info
lcov --remove coverage.info '/usr/*' --output-file coverage.info
genhtml coverage.info --output-directory coverage_html

覆盖率目标

组件目标覆盖率
核心库80%+
工具函数70%+
示例代码60%+

最佳实践

1. 测试隔离

// 每个测试使用独立的命名空间
TEST_F(CroupierClientTest, RegisterFunction_UniqueName) {
    std::string unique_id = "test.function." + std::to_string(std::rand());
    // ...
}

2. 清理资源

TEST_F(CroupierClientTest, ResourceCleanup) {
    CroupierClient client(config_);
    client.Connect();

    // 测试逻辑...

    // 确保清理
    client.Close();
}

3. 超时控制

TEST_F(CroupierClientTest, Timeout) {
    // 设置测试超时
    ASSERT_DURATION_LE(5s, {
        client.Connect();
    });
}

4. 参数化测试

class ParamTest : public ::testing::TestWithParam<int> {};

TEST_P(ParamTest, VariousAmounts) {
    int amount = GetParam();
    auto result = ProcessTransfer(amount);
    EXPECT_TRUE(result["success"]);
}

INSTANTIATE_TEST_SUITE_P(
    TransferAmounts,
    ParamTest,
    ::testing::Values(1, 10, 100, 1000)
);
在 GitHub 上编辑此页
最后更新: 2026/1/9 14:14
Prev
部署指南