mirror of
https://github.com/jimeh/go-mocktesting.git
synced 2026-02-18 19:46:38 +00:00
Merge pull request #1 from jimeh/initial-implementation
This commit is contained in:
87
.github/workflows/ci.yml
vendored
Normal file
87
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
---
|
||||
name: CI
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
with:
|
||||
version: v1.43
|
||||
env:
|
||||
VERBOSE: "true"
|
||||
|
||||
tidy:
|
||||
name: Tidy
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.15
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
- name: Check if mods are tidy
|
||||
run: make check-tidy
|
||||
|
||||
cov:
|
||||
name: Coverage
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
- name: Publish coverage
|
||||
uses: paambaati/codeclimate-action@v2.7.4
|
||||
env:
|
||||
VERBOSE: "true"
|
||||
GOMAXPROCS: 4
|
||||
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
|
||||
with:
|
||||
coverageCommand: make cov
|
||||
prefix: github.com/${{ github.repository }}
|
||||
coverageLocations: |
|
||||
${{ github.workspace }}/coverage.out:gocov
|
||||
|
||||
test:
|
||||
name: Test
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-latest
|
||||
- macos-latest
|
||||
- windows-latest
|
||||
go_version:
|
||||
- "1.15"
|
||||
- "1.16"
|
||||
- "1.17"
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.go_version }}
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
- name: Run tests
|
||||
run: go test -v -count=1 -race ./...
|
||||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
bin/*
|
||||
coverage.out
|
||||
96
.golangci.yml
Normal file
96
.golangci.yml
Normal file
@@ -0,0 +1,96 @@
|
||||
linters-settings:
|
||||
funlen:
|
||||
lines: 100
|
||||
statements: 150
|
||||
gocyclo:
|
||||
min-complexity: 20
|
||||
golint:
|
||||
min-confidence: 0
|
||||
govet:
|
||||
check-shadowing: true
|
||||
enable-all: true
|
||||
disable:
|
||||
- fieldalignment
|
||||
lll:
|
||||
line-length: 80
|
||||
tab-width: 4
|
||||
maligned:
|
||||
suggest-new: true
|
||||
misspell:
|
||||
locale: US
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- asciicheck
|
||||
- bodyclose
|
||||
- deadcode
|
||||
- depguard
|
||||
- durationcheck
|
||||
- errcheck
|
||||
- errorlint
|
||||
- exhaustive
|
||||
- exportloopref
|
||||
- funlen
|
||||
- gochecknoinits
|
||||
- goconst
|
||||
- gocritic
|
||||
- gocyclo
|
||||
- godot
|
||||
- gofumpt
|
||||
- goimports
|
||||
- goprintffuncname
|
||||
- gosec
|
||||
- gosimple
|
||||
- govet
|
||||
- importas
|
||||
- ineffassign
|
||||
- lll
|
||||
- misspell
|
||||
- nakedret
|
||||
- nilerr
|
||||
- nlreturn
|
||||
- noctx
|
||||
- nolintlint
|
||||
- prealloc
|
||||
- predeclared
|
||||
- revive
|
||||
- rowserrcheck
|
||||
- sqlclosecheck
|
||||
- staticcheck
|
||||
- structcheck
|
||||
- tparallel
|
||||
- typecheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- unused
|
||||
- varcheck
|
||||
- wastedassign
|
||||
- whitespace
|
||||
|
||||
issues:
|
||||
exclude:
|
||||
- Using the variable on range scope `tt` in function literal
|
||||
- Using the variable on range scope `tc` in function literal
|
||||
exclude-rules:
|
||||
- path: "_test\\.go"
|
||||
linters:
|
||||
- funlen
|
||||
- dupl
|
||||
- source: "^//go:generate "
|
||||
linters:
|
||||
- lll
|
||||
- source: "`json:"
|
||||
linters:
|
||||
- lll
|
||||
- source: "`xml:"
|
||||
linters:
|
||||
- lll
|
||||
- source: "`yaml:"
|
||||
linters:
|
||||
- lll
|
||||
|
||||
run:
|
||||
timeout: 2m
|
||||
allow-parallel-runners: true
|
||||
modules-download-mode: readonly
|
||||
20
LICENSE
Normal file
20
LICENSE
Normal file
@@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2021 Jim Myhrberg
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
205
Makefile
Normal file
205
Makefile
Normal file
@@ -0,0 +1,205 @@
|
||||
GOMODNAME := $(shell grep 'module' go.mod | sed -e 's/^module //')
|
||||
SOURCES := $(shell find . -name "*.go" -or -name "go.mod" -or -name "go.sum" \
|
||||
-or -name "Makefile")
|
||||
|
||||
# Verbose output
|
||||
ifdef VERBOSE
|
||||
V = -v
|
||||
endif
|
||||
|
||||
#
|
||||
# Environment
|
||||
#
|
||||
|
||||
BINDIR := bin
|
||||
TOOLDIR := $(BINDIR)/tools
|
||||
|
||||
# Global environment variables for all targets
|
||||
SHELL ?= /bin/bash
|
||||
SHELL := env \
|
||||
GO111MODULE=on \
|
||||
GOBIN=$(CURDIR)/$(TOOLDIR) \
|
||||
CGO_ENABLED=1 \
|
||||
PATH='$(CURDIR)/$(BINDIR):$(CURDIR)/$(TOOLDIR):$(PATH)' \
|
||||
$(SHELL)
|
||||
|
||||
#
|
||||
# Defaults
|
||||
#
|
||||
|
||||
# Default target
|
||||
.DEFAULT_GOAL := test
|
||||
|
||||
#
|
||||
# Tools
|
||||
#
|
||||
|
||||
TOOLS += $(TOOLDIR)/gobin
|
||||
$(TOOLDIR)/gobin:
|
||||
GO111MODULE=off go get -u github.com/myitcv/gobin
|
||||
|
||||
# external tool
|
||||
define tool # 1: binary-name, 2: go-import-path
|
||||
TOOLS += $(TOOLDIR)/$(1)
|
||||
|
||||
$(TOOLDIR)/$(1): $(TOOLDIR)/gobin Makefile
|
||||
gobin $(V) "$(2)"
|
||||
endef
|
||||
|
||||
$(eval $(call tool,godoc,golang.org/x/tools/cmd/godoc))
|
||||
$(eval $(call tool,gofumpt,mvdan.cc/gofumpt))
|
||||
$(eval $(call tool,goimports,golang.org/x/tools/cmd/goimports))
|
||||
$(eval $(call tool,golangci-lint,github.com/golangci/golangci-lint/cmd/golangci-lint@v1.43))
|
||||
$(eval $(call tool,gomod,github.com/Helcaraxan/gomod))
|
||||
|
||||
.PHONY: tools
|
||||
tools: $(TOOLS)
|
||||
|
||||
#
|
||||
# Development
|
||||
#
|
||||
|
||||
BENCH ?= .
|
||||
TESTARGS ?=
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f $(TOOLS)
|
||||
rm -f ./coverage.out ./go.mod.tidy-check ./go.sum.tidy-check
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
go test $(V) -count=1 -race $(TESTARGS) ./...
|
||||
|
||||
.PHONY: test-deps
|
||||
test-deps:
|
||||
go test all
|
||||
|
||||
.PHONY: lint
|
||||
lint: $(TOOLDIR)/golangci-lint
|
||||
golangci-lint $(V) run
|
||||
|
||||
.PHONY: format
|
||||
format: $(TOOLDIR)/goimports $(TOOLDIR)/gofumpt
|
||||
goimports -w . && gofumpt -w .
|
||||
|
||||
.SILENT: bench
|
||||
.PHONY: bench
|
||||
bench:
|
||||
go test $(V) -count=1 -bench=$(BENCH) $(TESTARGS) ./...
|
||||
|
||||
.PHONY: golden-update
|
||||
golden-update:
|
||||
GOLDEN_UPDATE=1 $(MAKE) test
|
||||
|
||||
.PHONY: golden-clean
|
||||
golden-clean:
|
||||
find . -type f -name '*.golden' -path '*/testdata/*' -delete
|
||||
find . -type d -empty -path '*/testdata/*' -delete
|
||||
|
||||
.PHONY: golden-regen
|
||||
golden-regen: golden-clean golden-update
|
||||
|
||||
#
|
||||
# Code Generation
|
||||
#
|
||||
|
||||
.PHONY: generate
|
||||
generate:
|
||||
go generate ./...
|
||||
|
||||
.PHONY: check-generate
|
||||
check-generate:
|
||||
$(eval CHKDIR := $(shell mktemp -d))
|
||||
cp -av . "$(CHKDIR)"
|
||||
make -C "$(CHKDIR)/" generate
|
||||
( diff -rN . "$(CHKDIR)" && rm -rf "$(CHKDIR)" ) || \
|
||||
( rm -rf "$(CHKDIR)" && exit 1 )
|
||||
|
||||
#
|
||||
# Coverage
|
||||
#
|
||||
|
||||
.PHONY: cov
|
||||
cov: coverage.out
|
||||
|
||||
.PHONY: cov-html
|
||||
cov-html: coverage.out
|
||||
go tool cover -html=./coverage.out
|
||||
|
||||
.PHONY: cov-func
|
||||
cov-func: coverage.out
|
||||
go tool cover -func=./coverage.out
|
||||
|
||||
coverage.out: $(SOURCES)
|
||||
go test $(V) -covermode=count -coverprofile=./coverage.out ./...
|
||||
|
||||
#
|
||||
# Dependencies
|
||||
#
|
||||
|
||||
.PHONY: deps
|
||||
deps:
|
||||
go mod download
|
||||
|
||||
.PHONY: deps-update
|
||||
deps-update:
|
||||
go get -u -t ./...
|
||||
|
||||
.PHONY: deps-analyze
|
||||
deps-analyze: $(TOOLDIR)/gomod
|
||||
gomod analyze
|
||||
|
||||
.PHONY: tidy
|
||||
tidy:
|
||||
go mod tidy $(V)
|
||||
|
||||
.PHONY: verify
|
||||
verify:
|
||||
go mod verify
|
||||
|
||||
.SILENT: check-tidy
|
||||
.PHONY: check-tidy
|
||||
check-tidy:
|
||||
cp go.mod go.mod.tidy-check
|
||||
cp go.sum go.sum.tidy-check
|
||||
go mod tidy
|
||||
( \
|
||||
diff go.mod go.mod.tidy-check && \
|
||||
diff go.sum go.sum.tidy-check && \
|
||||
rm -f go.mod go.sum && \
|
||||
mv go.mod.tidy-check go.mod && \
|
||||
mv go.sum.tidy-check go.sum \
|
||||
) || ( \
|
||||
rm -f go.mod go.sum && \
|
||||
mv go.mod.tidy-check go.mod && \
|
||||
mv go.sum.tidy-check go.sum; \
|
||||
exit 1 \
|
||||
)
|
||||
|
||||
#
|
||||
# Documentation
|
||||
#
|
||||
|
||||
# Serve docs
|
||||
.PHONY: docs
|
||||
docs: $(TOOLDIR)/godoc
|
||||
$(info serviing docs on http://127.0.0.1:6060/pkg/$(GOMODNAME)/)
|
||||
@godoc -http=127.0.0.1:6060
|
||||
|
||||
#
|
||||
# Release
|
||||
#
|
||||
|
||||
.PHONY: new-version
|
||||
new-version: check-npx
|
||||
npx standard-version
|
||||
|
||||
.PHONY: next-version
|
||||
next-version: check-npx
|
||||
npx standard-version --dry-run
|
||||
|
||||
.PHONY: check-npx
|
||||
check-npx:
|
||||
$(if $(shell which npx),,\
|
||||
$(error No npx found in PATH, please install NodeJS))
|
||||
146
README.md
146
README.md
@@ -1 +1,145 @@
|
||||
# mocktesting
|
||||
<h1 align="center">
|
||||
go-mocktesting
|
||||
</h1>
|
||||
|
||||
<p align="center">
|
||||
<strong>
|
||||
Mock *testing.T for the purpose of testing test helpers.
|
||||
</strong>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://pkg.go.dev/github.com/jimeh/go-mocktesting"><img src="https://img.shields.io/badge/%E2%80%8B-reference-387b97.svg?logo=go&logoColor=white" alt="Go Reference"></a>
|
||||
<a href="https://github.com/jimeh/go-mocktesting/actions"><img src="https://img.shields.io/github/workflow/status/jimeh/go-mocktesting/CI.svg?logo=github" alt="Actions Status"></a>
|
||||
<a href="https://codeclimate.com/github/jimeh/go-mocktesting"><img src="https://img.shields.io/codeclimate/coverage/jimeh/go-mocktesting.svg?logo=code%20climate" alt="Coverage"></a>
|
||||
<a href="https://github.com/jimeh/go-mocktesting/issues"><img src="https://img.shields.io/github/issues-raw/jimeh/go-mocktesting.svg?style=flat&logo=github&logoColor=white" alt="GitHub issues"></a>
|
||||
<a href="https://github.com/jimeh/go-mocktesting/pulls"><img src="https://img.shields.io/github/issues-pr-raw/jimeh/go-mocktesting.svg?style=flat&logo=github&logoColor=white" alt="GitHub pull requests"></a>
|
||||
<a href="https://github.com/jimeh/go-mocktesting/blob/master/LICENSE"><img src="https://img.shields.io/github/license/jimeh/go-mocktesting.svg?style=flat" alt="License Status"></a>
|
||||
</p>
|
||||
|
||||
## Import
|
||||
|
||||
```go
|
||||
import "github.com/jimeh/go-mocktesting"
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
func Example_basic() {
|
||||
assertTrue := func(t testing.TB, v bool) {
|
||||
if v != true {
|
||||
t.Error("expected false to be true")
|
||||
}
|
||||
}
|
||||
|
||||
mt := mocktesting.NewT("TestMyBoolean1")
|
||||
assertTrue(mt, true)
|
||||
fmt.Printf("Name: %s\n", mt.Name())
|
||||
fmt.Printf("Failed: %+v\n", mt.Failed())
|
||||
fmt.Printf("Aborted: %+v\n", mt.Aborted())
|
||||
|
||||
mt = mocktesting.NewT("TestMyBoolean2")
|
||||
assertTrue(mt, false)
|
||||
fmt.Printf("Name: %s\n", mt.Name())
|
||||
fmt.Printf("Failed: %+v\n", mt.Failed())
|
||||
fmt.Printf("Aborted: %+v\n", mt.Aborted())
|
||||
fmt.Printf("Output: %s\n", strings.Join(mt.Output(), ""))
|
||||
|
||||
// Output:
|
||||
// Name: TestMyBoolean1
|
||||
// Failed: false
|
||||
// Aborted: false
|
||||
// Name: TestMyBoolean2
|
||||
// Failed: true
|
||||
// Aborted: false
|
||||
// Output: expected false to be true
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
func Example_fatal() {
|
||||
requireTrue := func(t testing.TB, v bool) {
|
||||
if v != true {
|
||||
t.Fatal("expected false to be true")
|
||||
}
|
||||
}
|
||||
|
||||
mt := mocktesting.NewT("TestMyBoolean1")
|
||||
requireTrue(mt, true)
|
||||
fmt.Printf("Name: %s\n", mt.Name())
|
||||
fmt.Printf("Failed: %+v\n", mt.Failed())
|
||||
fmt.Printf("Aborted: %+v\n", mt.Aborted())
|
||||
|
||||
mt = mocktesting.NewT("TestMyBoolean2")
|
||||
mocktesting.Go(func() {
|
||||
requireTrue(mt, false)
|
||||
fmt.Println("This is never executed.")
|
||||
})
|
||||
fmt.Printf("Name: %s\n", mt.Name())
|
||||
fmt.Printf("Failed: %+v\n", mt.Failed())
|
||||
fmt.Printf("Aborted: %+v\n", mt.Aborted())
|
||||
fmt.Printf("Output: %s\n", strings.Join(mt.Output(), ""))
|
||||
|
||||
// Output:
|
||||
// Name: TestMyBoolean1
|
||||
// Failed: false
|
||||
// Aborted: false
|
||||
// Name: TestMyBoolean2
|
||||
// Failed: true
|
||||
// Aborted: true
|
||||
// Output: expected false to be true
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
func Example_subtests() {
|
||||
requireTrue := func(t testing.TB, v bool) {
|
||||
if v != true {
|
||||
t.Fatal("expected false to be true")
|
||||
}
|
||||
}
|
||||
|
||||
mt := mocktesting.NewT("TestMyBoolean")
|
||||
mt.Run("true", func(t testing.TB) {
|
||||
requireTrue(t, true)
|
||||
})
|
||||
fmt.Printf("Name: %s\n", mt.Name())
|
||||
fmt.Printf("Failed: %+v\n", mt.Failed())
|
||||
fmt.Printf("Sub1-Name: %s\n", mt.Subtests()[0].Name())
|
||||
fmt.Printf("Sub1-Failed: %+v\n", mt.Subtests()[0].Failed())
|
||||
fmt.Printf("Sub1-Aborted: %+v\n", mt.Subtests()[0].Aborted())
|
||||
|
||||
mt.Run("false", func(t testing.TB) {
|
||||
requireTrue(t, false)
|
||||
fmt.Println("This is never executed.")
|
||||
})
|
||||
fmt.Printf("Failed: %+v\n", mt.Failed())
|
||||
fmt.Printf("Sub2-Name: %s\n", mt.Subtests()[1].Name())
|
||||
fmt.Printf("Sub2-Failed: %+v\n", mt.Subtests()[1].Failed())
|
||||
fmt.Printf("Sub2-Aborted: %+v\n", mt.Subtests()[1].Aborted())
|
||||
fmt.Printf("Sub2-Output: %s\n", strings.Join(mt.Subtests()[1].Output(), ""))
|
||||
|
||||
// Output:
|
||||
// Name: TestMyBoolean
|
||||
// Failed: false
|
||||
// Sub1-Name: TestMyBoolean/true
|
||||
// Sub1-Failed: false
|
||||
// Sub1-Aborted: false
|
||||
// Failed: true
|
||||
// Sub2-Name: TestMyBoolean/false
|
||||
// Sub2-Failed: true
|
||||
// Sub2-Aborted: true
|
||||
// Sub2-Output: expected false to be true
|
||||
}
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
Please see the
|
||||
[Go Reference](https://pkg.go.dev/github.com/jimeh/go-mocktesting#section-documentation)
|
||||
for documentation and examples.
|
||||
|
||||
## License
|
||||
|
||||
[MIT](https://github.com/jimeh/go-mocktesting/blob/main/LICENSE)
|
||||
|
||||
189
example_test.go
Normal file
189
example_test.go
Normal file
@@ -0,0 +1,189 @@
|
||||
package mocktesting_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/jimeh/go-mocktesting"
|
||||
)
|
||||
|
||||
func Example_basic() {
|
||||
assertTrue := func(t testing.TB, v bool) {
|
||||
if v != true {
|
||||
t.Error("expected false to be true")
|
||||
}
|
||||
}
|
||||
|
||||
mt := mocktesting.NewT("TestMyBoolean1")
|
||||
assertTrue(mt, true)
|
||||
fmt.Printf("Name: %s\n", mt.Name())
|
||||
fmt.Printf("Failed: %+v\n", mt.Failed())
|
||||
fmt.Printf("Aborted: %+v\n", mt.Aborted())
|
||||
|
||||
mt = mocktesting.NewT("TestMyBoolean2")
|
||||
assertTrue(mt, false)
|
||||
fmt.Printf("Name: %s\n", mt.Name())
|
||||
fmt.Printf("Failed: %+v\n", mt.Failed())
|
||||
fmt.Printf("Aborted: %+v\n", mt.Aborted())
|
||||
fmt.Printf("Output: %s\n", strings.Join(mt.Output(), ""))
|
||||
|
||||
// Output:
|
||||
// Name: TestMyBoolean1
|
||||
// Failed: false
|
||||
// Aborted: false
|
||||
// Name: TestMyBoolean2
|
||||
// Failed: true
|
||||
// Aborted: false
|
||||
// Output: expected false to be true
|
||||
}
|
||||
|
||||
func Example_fatal() {
|
||||
requireTrue := func(t testing.TB, v bool) {
|
||||
if v != true {
|
||||
t.Fatal("expected false to be true")
|
||||
}
|
||||
}
|
||||
|
||||
mt := mocktesting.NewT("TestMyBoolean1")
|
||||
requireTrue(mt, true)
|
||||
fmt.Printf("Name: %s\n", mt.Name())
|
||||
fmt.Printf("Failed: %+v\n", mt.Failed())
|
||||
fmt.Printf("Aborted: %+v\n", mt.Aborted())
|
||||
|
||||
mt = mocktesting.NewT("TestMyBoolean2")
|
||||
mocktesting.Go(func() {
|
||||
requireTrue(mt, false)
|
||||
fmt.Println("This is never executed.")
|
||||
})
|
||||
fmt.Printf("Name: %s\n", mt.Name())
|
||||
fmt.Printf("Failed: %+v\n", mt.Failed())
|
||||
fmt.Printf("Aborted: %+v\n", mt.Aborted())
|
||||
fmt.Printf("Output: %s\n", strings.Join(mt.Output(), ""))
|
||||
|
||||
// Output:
|
||||
// Name: TestMyBoolean1
|
||||
// Failed: false
|
||||
// Aborted: false
|
||||
// Name: TestMyBoolean2
|
||||
// Failed: true
|
||||
// Aborted: true
|
||||
// Output: expected false to be true
|
||||
}
|
||||
|
||||
func Example_subtests() {
|
||||
requireTrue := func(t testing.TB, v bool) {
|
||||
if v != true {
|
||||
t.Fatal("expected false to be true")
|
||||
}
|
||||
}
|
||||
|
||||
mt := mocktesting.NewT("TestMyBoolean")
|
||||
mt.Run("true", func(t testing.TB) {
|
||||
requireTrue(t, true)
|
||||
})
|
||||
fmt.Printf("Name: %s\n", mt.Name())
|
||||
fmt.Printf("Failed: %+v\n", mt.Failed())
|
||||
fmt.Printf("Sub1-Name: %s\n", mt.Subtests()[0].Name())
|
||||
fmt.Printf("Sub1-Failed: %+v\n", mt.Subtests()[0].Failed())
|
||||
fmt.Printf("Sub1-Aborted: %+v\n", mt.Subtests()[0].Aborted())
|
||||
|
||||
mt.Run("false", func(t testing.TB) {
|
||||
requireTrue(t, false)
|
||||
fmt.Println("This is never executed.")
|
||||
})
|
||||
fmt.Printf("Failed: %+v\n", mt.Failed())
|
||||
fmt.Printf("Sub2-Name: %s\n", mt.Subtests()[1].Name())
|
||||
fmt.Printf("Sub2-Failed: %+v\n", mt.Subtests()[1].Failed())
|
||||
fmt.Printf("Sub2-Aborted: %+v\n", mt.Subtests()[1].Aborted())
|
||||
fmt.Printf("Sub2-Output: %s\n", strings.Join(mt.Subtests()[1].Output(), ""))
|
||||
|
||||
// Output:
|
||||
// Name: TestMyBoolean
|
||||
// Failed: false
|
||||
// Sub1-Name: TestMyBoolean/true
|
||||
// Sub1-Failed: false
|
||||
// Sub1-Aborted: false
|
||||
// Failed: true
|
||||
// Sub2-Name: TestMyBoolean/false
|
||||
// Sub2-Failed: true
|
||||
// Sub2-Aborted: true
|
||||
// Sub2-Output: expected false to be true
|
||||
}
|
||||
|
||||
func Example_subtests_in_subtests() {
|
||||
assertGreaterThan := func(t testing.TB, got int, min int) {
|
||||
if got <= min {
|
||||
t.Errorf("expected %d to be greater than %d", got, min)
|
||||
}
|
||||
}
|
||||
|
||||
mt := mocktesting.NewT("TestMyBoolean")
|
||||
mt.Run("positive", func(t testing.TB) {
|
||||
subMT, _ := t.(*mocktesting.T)
|
||||
|
||||
subMT.Run("greater than", func(t testing.TB) {
|
||||
assertGreaterThan(t, 5, 4)
|
||||
})
|
||||
subMT.Run("equal", func(t testing.TB) {
|
||||
assertGreaterThan(t, 5, 5)
|
||||
})
|
||||
subMT.Run("less than", func(t testing.TB) {
|
||||
assertGreaterThan(t, 4, 5)
|
||||
})
|
||||
})
|
||||
fmt.Printf("Name: %s\n", mt.Name())
|
||||
fmt.Printf("Failed: %+v\n", mt.Failed())
|
||||
fmt.Printf("Sub1-Name: %s\n", mt.Subtests()[0].Name())
|
||||
fmt.Printf("Sub1-Failed: %+v\n", mt.Subtests()[0].Failed())
|
||||
fmt.Printf("Sub1-Aborted: %+v\n", mt.Subtests()[0].Aborted())
|
||||
fmt.Printf("Sub1-Sub1-Name: %s\n", mt.Subtests()[0].Subtests()[0].Name())
|
||||
fmt.Printf(
|
||||
"Sub1-Sub1-Failed: %+v\n", mt.Subtests()[0].Subtests()[0].Failed(),
|
||||
)
|
||||
fmt.Printf(
|
||||
"Sub1-Sub1-Aborted: %+v\n", mt.Subtests()[0].Subtests()[0].Aborted(),
|
||||
)
|
||||
fmt.Printf("Sub1-Sub1-Name: %s\n", mt.Subtests()[0].Subtests()[1].Name())
|
||||
fmt.Printf(
|
||||
"Sub1-Sub2-Failed: %+v\n", mt.Subtests()[0].Subtests()[1].Failed(),
|
||||
)
|
||||
fmt.Printf(
|
||||
"Sub1-Sub2-Aborted: %+v\n", mt.Subtests()[0].Subtests()[1].Aborted(),
|
||||
)
|
||||
fmt.Printf(
|
||||
"Sub1-Sub3-Output: %s\n", strings.TrimSpace(
|
||||
strings.Join(mt.Subtests()[0].Subtests()[1].Output(), ""),
|
||||
),
|
||||
)
|
||||
fmt.Printf("Sub1-Sub1-Name: %s\n", mt.Subtests()[0].Subtests()[2].Name())
|
||||
fmt.Printf(
|
||||
"Sub1-Sub3-Failed: %+v\n", mt.Subtests()[0].Subtests()[2].Failed(),
|
||||
)
|
||||
fmt.Printf(
|
||||
"Sub1-Sub3-Aborted: %+v\n", mt.Subtests()[0].Subtests()[2].Aborted(),
|
||||
)
|
||||
fmt.Printf(
|
||||
"Sub1-Sub3-Output: %s\n", strings.TrimSpace(
|
||||
strings.Join(mt.Subtests()[0].Subtests()[2].Output(), ""),
|
||||
),
|
||||
)
|
||||
|
||||
// Output:
|
||||
// Name: TestMyBoolean
|
||||
// Failed: true
|
||||
// Sub1-Name: TestMyBoolean/positive
|
||||
// Sub1-Failed: true
|
||||
// Sub1-Aborted: false
|
||||
// Sub1-Sub1-Name: TestMyBoolean/positive/greater_than
|
||||
// Sub1-Sub1-Failed: false
|
||||
// Sub1-Sub1-Aborted: false
|
||||
// Sub1-Sub1-Name: TestMyBoolean/positive/equal
|
||||
// Sub1-Sub2-Failed: true
|
||||
// Sub1-Sub2-Aborted: false
|
||||
// Sub1-Sub3-Output: expected 5 to be greater than 5
|
||||
// Sub1-Sub1-Name: TestMyBoolean/positive/less_than
|
||||
// Sub1-Sub3-Failed: true
|
||||
// Sub1-Sub3-Aborted: false
|
||||
// Sub1-Sub3-Output: expected 4 to be greater than 5
|
||||
}
|
||||
5
go.mod
Normal file
5
go.mod
Normal file
@@ -0,0 +1,5 @@
|
||||
module github.com/jimeh/go-mocktesting
|
||||
|
||||
go 1.15
|
||||
|
||||
require github.com/stretchr/testify v1.7.0
|
||||
11
go.sum
Normal file
11
go.sum
Normal file
@@ -0,0 +1,11 @@
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
23
mocktesting.go
Normal file
23
mocktesting.go
Normal file
@@ -0,0 +1,23 @@
|
||||
// Package mocktesting provides a mock of *testing.T for the purpose of testing
|
||||
// test helpers.
|
||||
package mocktesting
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Go runs the provided function in a new goroutine, and blocks until the
|
||||
// goroutine has exited.
|
||||
//
|
||||
// This is essentially a helper function to avoid aborting the current goroutine
|
||||
// when a *T instance aborts the goroutine that any of FailNow(), Fatal(),
|
||||
// Fatalf(), SkipNow(), Skip(), or Skipf() are called from.
|
||||
func Go(f func()) {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
f()
|
||||
}()
|
||||
wg.Wait()
|
||||
}
|
||||
30
mocktesting_test.go
Normal file
30
mocktesting_test.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package mocktesting
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
func runInGoroutine(f func()) {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
f()
|
||||
}()
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func stringsUniq(strs []string) []string {
|
||||
m := map[string]bool{}
|
||||
|
||||
for _, s := range strs {
|
||||
m[s] = true
|
||||
}
|
||||
|
||||
r := make([]string, 0, len(m))
|
||||
for s := range m {
|
||||
r = append(r, s)
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
426
t.go
Normal file
426
t.go
Normal file
@@ -0,0 +1,426 @@
|
||||
package mocktesting
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestingT is an interface covering *mocktesting.T's internal use of
|
||||
// *testing.T. See WithTestingT() for more details.
|
||||
type TestingT interface {
|
||||
Fatal(args ...interface{})
|
||||
}
|
||||
|
||||
// T is a fake/mock implementation of testing.T. It records all actions
|
||||
// performed via all methods on the testing.T interface, so they can be
|
||||
// inspected and asserted.
|
||||
//
|
||||
// It is specifically intended for testing test helpers which accept a
|
||||
// *testing.T or *testing.B, so you can verify that the helpers call Fatal(),
|
||||
// Error(), etc, as they need.
|
||||
type T struct {
|
||||
name string
|
||||
abort bool
|
||||
baseTempdir string
|
||||
testingT TestingT
|
||||
deadline time.Time
|
||||
timeout bool
|
||||
|
||||
mux sync.RWMutex
|
||||
skipped bool
|
||||
failed int
|
||||
parallel bool
|
||||
output []string
|
||||
helpers []string
|
||||
aborted bool
|
||||
cleanups []func()
|
||||
env map[string]string
|
||||
subtests []*T
|
||||
tempdirs []string
|
||||
|
||||
// subtestNames is used to ensure subtests do not have conflicting names.
|
||||
subtestNames map[string]bool
|
||||
|
||||
// mkdirTempFunc is used by the TempDir function instead of ioutil.TempDir()
|
||||
// if it is not nil. This is only used by tests for TempDir itself to ensure
|
||||
// it behaves correctly if temp directory creation fails.
|
||||
mkdirTempFunc func(string, string) (string, error)
|
||||
|
||||
// Embed *testing.T to implement the testing.TB interface, which has a
|
||||
// private method to prevent it from being implemented. However that means
|
||||
// it's very difficult to test testing helpers.
|
||||
*testing.T
|
||||
}
|
||||
|
||||
// Ensure T struct implements testing.TB interface.
|
||||
var _ testing.TB = (*T)(nil)
|
||||
|
||||
func NewT(name string, options ...Option) *T {
|
||||
t := &T{
|
||||
name: strings.ReplaceAll(name, " ", "_"),
|
||||
abort: true,
|
||||
baseTempdir: os.TempDir(),
|
||||
deadline: time.Now().Add(10 * time.Minute),
|
||||
timeout: true,
|
||||
}
|
||||
|
||||
for _, opt := range options {
|
||||
opt.apply(t)
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
type Option interface {
|
||||
apply(*T)
|
||||
}
|
||||
|
||||
type optionFunc func(*T)
|
||||
|
||||
func (fn optionFunc) apply(g *T) {
|
||||
fn(g)
|
||||
}
|
||||
|
||||
// WithTimeout specifies a custom timeout for the mock test. It effectively
|
||||
// determines the return values of Deadline().
|
||||
//
|
||||
// When given a zero-value time.Duration, Deadline() will act as if no timeout
|
||||
// has been set.
|
||||
//
|
||||
// If this option is not used, the default timeout value is set to 10 minutes.
|
||||
func WithTimeout(d time.Duration) Option {
|
||||
return optionFunc(func(t *T) {
|
||||
if d > 0 {
|
||||
t.timeout = true
|
||||
t.deadline = time.Now().Add(d)
|
||||
} else {
|
||||
t.timeout = false
|
||||
t.deadline = time.Time{}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// WithDeadline specifies a custom timeout for the mock test, but setting the
|
||||
// deadline to an exact value, rather than setting it based on the offset from
|
||||
// now of a time.Duration. It effectively determines the return values of
|
||||
// Deadline().
|
||||
//
|
||||
// When given a empty time.Time{}, Deadline() will act as if no timeout has been
|
||||
// set.
|
||||
//
|
||||
// If this option is not used, the default timeout value is set to 10 minutes.
|
||||
func WithDeadline(d time.Time) Option {
|
||||
return optionFunc(func(t *T) {
|
||||
if d != (time.Time{}) {
|
||||
t.timeout = true
|
||||
t.deadline = d
|
||||
} else {
|
||||
t.timeout = false
|
||||
t.deadline = time.Time{}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// WithNoAbort disables aborting the current goroutine with runtime.Goexit()
|
||||
// when SkipNow or FailNow is called. This should be used with care, as it
|
||||
// causes behavior to diverge from normal *tesing.T, as code after calling
|
||||
// t.Fatal() will be executed.
|
||||
func WithNoAbort() Option {
|
||||
return optionFunc(func(t *T) {
|
||||
t.abort = false
|
||||
})
|
||||
}
|
||||
|
||||
// WithBaseTempdir sets the base directory that TempDir() creates temporary
|
||||
// directories within.
|
||||
//
|
||||
// If this option is not used, the default base directory used is os.TempDir().
|
||||
func WithBaseTempdir(dir string) Option {
|
||||
return optionFunc(func(t *T) {
|
||||
if dir != "" {
|
||||
t.baseTempdir = dir
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// WithTestingT accepts a *testing.T instance which is used to report internal
|
||||
// errors within *mocktesting.T itself. For example if the TempDir() function
|
||||
// fails to create a temporary directory on disk, it will call Fatal() on the
|
||||
// *testing.T instance provided here.
|
||||
//
|
||||
// If this option is not used, internal errors will instead cause a panic.
|
||||
func WithTestingT(testingT TestingT) Option {
|
||||
return optionFunc(func(t *T) {
|
||||
t.testingT = testingT
|
||||
})
|
||||
}
|
||||
|
||||
func (t *T) goexit() {
|
||||
t.aborted = true
|
||||
if t.abort {
|
||||
runtime.Goexit()
|
||||
}
|
||||
}
|
||||
|
||||
func (t *T) internalError(err error) {
|
||||
err = fmt.Errorf("mocktesting: %w", err)
|
||||
|
||||
if t.testingT != nil {
|
||||
t.testingT.Fatal(err)
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *T) Name() string {
|
||||
return t.name
|
||||
}
|
||||
|
||||
func (t *T) Deadline() (time.Time, bool) {
|
||||
return t.deadline, t.timeout
|
||||
}
|
||||
|
||||
func (t *T) Error(args ...interface{}) {
|
||||
t.Log(args...)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
func (t *T) Errorf(format string, args ...interface{}) {
|
||||
t.Logf(format, args...)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
func (t *T) Fail() {
|
||||
t.failed++
|
||||
}
|
||||
|
||||
func (t *T) FailNow() {
|
||||
t.Fail()
|
||||
t.goexit()
|
||||
}
|
||||
|
||||
func (t *T) Failed() bool {
|
||||
return t.failed > 0
|
||||
}
|
||||
|
||||
func (t *T) Fatal(args ...interface{}) {
|
||||
t.Log(args...)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
func (t *T) Fatalf(format string, args ...interface{}) {
|
||||
t.Logf(format, args...)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
func (t *T) Log(args ...interface{}) {
|
||||
t.mux.Lock()
|
||||
defer t.mux.Unlock()
|
||||
|
||||
t.output = append(t.output, fmt.Sprintln(args...))
|
||||
}
|
||||
|
||||
func (t *T) Logf(format string, args ...interface{}) {
|
||||
t.mux.Lock()
|
||||
defer t.mux.Unlock()
|
||||
|
||||
if len(format) == 0 || format[len(format)-1] != '\n' {
|
||||
format += "\n"
|
||||
}
|
||||
t.output = append(t.output, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func (t *T) Parallel() {
|
||||
t.parallel = true
|
||||
}
|
||||
|
||||
func (t *T) Skip(args ...interface{}) {
|
||||
t.Log(args...)
|
||||
t.SkipNow()
|
||||
}
|
||||
|
||||
func (t *T) Skipf(format string, args ...interface{}) {
|
||||
t.Logf(format, args...)
|
||||
t.SkipNow()
|
||||
}
|
||||
|
||||
func (t *T) SkipNow() {
|
||||
t.skipped = true
|
||||
t.goexit()
|
||||
}
|
||||
|
||||
func (t *T) Skipped() bool {
|
||||
return t.skipped
|
||||
}
|
||||
|
||||
func (t *T) Helper() {
|
||||
pc, _, _, ok := runtime.Caller(1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
fnName := runtime.FuncForPC(pc).Name()
|
||||
|
||||
t.mux.Lock()
|
||||
defer t.mux.Unlock()
|
||||
|
||||
t.helpers = append(t.helpers, fnName)
|
||||
}
|
||||
|
||||
func (t *T) Cleanup(f func()) {
|
||||
t.mux.Lock()
|
||||
defer t.mux.Unlock()
|
||||
|
||||
t.cleanups = append(t.cleanups, f)
|
||||
}
|
||||
|
||||
func (t *T) TempDir() string {
|
||||
// Allow setting MkdirTemp function for testing purposes.
|
||||
f := t.mkdirTempFunc
|
||||
if f == nil {
|
||||
f = ioutil.TempDir
|
||||
}
|
||||
|
||||
dir, err := f(t.baseTempdir, "go-mocktesting*")
|
||||
if err != nil {
|
||||
err = fmt.Errorf("TempDir() failed to create directory: %w", err)
|
||||
t.internalError(err)
|
||||
}
|
||||
|
||||
t.mux.Lock()
|
||||
defer t.mux.Unlock()
|
||||
t.tempdirs = append(t.tempdirs, dir)
|
||||
|
||||
return dir
|
||||
}
|
||||
|
||||
func (t *T) Run(name string, f func(testing.TB)) bool {
|
||||
name = t.newSubTestName(name)
|
||||
fullname := name
|
||||
if t.name != "" {
|
||||
fullname = t.name + "/" + name
|
||||
}
|
||||
|
||||
subtest := NewT(fullname)
|
||||
subtest.abort = t.abort
|
||||
subtest.baseTempdir = t.baseTempdir
|
||||
subtest.testingT = t.testingT
|
||||
subtest.deadline = t.deadline
|
||||
subtest.timeout = t.timeout
|
||||
|
||||
if t.subtestNames == nil {
|
||||
t.subtestNames = map[string]bool{}
|
||||
}
|
||||
|
||||
t.mux.Lock()
|
||||
t.subtests = append(t.subtests, subtest)
|
||||
t.subtestNames[name] = true
|
||||
t.mux.Unlock()
|
||||
|
||||
Go(func() {
|
||||
f(subtest)
|
||||
})
|
||||
|
||||
if subtest.Failed() {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
return !subtest.Failed()
|
||||
}
|
||||
|
||||
func (t *T) newSubTestName(name string) string {
|
||||
name = strings.ReplaceAll(name, " ", "_")
|
||||
|
||||
if !t.subtestNames[name] {
|
||||
return name
|
||||
}
|
||||
|
||||
i := 1
|
||||
for {
|
||||
n := name + "#" + fmt.Sprintf("%02d", i)
|
||||
if !t.subtestNames[n] {
|
||||
return n
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Inspection Methods which are not part of the testing.TB interface.
|
||||
//
|
||||
|
||||
// Output returns a string slice of all output produced by calls to Log(),
|
||||
// Logf(), Error(), Errorf(), Fatal(), Fatalf(), Skip(), and Skipf().
|
||||
func (t *T) Output() []string {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
|
||||
return t.output
|
||||
}
|
||||
|
||||
// CleanupFuncs returns a slice of functions given to Cleanup().
|
||||
func (t *T) CleanupFuncs() []func() {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
|
||||
return t.cleanups
|
||||
}
|
||||
|
||||
// CleanupNames returns a string slice of function names given to Cleanup().
|
||||
func (t *T) CleanupNames() []string {
|
||||
r := make([]string, 0, len(t.cleanups))
|
||||
for _, f := range t.cleanups {
|
||||
p := reflect.ValueOf(f).Pointer()
|
||||
r = append(r, runtime.FuncForPC(p).Name())
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// FailedCount returns the number of times Error(), Errorf(), Fail(), Failf(),
|
||||
// FailNow(), Fatal(), and Fatalf() were called.
|
||||
func (t *T) FailedCount() int {
|
||||
return t.failed
|
||||
}
|
||||
|
||||
// Aborted returns true if the TB instance aborted the current goroutine via
|
||||
// runtime.Goexit(), which is called by FailNow() and SkipNow().
|
||||
func (t *T) Aborted() bool {
|
||||
return t.aborted
|
||||
}
|
||||
|
||||
// HelperNames returns a list of function names which called Helper().
|
||||
func (t *T) HelperNames() []string {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
|
||||
return t.helpers
|
||||
}
|
||||
|
||||
// Paralleled returns true if Parallel() has been called.
|
||||
func (t *T) Paralleled() bool {
|
||||
return t.parallel
|
||||
}
|
||||
|
||||
// Subtests returns a list map of *TB instances for any subtests executed via
|
||||
// Run().
|
||||
func (t *T) Subtests() []*T {
|
||||
if t.subtests == nil {
|
||||
t.mux.Lock()
|
||||
t.subtests = []*T{}
|
||||
t.mux.Unlock()
|
||||
}
|
||||
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
|
||||
return t.subtests
|
||||
}
|
||||
31
t_go116.go
Normal file
31
t_go116.go
Normal file
@@ -0,0 +1,31 @@
|
||||
//go:build go1.16
|
||||
// +build go1.16
|
||||
|
||||
package mocktesting
|
||||
|
||||
func (t *T) Setenv(key string, value string) {
|
||||
t.mux.Lock()
|
||||
defer t.mux.Unlock()
|
||||
|
||||
if t.env == nil {
|
||||
t.env = map[string]string{}
|
||||
}
|
||||
|
||||
if key != "" {
|
||||
t.env[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
// Getenv returns a map[string]string of keys/values given to Setenv().
|
||||
func (t *T) Getenv() map[string]string {
|
||||
if t.env == nil {
|
||||
t.mux.Lock()
|
||||
t.env = map[string]string{}
|
||||
t.mux.Unlock()
|
||||
}
|
||||
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
|
||||
return t.env
|
||||
}
|
||||
182
t_go116_test.go
Normal file
182
t_go116_test.go
Normal file
@@ -0,0 +1,182 @@
|
||||
//go:build go1.16
|
||||
// +build go1.16
|
||||
|
||||
package mocktesting
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestT_Setenv(t *testing.T) {
|
||||
type fields struct {
|
||||
env map[string]string
|
||||
}
|
||||
type args struct {
|
||||
key string
|
||||
value string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want map[string]string
|
||||
}{
|
||||
{
|
||||
name: "empty key and value",
|
||||
args: args{},
|
||||
want: map[string]string{},
|
||||
},
|
||||
{
|
||||
name: "empty key",
|
||||
args: args{value: "bar"},
|
||||
want: map[string]string{},
|
||||
},
|
||||
{
|
||||
name: "empty value",
|
||||
args: args{key: "foo"},
|
||||
want: map[string]string{"foo": ""},
|
||||
},
|
||||
{
|
||||
name: "key and value",
|
||||
args: args{key: "foo", value: "bar"},
|
||||
want: map[string]string{"foo": "bar"},
|
||||
},
|
||||
{
|
||||
name: "add to existing",
|
||||
fields: fields{env: map[string]string{"hello": "world"}},
|
||||
args: args{key: "foo", value: "bar"},
|
||||
want: map[string]string{"hello": "world", "foo": "bar"},
|
||||
},
|
||||
{
|
||||
name: "overwrite existing",
|
||||
fields: fields{env: map[string]string{"foo": "world"}},
|
||||
args: args{key: "foo", value: "bar"},
|
||||
want: map[string]string{"foo": "bar"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mt := &T{env: tt.fields.env}
|
||||
|
||||
mt.Setenv(tt.args.key, tt.args.value)
|
||||
|
||||
assert.Equal(t, tt.want, mt.env)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestT_Getenv(t *testing.T) {
|
||||
type fields struct {
|
||||
env map[string]string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want map[string]string
|
||||
}{
|
||||
{
|
||||
name: "nil env",
|
||||
want: map[string]string{},
|
||||
},
|
||||
{
|
||||
name: "empty env",
|
||||
fields: fields{env: map[string]string{}},
|
||||
want: map[string]string{},
|
||||
},
|
||||
{
|
||||
name: "env",
|
||||
fields: fields{
|
||||
env: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
want: map[string]string{
|
||||
"hello": "world",
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mt := &T{env: tt.fields.env}
|
||||
|
||||
got := mt.Getenv()
|
||||
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestT_Run_Go116(t *testing.T) {
|
||||
type fields struct {
|
||||
name string
|
||||
abort bool
|
||||
baseTempdir string
|
||||
testingT testing.TB
|
||||
deadline time.Time
|
||||
timeout bool
|
||||
}
|
||||
type args struct {
|
||||
f func(testing.TB)
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want *T
|
||||
}{
|
||||
{
|
||||
name: "set environment variables",
|
||||
fields: fields{
|
||||
name: "set_environment_variables",
|
||||
abort: true,
|
||||
baseTempdir: os.TempDir(),
|
||||
deadline: time.Now().Add(10 * time.Minute),
|
||||
timeout: true,
|
||||
},
|
||||
args: args{
|
||||
f: func(t testing.TB) {
|
||||
// Type assert to *TB for compatibility with Go 1.16 and
|
||||
// earlier.
|
||||
mt := t.(*T)
|
||||
mt.Setenv("GO_ENV", "test")
|
||||
mt.Setenv("FOO", "bar")
|
||||
},
|
||||
},
|
||||
want: &T{
|
||||
name: "set_environment_variables",
|
||||
abort: true,
|
||||
baseTempdir: os.TempDir(),
|
||||
deadline: time.Now().Add(10 * time.Minute),
|
||||
timeout: true,
|
||||
env: map[string]string{
|
||||
"GO_ENV": "test",
|
||||
"FOO": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mt := &T{
|
||||
name: tt.fields.name,
|
||||
abort: tt.fields.abort,
|
||||
baseTempdir: tt.fields.baseTempdir,
|
||||
testingT: tt.fields.testingT,
|
||||
deadline: tt.fields.deadline,
|
||||
timeout: tt.fields.timeout,
|
||||
}
|
||||
|
||||
runInGoroutine(func() {
|
||||
tt.args.f(mt)
|
||||
})
|
||||
|
||||
assertEqualMocktestingT(t, tt.want, mt)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user