Skip to content

Commit 0b5ca29

Browse files
committed
Initial commit of code
1 parent 887ef43 commit 0b5ca29

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+4295
-0
lines changed

.gitignore

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Compiled Object files, Static and Dynamic libs (Shared Objects)
2+
*.o
3+
*.a
4+
*.so
5+
6+
# Folders
7+
_obj
8+
_test
9+
debug
10+
.vscode
11+
12+
# Architecture specific extensions/prefixes
13+
*.[568vq]
14+
[568vq].out
15+
16+
*.cgo1.go
17+
*.cgo2.c
18+
_cgo_defun.c
19+
_cgo_gotypes.go
20+
_cgo_export.*
21+
22+
_testmain.go
23+
24+
*.exe
25+
*.test
26+
*.prof
27+
28+
# Test Files
29+
ddt/_ddt/ddt_test.json

.travis.yml

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
language: go
2+
3+
go:
4+
- 1.1
5+
- 1.2
6+
- 1.3
7+
- 1.4
8+
- 1.5
9+
- 1.6
10+
- 1.7
11+
- 1.8.x
12+
- tip
13+
14+
script: go test -v ./...

CONTRIBUTING.md

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Contributing, Developer Guide, and Coding Conventions
2+
Thank you for taking the time to improve tester. Below are a few guidelines
3+
to check before contributing.
4+
5+
## Tenets
6+
* Tester is opinionated
7+
* Tester is lightweight
8+
* Tester is intuitive
9+
* Tester is consitent
10+
11+
## Dependency Management
12+
Copy dependencies to the `vendor` folder.
13+
See <https://golang.org/cmd/go/#hdr-Vendor_Directories> for more details.
14+
15+
## Makefile
16+
It's there to take care of some best practices for you, please use it.
17+
18+
## Code Review Guidelines
19+
See <https://github.com/golang/go/wiki/CodeReviewComments>.
20+
21+
### Addenda
22+
The rule of thumb is: if `go fmt` allows it, so be it. The guidelines below
23+
intend to make the coding style more consistent.
24+
25+
#### Vertical Ordering
26+
From _Clean Code, Chapter 5: Formatting_
27+
> We would like a source file to be like a newspaper article. The name should be
28+
simple but explanatory. The name, by itself, should be sufficient to tell us
29+
whether we are in the right module or not. The topmost parts of the source file
30+
should provide the high-level concepts and algorithms. Detail should increase as
31+
we move downward, until at the end we find the lowest level functions
32+
and details in the source file... In general we want function call dependencies
33+
to point in the downward direction. That is, a function that is called should be
34+
below a function that does the calling. This creates a nice flow down the source
35+
code module from high level to low level.
36+
37+
Also, see <https://talks.golang.org/2013/bestpractices.slide#14>,
38+
which recommends the following order:
39+
1. Header: license information, build tags, package documentation.
40+
1. Imports: related groups separated by blank lines.
41+
1. Body: the rest of the code starting with the most significant types,
42+
and ending with helper functions and helper types.
43+
44+
#### Line Length
45+
Keep line length under 120 characters for code and 80 for documentation.
46+
Break lines in a way that improves readability but still keeps the code compact;
47+
for example,
48+
49+
callable.testContext.errorf(
50+
file, line, "Function call did not panic as expected.\nExpected: %s\n", expectedError)
51+
52+
is readable and more compact than
53+
54+
callable.testContext.errorf(
55+
file,
56+
line,
57+
"Function call did not panic as expected.\nExpected: %s\n",
58+
expectedError)
59+
60+
Either is definitely more readable than
61+
62+
callable.testContext.errorf(file, line,
63+
"Function call did not panic as expected.\nExpected: %s\n", expectedError)
64+
65+
#### Variable Names
66+
Do not prefer `c` to `lineCount`; see _Clean Code, Chapter 2: Meaningful Names_.
67+
> There can be no worse reason for using the [variable] name c than because a
68+
and b were already taken.
69+
70+
That said, find the shortest name that's self-explanatory:
71+
72+
> Shorter names are generally better than longer ones, so long as they are
73+
clear. Add no more context to a name than is necessary.
74+
75+
Loop variables can be a single letter (given that the loop block is short).

Makefile

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
GO ?= go
2+
ALL_PACKAGES := ./...
3+
LOCAL_PACKAGES := `$(GO) list $(ALL_PACKAGES) | grep -v /vendor/`
4+
5+
all: clean check
6+
7+
check:
8+
$(GO) fmt $(LOCAL_PACKAGES)
9+
$(GO) vet $(LOCAL_PACKAGES)
10+
$(GO) test $(LOCAL_PACKAGES)
11+
12+
clean:
13+
$(GO) clean $(ALL_PACKAGES)

README.md

