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

Go语言映射对象测试:如何隔离Core层完成API单元测试

如何Mock依赖完成Go语言API包的单元测试

问题背景

我正在尝试为Go语言代码编写单元测试,发现方法Mock的相关内容难度较高。以下是我的代码示例,希望能得到帮助:

API包代码

package api

import (
    "fmt"
    "core"
)

type createUserDTO struct {
    Id string
}

func ApiMethod() {
    fmt.Println("some incoming api call wit user")
    incomingUserData := &createUserDTO{Id: "testId"}
    mapedUser := incomingUserData.mapUser()
    mapedUser.Create()
}

func (createUserDTO *createUserDTO) mapUser() core.User {
    return &core.UserCore{Id: createUserDTO.Id} // 注:原代码存在笔误,core包中实际是CoreUser
}

Core包代码

package core

import (
    "fmt"
)

type CoreUser struct{ Id string }

type User interface {
    Create()
}

func (user CoreUser) Create() {
    fmt.Println("Do Stuff")
}

我的问题是:如何在不测试core包(尤其是其中的Create()方法)的前提下,完成api包中所有公共方法的单元测试?

解决方案

核心问题是当前API包的代码硬编码依赖了core包的具体实现,mapUser方法直接返回core.CoreUser实例,导致无法替换这个依赖来MockCreate方法。我们需要通过依赖注入的方式,让User实例的创建逻辑可被替换。

方法一:通过全局工厂函数实现依赖替换

改造API包代码,引入全局工厂函数来创建User实例,这样测试时可以替换该函数:

package api

import (
    "fmt"
    "core"
)

type createUserDTO struct {
    Id string
}

// UserFactory 定义创建User实例的工厂类型
type UserFactory func(id string) core.User

// 默认工厂函数,返回真实的core.CoreUser
var userFactory UserFactory = func(id string) core.User {
    return &core.CoreUser{Id: id}
}

func ApiMethod() {
    fmt.Println("some incoming api call wit user")
    incomingUserData := &createUserDTO{Id: "testId"}
    mapedUser := incomingUserData.mapUser()
    mapedUser.Create()
}

func (dto *createUserDTO) mapUser() core.User {
    return userFactory(dto.Id)
}

编写单元测试,定义Mock的User实现并替换工厂函数:

package api

import (
    "testing"
    "core"
)

// MockUser 实现core.User接口的Mock结构体,用于跟踪Create方法调用
type MockUser struct {
    CreateCalled bool
}

func (m *MockUser) Create() {
    m.CreateCalled = true
}

func TestApiMethod(t *testing.T) {
    // 保存原工厂函数,测试结束后恢复
    originalFactory := userFactory
    defer func() { userFactory = originalFactory }()

    mockUser := &MockUser{}
    // 替换工厂函数,返回Mock实例并验证参数
    userFactory = func(id string) core.User {
        if id != "testId" {
            t.Errorf("期望ID为'testId',实际得到'%s'", id)
        }
        return mockUser
    }

    // 调用待测试的ApiMethod
    ApiMethod()

    // 验证Create方法是否被调用
    if !mockUser.CreateCalled {
        t.Error("User的Create方法未被调用")
    }
}

方法二:通过结构体封装实现依赖注入(推荐)

这种方式更符合Go的最佳实践,避免全局变量,通过结构体封装依赖:

package api

import (
    "fmt"
    "core"
)

type API struct {
    userFactory UserFactory
}

type UserFactory func(id string) core.User

// NewAPI 创建API实例,允许传入自定义工厂函数,默认使用真实实现
func NewAPI(factory UserFactory) *API {
    if factory == nil {
        factory = func(id string) core.User {
            return &core.CoreUser{Id: id}
        }
    }
    return &API{userFactory: factory}
}

type createUserDTO struct {
    Id string
}

func (a *API) ApiMethod() {
    fmt.Println("some incoming api call wit user")
    incomingUserData := &createUserDTO{Id: "testId"}
    mapedUser := incomingUserData.mapUser(a.userFactory)
    mapedUser.Create()
}

func (dto *createUserDTO) mapUser(factory UserFactory) core.User {
    return factory(dto.Id)
}

对应的单元测试代码:

package api

import (
    "testing"
    "core"
)

type MockUser struct {
    CreateCalled bool
}

func (m *MockUser) Create() {
    m.CreateCalled = true
}

func TestApiMethod(t *testing.T) {
    mockUser := &MockUser{}
    // 创建API实例时传入Mock工厂函数
    api := NewAPI(func(id string) core.User {
        if id != "testId" {
            t.Errorf("期望ID为'testId',实际得到'%s'", id)
        }
        return mockUser
    })

    api.ApiMethod()

    if !mockUser.CreateCalled {
        t.Error("User的Create方法未被调用")
    }
}

总结

两种方法的核心都是解除API包对core包具体实现的硬依赖,通过可替换的工厂函数或依赖注入,让测试时能使用Mock的User实例,从而只验证API包的逻辑:

  • 验证mapUser是否正确传递了ID参数
  • 验证Create方法是否被正确调用

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 11:01:57