集成测试拓扑

集成测试场景涉及两个或多个在同一领域中运行的并交换功能的组件。虽然大多数测试都是仅针对单个组件的单元测试,但集成测试场景需要定义领域拓扑和功能路由。

测试 Runner 框架集成

集成测试组件通过添加与支持的语言特定测试框架匹配的测试运行程序分片,与测试运行程序框架集成。

Rust

include: [
    "//src/sys/test_runners/rust/default.shard.cml",
    "syslog/client.shard.cml",
],

// Information about the program to run.
program: {
    // The binary to run for this component.
    binary: "bin/echo_integration_test_rust",
},

C++

include: [
    "//src/sys/test_runners/gtest/default.shard.cml",
    "syslog/client.shard.cml",
],

// Information about the program to run.
program: {
    // The binary to run for this component.
    binary: "bin/echo_integration_test_cpp",
},

此分片提供两个关键元素:

  1. 公开 fuchsia.test.Suite 协议,框架发现和执行测试用例需要该协议。
  2. 将程序 runner 设置为为给定测试框架提供的测试运行程序。

测试领域拓扑

集成测试组件声明测试领域的拓扑,并将其自身作为父级。这样,测试控制器就可以负责在被测组件及其依赖项之间进行功能路由。

以下是对 echo_server 组件进行集成测试的示例拓扑:


集成测试拓扑

这是一个简单的测试领域,它绑定到 echo_server 组件公开的 fidl.examples.routing.echo.Echo 协议。echo_integration_test 软件包包含以下组件:

  • echo_integration_test - 测试控制器组件
  • echo_server - 受测组件

您可以通过以下方式定义测试领域拓扑:

  • Realm Builder:使用 Realm Builder 库在代码中动态生成。
  • 组件清单:使用组件清单语言 (CML) 进行静态静态显示。

您可以参考下表来确定哪种方法最适合您的集成测试:

集成测试用例 Realm Builder 静态 CML
使用静态依赖项进行简单的集成测试
每个测试用例的唯一组件实例
绑定到每个测试用例的被测组件的生命周期
被测组件的动态虚构实例、模拟实例和桩实例
测试用例之间的动态路由和配置

Realm Builder

如果需要在运行时定义领域拓扑,或需要将组件替换为本地模拟实现,您可以使用 Realm Builder 库在测试代码中动态创建拓扑。

测试控制器组件的清单包含使用其组件清单分片的 Realm Builder 库:

Rust

include: [
    "sys/component/realm_builder.shard.cml",
    // ...
],

C++

include: [
    "sys/component/realm_builder.shard.cml",
    // ...
],

测试控制器代码会构建测试领域拓扑,添加 echo_server 作为子级组件并声明必要的功能路由回父级:

Rust

use {
    // ...
    fuchsia_component_test::{
        Capability, ChildOptions, LocalComponentHandles, RealmBuilder, Ref, Route,
    },
    futures::{StreamExt, TryStreamExt},
};

// ...

let builder = RealmBuilder::new().await?;

// Add component to the realm, which is fetched using a URL.
let echo_server = builder
    .add_child(
        "echo_server",
        "fuchsia-pkg://fuchsia.com/realm-builder-examples#meta/echo_server.cm",
        ChildOptions::new(),
    )
    .await?;

builder
    .add_route(
        Route::new()
            .capability(Capability::protocol_by_name("fidl.examples.routing.echo.Echo"))
            .from(&echo_server)
            .to(Ref::parent()),
    )
    .await?;

C++

#include <lib/sys/component/cpp/testing/realm_builder.h>

// ...

auto builder = RealmBuilder::Create();

// Add component server to the realm, which is fetched using a URL.
builder.AddChild("echo_server",
                 "fuchsia-pkg://fuchsia.com/realm-builder-examples#meta/echo_server.cm");

builder.AddRoute(Route{.capabilities = {Protocol{"fidl.examples.routing.echo.Echo"}},
                       .source = ChildRef{"echo_server"},
                       .targets = {ParentRef()}});

测试控制器代码通过已创建的大区所提供的功能与 echo_server 进行交互:

Rust

let realm = builder.build().await?;

let echo = realm.root.connect_to_protocol_at_exposed_dir::<fecho::EchoMarker>()?;
assert_eq!(echo.echo_string(Some("hello")).await?, Some("hello".to_owned()));

C++

auto realm = builder.Build(dispatcher());

auto echo = realm.component().ConnectSync<fidl::examples::routing::echo::Echo>();
fidl::StringPtr response;
echo->EchoString("hello", &response);
ASSERT_EQ(response, "hello");

如需详细了解如何使用 Realm Builder 实现测试,请参阅 Realm Builder 开发者指南。

组件清单

如果测试中的所有组件都是静态的,您可以在测试控制器的组件清单中使用 CML 以声明方式定义测试领域的整个拓扑。

测试控制器组件的清单以静态方式将被测 echo_server 组件声明为子项,并将必要的功能路由回父组件:

Rust

{
    include: [
        "//src/sys/test_runners/rust/default.shard.cml",
        "syslog/client.shard.cml",
    ],

    // Information about the program to run.
    program: {
        // The binary to run for this component.
        binary: "bin/echo_integration_test_rust",
    },


    // Child components orchestrated by the integration test.
    children: [
        {
            name: "echo_server",
            url: "#meta/echo_server.cm",
        },
    ],

    // Capabilities used by this component.
    use: [
        {
            protocol: [ "fidl.examples.routing.echo.Echo" ],
            from: "#echo_server",
        },
    ],

    // Capabilities required by components under test.
    offer: [
        {
            protocol: [
                "fuchsia.inspect.InspectSink",
                "fuchsia.logger.LogSink",
            ],
            from: "parent",
            to: "#echo_server",
        },
    ],
}

