You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用GoogleTest对Renderer API进行单元测试?

问题描述

我正在编写的库中包含如下Renderer API:

class EXPORT Renderer {
  public:
    Renderer(const Renderer &) = delete;
    Renderer &operator=(const Renderer &) = delete;
    virtual ~Renderer();

  protected:
    Renderer() = default;

  public:
    [[nodiscard]] static std::unique_ptr<Renderer> create(const RendererDescriptor &descriptor);

    [[nodiscard]] virtual std::unique_ptr<SwapChain> createSwapChain(const SwapChainDescriptor &descriptor, const Window &window) = 0;

    [[nodiscard]] virtual std::unique_ptr<Buffer> createVertexBuffer(const BufferDescriptor &descriptor, const VertexFormat &format) = 0;

    [[nodiscard]] virtual std::unique_ptr<Buffer> createIndexBuffer(const BufferDescriptor &descriptor, Format format) = 0;

    [[nodiscard]] virtual std::unique_ptr<Shader> createShaderFromString(const ShaderDescriptor &descriptor, std::string_view source) = 0;

    [[nodiscard]] virtual std::unique_ptr<PipelineState> createPipelineState(const PipelineStateDescriptor &descriptor) = 0;

    [[nodiscard]] virtual std::unique_ptr<CommandList> createCommandList(const CommandListDescriptor &descriptor) = 0;

    [[nodiscard]] virtual std::unique_ptr<Texture> createTexture(const TextureDescriptor &descriptor) = 0;

    [[nodiscard]] virtual std::unique_ptr<RenderTarget> createRenderTarget(const RenderTargetDescriptor &descriptor) = 0;

    [[nodiscard]] virtual std::unique_ptr<RenderPass> createRenderPass(const RenderPassDescriptor &descriptor) = 0;
};

该Renderer API在另一库中实现了OpenGL后端,示例如下:

class GLRenderer final : public Renderer {
  public:
    explicit GLRenderer(const RendererDescriptor &descriptor);

    std::unique_ptr<SwapChain> createSwapChain(const SwapChainDescriptor &descriptor, const Window &window) override;

    std::unique_ptr<Buffer> createVertexBuffer(const BufferDescriptor &descriptor, const VertexFormat &format) override;

    std::unique_ptr<Buffer> createIndexBuffer(const BufferDescriptor &descriptor, Format format) override;

    std::unique_ptr<Shader> createShaderFromString(const ShaderDescriptor &descriptor, std::string_view source) override;

    std::unique_ptr<PipelineState> createPipelineState(const PipelineStateDescriptor &descriptor) override;

    std::unique_ptr<CommandList> createCommandList(const CommandListDescriptor &descriptor) override;

    std::unique_ptr<Texture> createTexture(const TextureDescriptor &descriptor) override;

    std::unique_ptr<RenderTarget> createRenderTarget(const RenderTargetDescriptor &descriptor) override;

    std::unique_ptr<RenderPass> createRenderPass(const RenderPassDescriptor &descriptor) override;

  private:
// private members
};

Renderer::create工厂方法会加载此OpenGL后端库并分配GLRenderer实例后返回。请问应如何使用GoogleTest对该API进行单元测试?


测试方案

针对这个Renderer API的单元测试,可以围绕实例创建有效性、资源创建逻辑、边界参数处理三个核心方向展开,利用GoogleTest的断言机制验证行为:

1. 基础实例创建测试

先验证Renderer工厂方法的核心逻辑:正常配置能返回有效实例,无效配置返回空。

TEST(RendererTest, CreateInstanceWithValidDescriptor) {
    RendererDescriptor desc;
    desc.api = RenderAPI::OpenGL;
    desc.debug = true; // 开启调试模式

    auto renderer = Renderer::create(desc);
    ASSERT_NE(renderer, nullptr); // 验证实例非空
}

TEST(RendererTest, CreateInstanceWithInvalidAPI) {
    RendererDescriptor invalid_desc;
    invalid_desc.api = RenderAPI::Unknown; // 传入不支持的API类型

    auto renderer = Renderer::create(invalid_desc);
    ASSERT_EQ(renderer, nullptr); // 验证无效配置返回空
}

2. 各类资源创建测试

针对每个createXXX方法,覆盖正常创建、边界参数、错误参数三种场景:

顶点缓冲区创建测试