+172
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# Tester: Test More, Type Less
2+
Lightweight test utilities to use with Go's testing package.
3+
4+
## Features
5+
* Assertions that make testss easier to read, write, and debug
6+
* Streamlined data providers for data-driven testing (DDT)
7+
* Test hooks' hygiene check
8+
9+
## Motivations
10+
Most tests follow the same pattern: set up, invoke the unit under test, assert,
11+
then clean up (if need be); said pattern encourages code reuse and consistency.
12+
By using test utilities, you can spend more time thinking about test stratigies
13+
and less time typing boilerplate code.
14+
15+
## Quick Start
16+
Get the latest version (`go get -u github.com/workfit/tester`) then test away:
17+
18+
package hitchhiker
19+
20+
import (
21+
"testing"
22+
"github.com/workfit/tester/assert"
23+
)
24+
25+
func TestDeepThought(t *testing.T) {
26+
computer := NewDeepThoughtComputer()
27+
answer, err := computer.AnswerTheUltimateQuestion()
28+
if assert.For(t).ThatActual(err).IsNil().Passed() {
29+
assert.For(t).ThatActual(answer).Equals(42)
30+
}
31+
}
32+
33+
## Learn More
34+
The following can be also found at <https://godoc.org/github.com/workfit/tester>
35+
36+
### Assertions
37+
Package `assert` provides a more readable way to assert in test cases;
38+
for example:
39+
40+
assert.For(t).ThatCalling(fn).PanicsReporting("expected error")
41+
42+
This way, the assert statement reads well; it flows like a proper sentence.
43+
44+
In addition, one can easily tell which value is the test case got (actual)
45+
and which it wanted (expected); this is key to printing the values correctly
46+
to make debugging a bit easier. In Go, the actual value is usually printed
47+
first; for example:
48+
49+
assert.For(t).ThatActual(foo).Equals(expected)
50+
51+
The above enforces said order in both reading the code and the assertion failure
52+
message (if any).
53+
54+
For convenience (that also improves readability), there are methods for special
55+
cases like:
56+
57+
assert.For(t).ThatActual(foo).IsTrue()
58+
assert.For(t).ThatActualString(bar).IsEmpty()
59+
60+
Which are equivalent to:
61+
62+
assert.For(t).ThatActual(foo).Equals(true)
63+
assert.For(t).ThatActual(len(bar)).Equals(0)
64+
65+
To identify a test case in a table-driven test, optional ID parameters can be
66+
specified and will be included in failure messages:
67+
68+
cases := []struct {
69+
id string
70+
actual interface{}
71+
expected interface{}
72+
}{
73+
{"different values", 42, 13},
74+
{"different types", 42.0, 42},
75+
{"different containers", [...]int{42}, []int{42}},
76+
}
77+
78+
for _, c := range cases {
79+
assert.For(t, c.id).ThatActual(c.actual).Equals(c.expected)
80+
}
81+
82+
After an assertion is performed, a `ValueAssertionResult` is returned to allow
83+
for post-assert actions to be performed; for example:
84+
85+
assert.For(t).ThatActual(value).Equals(expected).ThenDiffOnFail()
86+
87+
Which will pretty-print a detailed recursive diff of both objects on failure.
88+
89+
It also can be used as a condition to perform extra test steps:
90+
91+
value := GetValue()
92+
if assert.For(t).ThatActual(value).IsNotNil().Passed() {
93+
assert.For(t).ThatActual(value.GetFoo()).Equals(expected)
94+
}
95+
96+
Or to perform a deeper analysis of the test values:
97+
98+
if !assert.For(t).ThatActual(value).Equals(expected).Passed() {
99+
analyze(value, expected) // e.g., analyze may look at common bugs
100+
}
101+
102+
Conveniently, the last example above can be rewritten as:
103+
104+
assert.For(t).ThatActual(value).Equals(expected).ThenRunOnFail(analyze)
105+
106+
Another use is to print a custom failure message:
107+
108+
assert.For(t).ThatActual(foo).Equals(bar).ThenRunOnFail(func(actual, expected interface{}) {
109+
fmt.Printf("JSON: %q != %q", actual.ToJSON(), expected.ToJSON())
110+
})
111+
112+
The above pattern allows for reuse of post-failure analysis and cleanup.
113+
114+
The interfaces in this package are still a work-in-progress, and are subject
115+
to change.
116+
117+
### Test Hooks
118+
What exists merely for test code to see shall not be exported to the world.
119+
You can tag test-hook fields like the following:
120+
121+
type sleeper struct {
122+
sleep func(time.Duration) `test-hook:"verify-unexported"`
123+
}
124+
125+
Using tags, instead of comments, enables you to search the codebase for test
126+
hooks and validate, via reflection, that they're not exported.
127+
A test case should be added to verify that test hooks are hidden:
128+
129+
func TestHooksAreHidden(t *testing.T) {
130+
assert.For(t).ThatType(reflect.TypeOf(sleeper{})).HidesTestHooks()
131+
}
132+
133+
### Data-Driven Testing (DDT)
134+
When the number of test cases in a table-driven test gets out of hand and they
135+
cannot fit neatly in structs anymore, the use of a data provider is in order.
136+
Package `ddt` provides a way to load test cases from a JSON file whose path
137+
is derived from the caller's test function name and file. The file path is
138+
`<package under test>/_ddt/<basename of test file>.json`; for example,
139+
`hitchhiker/_ddt/question_test.json` with the following schema:
140+
141+
{
142+
"testFunctions": {
143+
"<name of test function>": [
144+
{
145+
<properties of the test case to unmarshal>
146+
}
147+
]
148+
}
149+
}
150+
151+
For example, the JSON content may look like the following:
152+
153+
{
154+
"testFunctions": {
155+
"TestDeepThought": [
156+
{
157+
"id": "The Ultimate Question",
158+
"input": {
159+
"question": "What do you get when you multiply six by nine?",
160+
"timeoutInHours": 65700000000,
161+
"config": {"base": 13}
162+
},
163+
"expected": {
164+
"answer": "42",
165+
"error": null
166+
}
167+
}
168+
]
169+
}
170+
}
171+
172+
The details of the test case struct are left for the tester to specify.

0 commit comments

Comments
 (0)