C++

{
    include: [
        "//src/sys/test_runners/gtest/default.shard.cml",
        "syslog/client.shard.cml",
    ],

    // Information about the program to run.
    program: {
        // The binary to run for this component.
        binary: "bin/echo_integration_test_cpp",
    },


    // Child components orchestrated by the integration test.
    children: [
        {
            name: "echo_server",
            url: "#meta/echo_server.cm",
        },
    ],

    // Capabilities used by this component.
    use: [
        {
            protocol: [ "fidl.examples.routing.echo.Echo" ],
            from: "#echo_server",
        },
    ],

    // Capabilities required by components under test.
    offer: [
        {
            protocol: [
                "fuchsia.inspect.InspectSink",
                "fuchsia.logger.LogSink",
            ],
            from: "parent",
            to: "#echo_server",
        },
    ],
}

测试控制器代码通过其公开的功能与 echo_server 进行交互:

Rust

use {anyhow::Error, fidl_fidl_examples_routing_echo as fecho, fuchsia_component::client};

#[fuchsia::test]
async fn echo_integration_test() -> Result<(), Error> {
    const ECHO_STRING: &str = "Hello, world!";

    let echo = client::connect_to_protocol::<fecho::EchoMarker>()
        .expect("error connecting to echo server");
    let out = echo.echo_string(Some(ECHO_STRING)).await.expect("echo_string failed");

    assert_eq!(ECHO_STRING, out.unwrap());
    Ok(())
}

C++

#include <fidl/examples/routing/echo/cpp/fidl.h>
#include <lib/fidl/cpp/string.h>
#include <lib/sys/cpp/component_context.h>

#include <string>

#include <gtest/gtest.h>

TEST(EchoIntegrationTest, TestEcho) {
  ::fidl::examples::routing::echo::EchoSyncPtr echo_proxy;
  auto context = sys::ComponentContext::Create();
  context->svc()->Connect(echo_proxy.NewRequest());

  ::fidl::StringPtr request("Hello, world!");
  ::fidl::StringPtr response = nullptr;
  ASSERT_TRUE(echo_proxy->EchoString(request, &response) == ZX_OK);
  ASSERT_TRUE(request == response);
}

测试软件包

所有受测组件都包含在同一封闭测试软件包中。这有助于增强在不同环境中运行和更新测试的能力,而无需担心依赖项不同步。

请参阅以下 BUILD.gn 文件,该文件定义了此示例的 fuchsia_test_package() 目标:

Rust

rustc_test("bin") {
  name = "echo_integration_test_rust"
  edition = "2021"

  deps = [
    "//examples/components/routing/fidl:echo_rust",
    "//src/lib/fuchsia",
    "//src/lib/fuchsia-component",
    "//third_party/rust_crates:anyhow",
  ]

  sources = [ "src/lib.rs" ]
}


fuchsia_component("echo_integration_test_component") {
  testonly = true
  component_name = "echo_integration_test"
  manifest = "meta/echo_integration_test.cml"
  deps = [ ":bin" ]
}


fuchsia_test_package("echo_integration_test_rust") {
  test_components = [ ":echo_integration_test_component" ]
  deps = [ "//examples/components/routing/rust/echo_server:echo_server_cmp" ]
}

C++

executable("bin") {
  output_name = "echo_integration_test_cpp"
  sources = [ "echo_integration_test.cc" ]
  deps = [
    "//examples/components/routing/fidl:echo_hlcpp",
    "//sdk/lib/sys/cpp",
    "//sdk/lib/sys/cpp/testing:unit",
    "//src/lib/fxl/test:gtest_main",
    "//third_party/googletest:gtest",
    "//zircon/system/ulib/async-loop:async-loop-cpp",
    "//zircon/system/ulib/async-loop:async-loop-default",
  ]
  testonly = true
}


fuchsia_component("echo_integration_test_component") {
  testonly = true
  component_name = "echo_integration_test"
  manifest = "meta/echo_integration_test.cml"
  deps = [ ":bin" ]
}


fuchsia_test_package("echo_integration_test_cpp") {
  test_components = [ ":echo_integration_test_component" ]
  deps = [ "//examples/components/routing/cpp/echo_server:echo_server_cmp" ]
}

使用以下变量将组件内置到 fuchsia_test_package() 中:

  • test_components:此类组件包含公开 fuchsia.test.Suite 协议的测试。
  • deps:集成测试所需的其他组件依赖项。

如需了解详情,请参阅测试软件包 GN 模板

测试组件名称

组件的名称用于标识组件拓扑中的唯一实例。对于在测试拓扑中运行的组件,名称路径相对于测试领域中的根组件。在上面的示例中,根组件是提供 fuchsia.test.Suite 协议的测试控制器组件。

子名称格式取决于您的测试领域拓扑

  • 静态 CML:以静态方式声明为根测试控制器子项的组件只能通过其组件 name 进行标识。
  • Realm Builder:Realm Builder 在测试控制器与子组件之间引入一个中间集合。如需详细了解使用 Realm Builder 的测试组件名称,请参阅 Realm Builder 开发者指南。