使用模拟资源测试命令及处理操作系统信号
立即解锁
发布时间: 2025-09-14 00:08:01 阅读量: 3 订阅数: 38 AIGC 

### 使用模拟资源测试命令及处理操作系统信号
#### 1. 使用模拟资源测试命令
在测试外部命令时,通常可以直接执行这些命令,但有时直接在测试代码的机器上执行命令并不理想或不可行。这时可以在测试期间使用 Go 函数来模拟外部命令,这种方法还能使用 Go 代码模拟异常情况,如超时。
##### 1.1 准备工作
- **编辑 `timeoutStep.go` 文件**:
- 定义一个包变量 `command`,并将其初始化为 `exec.CommandContext` 函数。
```go
var command = exec.CommandContext
```
- 在 `execute` 方法中使用 `command` 变量创建 `exec.Cmd` 实例。
```go
func (s timeoutStep) execute() (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), s.timeout)
defer cancel()
cmd := command(ctx, s.exe, s.args...)
cmd.Dir = s.proj
if err := cmd.Run(); err != nil {
if ctx.Err() == context.DeadlineExceeded {
return "", &stepErr{
step: s.name,
msg: "failed time out",
cause: context.DeadlineExceeded,
}
}
return "", &stepErr{
step: s.name,
msg: "failed to execute",
cause: err,
}
}
return s.message, nil
}
```
##### 1.2 更新测试文件 `main_test.go`
- **导入必要的包**:
```go
import (
"bytes"
"context"
"errors"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"testing"
"time"
)
```
- **创建模拟函数**:
- `mockCmdContext` 函数用于模拟 `exec.CommandContext` 函数。
```go
func mockCmdContext(ctx context.Context, exe string, args ...string) *exec.Cmd {
cs := []string{"-test.run=TestHelperProcess"}
cs = append(cs, exe)
cs = append(cs, args...)
cmd := exec.CommandContext(ctx, os.Args[0], cs...)
cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
return cmd
}
```
- `mockCmdTimeout` 函数用于模拟超时命令。
```go
func mockCmdTimeout(ctx context.Context, exe string, args ...string) *exec.Cmd {
cmd := mockCmdContext(ctx, exe, args...)
cmd.Env = append(cmd.Env, "GO_HELPER_TIMEOUT=1")
return cmd
}
```
- `TestHelperProcess` 函数用于模拟命令行为。
```go
func TestHelperProcess(t *testing.T) {
if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
return
}
if os.Getenv("GO_HELPER_TIMEOUT") == "1" {
time.Sleep(15 * time.Second)
}
if os.Args[2] == "git" {
fmt.Fprintln(os.Stdout, "Everything up-to-date")
os.Exit(0)
}
os.Exit(1)
}
```
- **更新 `TestRun` 测试函数**:
- 移除跳过测试的代码。
- 添加 `mockCmd` 字段到测试用例中。
- 根据需要设置 `mockCmd` 字段。
```go
func TestRun(t *testing.T) {
var testCases = []struct {
name string
proj string
out string
expErr error
setupGit bool
mockCmd func (ctx context.Context, name string, arg ...string) *exec.Cmd
}{
{name: "success", proj: "./testdata/tool/",
out: "Go Build: SUCCESS\n" +
"Go Test: SUCCESS\n" +
"Gofmt: SUCCESS\n" +
"Git Push: SUCCESS\n",
expErr: nil,
setupGit: true,
mockCmd: nil},
{name: "successMock", proj: "./testdata/tool/",
out: "Go Build: SUCCESS\n" +
"Go Test: SUCCESS\n" +
"Gofmt: SUCCESS\n" +
"Git Push: SUCCESS\n",
expErr: nil,
setupGit: false,
mockCmd: mockCmdContext},
{name: "fail", proj: "./testdata/toolErr",
out: "",
expErr: &stepErr{step: "go build"},
setupGit: false,
mockCmd: nil},
{name: "failFormat", proj: "./testdata/toolFmtErr",
out: "",
expErr: &stepErr{step: "go fmt"},
setupGit: false,
mockCmd: nil},
{name: "failTimeout", proj: "./testdata/tool",
out: "",
expErr: context.DeadlineExceeded,
setupGit: false,
mockCmd: mockCmdTimeout},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
if tc.setupGit {
_, err := exec.LookPath("git")
if err != nil {
t.Skip("Git not installed. Skipping test.")
}
cleanup := setupGit(t, tc.proj)
defer cleanup()
}
if tc.mockCmd != nil {
command = tc.mockCmd
}
var out bytes.Buffer
err := run(tc.proj, &out)
if tc.expErr != nil {
if err == nil {
t.Errorf("Expected error: %q. Got 'nil' instead.", tc.expErr)
return
}
if !errors.Is(err, tc.expErr) {
t.Errorf("Expected error: %q. Got %q.", tc.expErr, err)
}
return
}
if err != nil {
t.Errorf("Unexpected error: %q", err)
}
if out.String() != tc.out {
t.Errorf("Expected output: %q. Got %q", tc.out, out.String())
}
})
}
}
```
##### 1.3 执行测试
保存 `main_test.go` 文件并执行测试:
```sh
go test -v
```
#### 2. 处理操作系统信号
在 Unix/Linux 操作系统中,信号常用于在运行的进程之间传递事件。默认情况下,程序收到中断信号会立即停止执行,这可能导致数据丢失等问题。因此,需要正确处理信号,让程序有机会清理资源、保存数据并干净地退出。
##### 2.1 定义错误值
编辑 `errors.go` 文件,添加 `ErrSignal` 错误值。
```go
var (
ErrValidation = errors.New("Validation failed")
ErrSignal = errors.New("Received
```
0
0
复制全文
相关推荐









