golang简单快照测试框架插件库cupaloy的使用

Golang简单快照测试框架插件库cupaloy的使用

Mascot

概述

cupaloy是一个非常简单的Go快照测试库:它会为你的测试输出创建快照,并与提交的测试快照进行比较。如果值不匹配,测试将会失败。

你不需要手动管理快照文件:只需在测试中使用cupaloy.SnapshotT(t, value)函数,cupaloy会自动找到相关的快照文件(基于测试名称)并与给定值进行比较。

使用方法

编写测试

首先,编写一个生成输出的测试用例,并将此输出传递给cupaloy.SnapshotT

func TestParsing(t *testing.T) {
    ast := ParseFile("test_input")

    // 检查结果是否与上次更新快照时相同
    // 如果结果发生变化(例如因为解析器的行为发生了变化)
    // 测试将失败并显示包含更改差异的错误
    cupaloy.SnapshotT(t, ast)
}

第一次运行此测试时,将自动创建一个快照(使用github.com/davecgh/go-spew包)。

更新快照

当你的软件行为发生变化导致快照更改时,此测试将开始失败,并显示一个错误,显示新旧快照之间的差异。一旦你确认新快照是正确的(而不是意外更改),你可以通过设置UPDATE_SNAPSHOTS环境变量并重新运行测试来保存新快照:

UPDATE_SNAPSHOTS=true go test ./...

这将更新所有快照文件以反映代码的当前输出。

支持的格式

测试输出的快照是使用github.com/davecgh/go-spew包生成的,该包使用反射深度漂亮打印你的测试结果,因此几乎支持所有基本类型(从简单的字符串、切片和映射到深度嵌套的结构体)而不会出现问题。唯一不能完全漂亮打印的类型是函数和通道。

你的测试输出的最重要属性是它是确定性的:如果你的输出包含时间戳或其他每次运行都会更改的字段,那么cupaloy会检测到这一点并导致测试失败。

更多示例

表驱动测试

var testCases = map[string][]string{
    "TestCaseOne": []string{......},
    "AnotherTestCase": []string{......},
    ....
}

func TestCases(t *testing.T) {
    for testName, args := range testCases {
        t.Run(testName, func(t *testing.T) {
            result := functionUnderTest(args...)
            cupaloy.SnapshotT(t, result)
        })
    }
}

更改输出目录

func TestSubdirectory(t *testing.T) {
    result := someFunction()
    snapshotter := cupaloy.New(cupaloy.SnapshotSubdirectory("testdata"))
    err := snapshotter.Snapshot(result)
    if err != nil {
        t.Fatalf("error: %s", err)
    }
}

调试

Windows注意事项

需要注意的是,Windows上的git可能配置为在检出时将\n替换为\r\n(在GitHub Actions上就是这种情况)。在这种情况下,快照看起来相同,但测试会失败。请确保git在所有地方都正确配置。


更多关于golang简单快照测试框架插件库cupaloy的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang简单快照测试框架插件库cupaloy的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang 快照测试框架 Cupaloy 使用指南

Cupaloy 是一个简单易用的 Golang 快照测试框架,它可以帮助你快速验证函数的输出是否符合预期。下面我将详细介绍如何使用 Cupaloy 进行快照测试。

安装 Cupaloy

首先安装 Cupaloy 包:

go get github.com/bradleyjkemp/cupaloy

基本用法

简单示例

package main

import (
	"testing"
	
	"github.com/bradleyjkemp/cupaloy"
)

func TestStringOutput(t *testing.T) {
	result := "Hello, World!"
	cupaloy.SnapshotT(t, result)
}

func TestStructOutput(t *testing.T) {
	type Person struct {
		Name string
		Age  int
	}
	
	result := Person{
		Name: "Alice",
		Age:  30,
	}
	
	cupaloy.SnapshotT(t, result)
}

工作原理

  1. 第一次运行测试时,Cupaloy 会创建快照文件(默认在 __snapshots__ 目录)
  2. 后续测试运行时,会将输出与快照文件中的内容进行比较
  3. 如果输出不匹配,测试会失败并显示差异

高级用法

自定义快照目录

func TestWithCustomSnapshotDir(t *testing.T) {
	cupaloy := cupaloy.New(cupaloy.SnapshotFileExtension(".snap"))
	
	result := map[string]interface{}{
		"key": "value",
		"num": 42,
	}
	
	cupaloy.SnapshotT(t, result)
}

忽略某些字段

func TestWithFieldIgnores(t *testing.T) {
	type Config struct {
		APIKey    string
		Endpoint  string
		Timeout   int
		CreatedAt time.Time
	}
	
	config := Config{
		APIKey:    "secret-key",
		Endpoint:  "https://api.example.com",
		Timeout:   30,
		CreatedAt: time.Now(),
	}
	
	// 忽略 APIKey 和 CreatedAt 字段
	cupaloy.New(
		cupaloy.FieldsToIgnore("APIKey", "CreatedAt"),
	).SnapshotT(t, config)
}

自定义比较器

func TestWithCustomComparator(t *testing.T) {
	type Product struct {
		ID        string
		Name      string
		Price     float64
		UpdatedAt time.Time
	}
	
	product := Product{
		ID:        "123",
		Name:      "Laptop",
		Price:     999.99,
		UpdatedAt: time.Now(),
	}
	
	// 自定义比较器忽略时间字段
	cupaloy.New(
		cupaloy.SnapshotSubset(func(p Product) interface{} {
			return struct {
				ID    string
				Name  string
				Price float64
			}{
				ID:    p.ID,
				Name:  p.Name,
				Price: p.Price,
			}
		}),
	).SnapshotT(t, product)
}

更新快照

当你的代码变更导致快照需要更新时:

  1. 运行测试时会显示差异
  2. 删除旧的快照文件或使用 -update 标志重新生成:
    go test -v ./... -update
    

最佳实践

  1. 快照测试适合用于

    • 复杂数据结构的输出验证
    • 模板渲染结果
    • API响应格式
    • CLI工具输出
  2. 不适合用于

    • 包含随机值或时间戳的输出(除非忽略这些字段)
    • 需要精确断言特定值的场景
  3. 维护建议

    • 定期检查快照测试失败是否是预期的变更
    • 不要盲目更新快照,要确认变更是否符合预期
    • 为快照测试添加有意义的描述

完整示例

package main

import (
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"
	
	"github.com/bradleyjkemp/cupaloy"
)

func TestAPIHandler(t *testing.T) {
	// 创建测试HTTP服务器
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		response := map[string]interface{}{
			"status": "success",
			"data": map[string]interface{}{
				"user": map[string]interface{}{
					"id":    "user-123",
					"name":  "John Doe",
					"email": "john@example.com",
				},
			},
		}
		
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(response)
	})
	
	// 创建测试请求
	req := httptest.NewRequest("GET", "/user", nil)
	w := httptest.NewRecorder()
	handler.ServeHTTP(w, req)
	
	// 解析响应
	var response map[string]interface{}
	if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil {
		t.Fatal(err)
	}
	
	// 使用快照测试验证响应
	cupaloy.New(
		cupaloy.FieldsToIgnore("user.id"), // 忽略ID字段
	).SnapshotT(t, response)
}

Cupaloy 提供了一种简单高效的方式来验证代码输出,特别适合那些输出结构复杂但相对稳定的场景。通过合理使用,可以显著减少测试代码的编写和维护工作量。

回到顶部