TEST(RendererTest, CreateVertexBufferWithValidParams) {
    auto renderer = Renderer::create(GetValidRendererDescriptor());
    ASSERT_NE(renderer, nullptr);

    BufferDescriptor buffer_desc;
    buffer_desc.size = 1024; // 1KB缓冲区
    buffer_desc.usage = BufferUsage::Vertex;
    buffer_desc.memory_type = MemoryType::HostVisible;

    VertexFormat vertex_format;
    vertex_format.attributes = {
        {AttributeType::Position, Format::Float3},
        {AttributeType::UV, Format::Float2}
    };

    auto vertex_buffer = renderer->createVertexBuffer(buffer_desc, vertex_format);
    ASSERT_NE(vertex_buffer, nullptr);
    // 若Buffer类有查询接口,可进一步验证属性
    EXPECT_EQ(vertex_buffer->getSize(), 1024);
}

TEST(RendererTest, CreateVertexBufferWithZeroSize) {
    auto renderer = Renderer::create(GetValidRendererDescriptor());
    ASSERT_NE(renderer, nullptr);

    BufferDescriptor buffer_desc;
    buffer_desc.size = 0; // 无效的缓冲区大小
    buffer_desc.usage = BufferUsage::Vertex;

    VertexFormat vertex_format;
    // 配置合法的顶点格式

    auto vertex_buffer = renderer->createVertexBuffer(buffer_desc, vertex_format);
    ASSERT_EQ(vertex_buffer, nullptr); // 验证非法参数返回空
}

着色器创建测试

TEST(RendererTest, CreateShaderWithValidSource) {
    auto renderer = Renderer::create(GetValidRendererDescriptor());
    ASSERT_NE(renderer, nullptr);

    ShaderDescriptor shader_desc;
    shader_desc.type = ShaderType::Vertex;
    shader_desc.stage = ShaderStage::Vertex;

    // 合法的GLSL顶点着色器源码
    std::string_view source = R"(
        #version 450 core
        layout(location = 0) in vec3 aPos;
        void main() {
            gl_Position = vec4(aPos, 1.0);
        }
    )";

    auto shader = renderer->createShaderFromString(shader_desc, source);
    ASSERT_NE(shader, nullptr);
}

TEST(RendererTest, CreateShaderWithInvalidSource) {
    auto renderer = Renderer::create(GetValidRendererDescriptor());
    ASSERT_NE(renderer, nullptr);

    ShaderDescriptor shader_desc;
    shader_desc.type = ShaderType::Vertex;

    // 存在语法错误的源码
    std::string_view bad_source = R"(
        #version 450 core
        layout(location = 0) in vec3 aPos;
        void main() {
            gl_Position = vec4(aPos); // 缺少第四个分量参数
        }
    )";

    auto shader = renderer->createShaderFromString(shader_desc, bad_source);
    ASSERT_EQ(shader, nullptr); // 验证错误源码返回空
}

3. 窗口依赖资源测试(如SwapChain)

SwapChain依赖Window实例,测试时可使用离线窗口(无显示)或模拟窗口对象:

TEST(RendererTest, CreateSwapChainWithValidWindow) {
    auto renderer = Renderer::create(GetValidRendererDescriptor());
    ASSERT_NE(renderer, nullptr);

    // 创建无显示的测试窗口(例如GLFW的offscreen模式)
    Window test_window;
    test_window.create(800, 600, "Test Window", WindowMode::Offscreen);
    ASSERT_TRUE(test_window.isValid());

    SwapChainDescriptor sc_desc;
    sc_desc.width = 800;
    sc_desc.height = 600;
    sc_desc.format = Format::RGBA8Unorm;

    auto swap_chain = renderer->createSwapChain(sc_desc, test_window);
    ASSERT_NE(swap_chain, nullptr);
    EXPECT_EQ(swap_chain->getWidth(), 800);
}

4. 测试框架优化配置

  • 使用测试夹具:通过SetUp()和TearDown()方法复用Renderer实例创建逻辑,减少重复代码:
    class RendererTestFixture : public ::testing::Test {
    protected:
        void SetUp() override {
            renderer = Renderer::create(GetValidRendererDescriptor());
        }
    
        void TearDown() override {
            renderer.reset();
        }
    
        std::unique_ptr<Renderer> renderer;
    };
    
    // 基于夹具的测试案例
    TEST_F(RendererTestFixture, CreateCommandListSuccess) {
        ASSERT_NE(renderer, nullptr);
        CommandListDescriptor cmd_desc;
        auto cmd_list = renderer->createCommandList(cmd_desc);
        ASSERT_NE(cmd_list, nullptr);
    }
    
  • 链接依赖:测试项目需链接GoogleTest库、Renderer库以及OpenGL后端库,确保编译时能找到所有符号。
  • 资源清理:每个测试案例结束后主动释放GPU资源,避免状态污染或内存泄漏。

内容的提问来源于stack exchange,提问作者RafalMaziejuk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 17:14:51