|
1 | | -# Assert & Require |
| 1 | +# testing |
2 | 2 |
|
3 | 3 | [English](README.md) | [中文](README_CN.md) |
4 | 4 |
|
5 | | -Go-Spring Testing provides two packages for making assertions in tests: `assert` and `require`. Both packages offer a |
6 | | -fluent API for writing clear and expressive test assertions. |
| 5 | +Go-Spring Testing is an elegant test assertion library designed for Go, providing a Fluent API style that |
| 6 | +makes your test code clearer and more readable. |
7 | 7 |
|
8 | | -### assert |
| 8 | +## Features Overview |
9 | 9 |
|
10 | | -The `assert` package provides assertion functions that allow tests to continue running even when an assertion fails. |
| 10 | +- 📝 **Dual-mode support**: Provides both `assert` and `require` modes to meet different scenario requirements |
| 11 | +- 💧 **Fluent API**: Method chaining makes code more readable, close to natural language |
| 12 | +- 🏷️ **Type safety**: Generics guarantee type safety with compile-time error checking |
| 13 | +- 🔧 **Type-specific**: Provides dedicated assertion methods for different data types |
| 14 | +- 🧩 **Feature-rich**: Covers the vast majority of assertion needs in daily testing, supporting generic values, |
| 15 | + errors, numbers, strings, slices, maps, panic detection, and more |
| 16 | +- ✅ **Zero dependencies**: Only depends on the Go standard library |
11 | 17 |
|
12 | | -When an assertion in the `assert` package fails, the test function continues executing subsequent assertions. This is |
13 | | -useful when you want to report multiple failures in a single test run. |
| 18 | +## assert vs require |
14 | 19 |
|
15 | | -### require |
| 20 | +Go-Spring Testing provides two packages to meet different testing needs: |
16 | 21 |
|
17 | | -The `require` package provides assertion functions that stop test execution immediately when an assertion fails. |
| 22 | +### `assert` package |
18 | 23 |
|
19 | | -When an assertion in the `require` package fails, the test function stops executing and no further assertions are |
20 | | -checked. This is useful when subsequent assertions would panic or cause other issues if a critical condition is not met. |
| 24 | +The `assert` package provides assertion functions that **do not terminate test execution when an assertion fails**. |
21 | 25 |
|
22 | | -### Basic Example |
| 26 | +When an assertion fails, the test continues running and subsequent assertions will still be checked. |
| 27 | +This is very useful when you want to report multiple failures in a single test run and see all issues at once. |
| 28 | + |
| 29 | +### `require` package |
| 30 | + |
| 31 | +The `require` package provides assertion functions that **immediately stop test execution when an assertion fails**. |
| 32 | + |
| 33 | +When an assertion fails, the test terminates immediately and no further assertions are checked. |
| 34 | +This is suitable for scenarios where critical conditions are not met and subsequent assertions may cause panics or other issues. |
| 35 | +For example, when you need to verify that an object is non-nil before you can proceed with subsequent operations. |
| 36 | + |
| 37 | +## Basic Example |
23 | 38 |
|
24 | 39 | ```go |
25 | 40 | package main |
26 | 41 |
|
27 | 42 | import ( |
28 | 43 | "testing" |
| 44 | + "os" |
| 45 | + "math" |
29 | 46 |
|
30 | 47 | "github.com/go-spring/stdlib/testing/assert" |
31 | 48 | "github.com/go-spring/stdlib/testing/require" |
32 | 49 | ) |
33 | 50 |
|
34 | | -func TestSomething(t *testing.T) { |
35 | | - // Using assert - test continues on failure |
36 | | - assert.That(t, "hello").Equal("hello") |
37 | | - assert.Number(t, 42).GreaterThan(40) |
| 51 | +func TestExample(t *testing.T) { |
| 52 | + // Generic assertions - works with any type |
| 53 | + assert.That(t, "hello").Equal("hello") // Equality assertion |
| 54 | + assert.That(t, user).NotNil() // Non-nil assertion |
| 55 | + assert.That(t, 42).True() // Boolean is true |
| 56 | + |
| 57 | + // Using require - test stops immediately on failure |
| 58 | + require.That(t, user).NotNil() |
| 59 | + |
| 60 | + // Error assertions |
| 61 | + err := someFunc() |
| 62 | + assert.Error(t, err).NotNil() // Expect an error to occur |
| 63 | + assert.Error(t, err).Is(os.IsNotExist) // Check error type using errors.Is |
| 64 | + |
| 65 | + // Number assertions |
| 66 | + assert.Number(t, 42).GreaterThan(40) // Greater than |
| 67 | + assert.Number(t, 100).Between(0, 200) // Within range |
| 68 | + assert.Number(t, 0).Zero() // Equal to zero |
| 69 | + assert.Number(t, 3.14).InDelta(math.Pi, 0.01) // Floating point comparison with tolerance |
| 70 | + |
| 71 | + // String assertions |
| 72 | + assert.String(t, "user@example.com").IsEmail() // Validate email format |
| 73 | + assert.String(t, "hello world").Contains("world") // Contains substring |
| 74 | + assert.String(t, "hello").HasPrefix("he") // Prefix check |
| 75 | + assert.String(t, `{"name": "bob"}`).JSONEqual(`{"name":"bob"}`) // JSON structural equality |
| 76 | + |
| 77 | + // Slice assertions |
| 78 | + assert.Slice(t, []int{1, 2, 3}).Contains(2) // Contains element |
| 79 | + assert.Slice(t, []int{1, 2, 3}).Length(3) // Length check |
| 80 | + assert.Slice(t, []int{1, 2, 3}).NotEmpty() // Not empty check |
| 81 | + assert.Slice(t, []int{1, 2, 3}).AllUnique() // All elements unique |
38 | 82 |
|
39 | | - // Using require - test stops on failure |
40 | | - require.That(t, someValue).NotNil() |
| 83 | + // Map assertions |
| 84 | + m := map[string]int{"a": 1, "b": 2} |
| 85 | + assert.Map(t, m).ContainsKey("a") // Contains key |
| 86 | + assert.Map(t, m).ContainsKeyValue("a", 1) // Contains key-value pair |
| 87 | + assert.Map(t, m).Length(2) // Length check |
41 | 88 |
|
42 | | - // Type-specific assertions |
43 | | - assert.String(t, "user@example.com").IsEmail() |
44 | | - assert.Number(t, 100).Between(0, 200) |
45 | | - assert.Slice(t, []int{1, 2, 3}).Contains(2) |
| 89 | + // Panic assertion |
| 90 | + assert.Panic(t, func() { |
| 91 | + panic("something wrong happened") |
| 92 | + }, "wrong") // Assert that panic occurs and message contains "wrong" |
46 | 93 | } |
47 | 94 | ``` |
48 | 95 |
|
| 96 | +## Assertion Method Reference |
| 97 | + |
| 98 | +### Generic Assertions (That) |
| 99 | + |
| 100 | +These generic assertion methods can be used with any type. |
| 101 | +**All methods support adding `msg ...string` at the end for custom error messages**. |
| 102 | + |
| 103 | +| Method | Description | |
| 104 | +|--------|-------------| |
| 105 | +| `True(...msg)` | Verify that the boolean value is `true` | |
| 106 | +| `False(...msg)` | Verify that the boolean value is `false` | |
| 107 | +| `Nil(...msg)` | Verify that the value is `nil` (correctly handles nil in interface types) | |
| 108 | +| `NotNil(...msg)` | Verify that the value is not `nil` | |
| 109 | +| `Equal(expected, ...msg)` | Deep comparison using `reflect.DeepEqual` | |
| 110 | +| `NotEqual(expected, ...msg)` | Verify not deeply equal | |
| 111 | +| `Same(expected, ...msg)` | Exact comparison using `==` (same pointer address) | |
| 112 | +| `NotSame(expected, ...msg)` | Comparison using `!=` | |
| 113 | +| `TypeOf(interface, ...msg)` | Verify that the type is assignable to the target type | |
| 114 | +| `Implements(interface, ...msg)` | Verify that the type implements the specified interface | |
| 115 | +| `Has(expected, ...msg)` | Call the value's `Has` method, verify it returns `true` | |
| 116 | +| `Contains(expected, ...msg)` | Call the value's `Contains` method, verify it returns `true` | |
| 117 | + |
| 118 | +### Error Assertions (Error) |
| 119 | + |
| 120 | +Dedicated assertions for `error` type. |
| 121 | +**All methods support adding `msg ...string` at the end for custom error messages**. |
| 122 | + |
| 123 | +| Method | Description | |
| 124 | +|--------|-------------| |
| 125 | +| `Nil(...msg)` | Verify the error is `nil` | |
| 126 | +| `NotNil(...msg)` | Verify the error is not `nil` | |
| 127 | +| `Is(target, ...msg)` | Verify error is the target error using `errors.Is` | |
| 128 | +| `NotIs(target, ...msg)` | Verify error is not the target error using `errors.Is` | |
| 129 | +| `String(expect, ...msg)` | Verify error message string equality | |
| 130 | +| `Matches(pattern, ...msg)` | Verify error message matches regular expression | |
| 131 | + |
| 132 | +### Number Assertions (Number) |
| 133 | + |
| 134 | +Supports all numeric types (`int`/`uint`/`float`, etc.). |
| 135 | +**All methods support adding `msg ...string` at the end for custom error messages**. |
| 136 | + |
| 137 | +| Method | Description | |
| 138 | +|--------|-------------| |
| 139 | +| `Equal(expect, ...msg)` | Equal to | |
| 140 | +| `NotEqual(expect, ...msg)` | Not equal to | |
| 141 | +| `GreaterThan(expect, ...msg)` | Greater than | |
| 142 | +| `GreaterOrEqual(expect, ...msg)` | Greater than or equal to | |
| 143 | +| `LessThan(expect, ...msg)` | Less than | |
| 144 | +| `LessOrEqual(expect, ...msg)` | Less than or equal to | |
| 145 | +| `Zero(...msg)` | Equal to zero | |
| 146 | +| `NotZero(...msg)` | Not equal to zero | |
| 147 | +| `Positive(...msg)` | Positive number | |
| 148 | +| `NotPositive(...msg)` | Non-positive (≤ 0) | |
| 149 | +| `Negative(...msg)` | Negative number | |
| 150 | +| `NotNegative(...msg)` | Non-negative (≥ 0) | |
| 151 | +| `Between(lower, upper, ...msg)` | Within the interval (inclusive) | |
| 152 | +| `NotBetween(lower, upper, ...msg)` | Not within the interval | |
| 153 | +| `InDelta(expect, delta, ...msg)` | Within the expected error tolerance | |
| 154 | +| `IsNaN(...msg)` | Is NaN (only valid for floats) | |
| 155 | +| `IsInf(sign, ...msg)` | Is infinity (sign ≥ 0 for +Inf, < 0 for -Inf) | |
| 156 | +| `IsFinite(...msg)` | Is a finite number (not NaN and not Inf) | |
| 157 | + |
| 158 | +### String Assertions (String) |
| 159 | + |
| 160 | +Dedicated assertions for `string` type. |
| 161 | +**All methods support adding `msg ...string` at the end for custom error messages**. |
| 162 | + |
| 163 | +| Method | Description | |
| 164 | +|--------|-------------| |
| 165 | +| `Length(length, ...msg)` | Verify length | |
| 166 | +| `Blank(...msg)` | Verify empty or all whitespace | |
| 167 | +| `NotBlank(...msg)` | Verify not blank | |
| 168 | +| `Equal(expect, ...msg)` | Equal to | |
| 169 | +| `NotEqual(expect, ...msg)` | Not equal to | |
| 170 | +| `EqualFold(expect, ...msg)` | Case-insensitive equality | |
| 171 | +| `JSONEqual(expect, ...msg)` | Deserialize JSON and compare structural equality | |
| 172 | +| `Matches(pattern, ...msg)` | Match regular expression | |
| 173 | +| `HasPrefix(prefix, ...msg)` | Starts with the specified prefix | |
| 174 | +| `HasSuffix(suffix, ...msg)` | Ends with the specified suffix | |
| 175 | +| `Contains(substr, ...msg)` | Contains substring | |
| 176 | +| `IsLowerCase(...msg)` | All lowercase | |
| 177 | +| `IsUpperCase(...msg)` | All uppercase | |
| 178 | +| `IsNumeric(...msg)` | All digits | |
| 179 | +| `IsAlpha(...msg)` | All letters | |
| 180 | +| `IsAlphaNumeric(...msg)` | All letters and digits | |
| 181 | +| `IsEmail(...msg)` | Verify is a valid email address | |
| 182 | +| `IsURL(...msg)` | Verify is a valid URL | |
| 183 | +| `IsIPv4(...msg)` | Verify is a valid IPv4 address | |
| 184 | +| `IsHex(...msg)` | Verify is a valid hexadecimal string | |
| 185 | +| `IsBase64(...msg)` | Verify is a valid Base64 encoding | |
| 186 | + |
| 187 | +### Slice Assertions (Slice) |
| 188 | + |
| 189 | +Dedicated assertions for slice type `[]T`. |
| 190 | +**All methods support adding `msg ...string` at the end for custom error messages**. |
| 191 | + |
| 192 | +| Method | Description | |
| 193 | +|--------|-------------| |
| 194 | +| `Length(length, ...msg)` | Verify length | |
| 195 | +| `Nil(...msg)` | Verify is nil | |
| 196 | +| `NotNil(...msg)` | Verify is not nil | |
| 197 | +| `Empty(...msg)` | Verify empty (length is zero) | |
| 198 | +| `NotEmpty(...msg)` | Verify not empty | |
| 199 | +| `Equal(expect, ...msg)` | Slice is exactly equal (element order and values match) | |
| 200 | +| `NotEqual(expect, ...msg)` | Verify not equal | |
| 201 | +| `Contains(element, ...msg)` | Contains element | |
| 202 | +| `NotContains(element, ...msg)` | Does not contain element | |
| 203 | +| `ContainsSlice(sub, ...msg)` | Contains consecutive sub-slice | |
| 204 | +| `NotContainsSlice(sub, ...msg)` | Does not contain consecutive sub-slice | |
| 205 | +| `HasPrefix(prefix, ...msg)` | Starts with the specified slice as a prefix | |
| 206 | +| `HasSuffix(suffix, ...msg)` | Ends with the specified slice as a suffix | |
| 207 | +| `AllUnique(...msg)` | All elements are unique | |
| 208 | +| `AllMatches(fn, ...msg)` | All elements satisfy the predicate function | |
| 209 | +| `AnyMatches(fn, ...msg)` | At least one element satisfies the predicate function | |
| 210 | +| `NoneMatches(fn, ...msg)` | No element satisfies the predicate function | |
| 211 | + |
| 212 | +### Map Assertions (Map) |
| 213 | + |
| 214 | +Dedicated assertions for map type `map[K]V`. |
| 215 | +**All methods support adding `msg ...string` at the end for custom error messages**. |
| 216 | + |
| 217 | +| Method | Description | |
| 218 | +|--------|-------------| |
| 219 | +| `Length(length, ...msg)` | Verify length | |
| 220 | +| `Nil(...msg)` | Verify is nil | |
| 221 | +| `NotNil(...msg)` | Verify is not nil | |
| 222 | +| `Empty(...msg)` | Verify empty | |
| 223 | +| `NotEmpty(...msg)` | Verify not empty | |
| 224 | +| `Equal(expect, ...msg)` | Exactly equal | |
| 225 | +| `NotEqual(expect, ...msg)` | Not equal | |
| 226 | +| `ContainsKey(key, ...msg)` | Contains key | |
| 227 | +| `NotContainsKey(key, ...msg)` | Does not contain key | |
| 228 | +| `ContainsValue(value, ...msg)` | Contains value | |
| 229 | +| `NotContainsValue(value, ...msg)` | Does not contain value | |
| 230 | +| `ContainsKeyValue(key, value, ...msg)` | Contains the specified key-value pair | |
| 231 | +| `ContainsKeys(keys, ...msg)` | Contains all specified keys | |
| 232 | +| `NotContainsKeys(keys, ...msg)` | Does not contain any of the specified keys | |
| 233 | +| `ContainsValues(values, ...msg)` | Contains all specified values | |
| 234 | +| `NotContainsValues(values, ...msg)` | Does not contain any of the specified values | |
| 235 | +| `SubsetOf(expect, ...msg)` | Current map is a subset of expect (all key-value pairs exist in expect) | |
| 236 | +| `SupersetOf(expect, ...msg)` | Current map is a superset of expect (all key-value pairs in expect exist in current) | |
| 237 | +| `HasSameKeys(expect, ...msg)` | Has exactly the same set of keys as expect | |
| 238 | +| `HasSameValues(expect, ...msg)` | Has exactly the same multiset of values as expect (order doesn't matter) | |
| 239 | + |
| 240 | +### Panic Assertion |
| 241 | + |
| 242 | +Used to detect whether a function will panic. This is a top-level function. |
| 243 | +**Supports adding `msg ...string` at the end for custom error messages**. |
| 244 | + |
| 245 | +| Method | Description | |
| 246 | +|--------|-------------| |
| 247 | +| `Panic(t, fn, pattern, ...msg)` | Assert that `fn` panics, and the panic message matches the regex `pattern` | |
| 248 | + |
| 249 | +## Custom Error Messages |
| 250 | + |
| 251 | +All assertion methods support adding custom error messages at the end: |
| 252 | + |
| 253 | +```go |
| 254 | +assert.That(t, result).Equal(expected, "result should match expected") |
| 255 | +assert.Number(t, age).GreaterThan(18, "user should be an adult") |
| 256 | +``` |
| 257 | + |
49 | 258 | ## License |
50 | 259 |
|
51 | 260 | Apache License 2.0 |
0 commit comments