Skip to content

Commit a267035

Browse files
committed
Add Greater, GreaterOrEqual, Less, LessOrEqual, Empty, NotEmpty, and NotEqualDelta assert methods
1 parent 1c4d180 commit a267035

5 files changed

Lines changed: 281 additions & 48 deletions

File tree

CHANGELOG.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,17 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
66
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
77

88

9-
## [Unreleased](https://github.com/gravitton/assert/compare/v1.1.0...master)
9+
## [Unreleased](https://github.com/gravitton/assert/compare/v1.2.0...master)
1010

11-
## v1.1.0 (2026-04-22)
11+
## v1.2.0 (2026-04-22)(https://github.com/gravitton/assert/compare/v1.1.0...v1.2.0)
12+
### Added
13+
- Added `Greater`, `GreaterOrEqual`, `Less`, and `LessOrEqual` assert methods for numeric comparison
14+
- Added `Empty` and `NotEmpty` assert methods for zero-length checks on strings, slices, maps, and channels
15+
- Added `NotEqualDelta` assert method, complementing `EqualDelta`
16+
### Fixed
17+
- `Error` and `NoError` now correctly treat typed nil errors (e.g. `(*MyError)(nil)`) as nil
18+
19+
## v1.1.0 (2026-04-22)(https://github.com/gravitton/assert/compare/v1.0.0...v1.1.0)
1220
### Added
1321
- Added `Matches` and `NotMatches` assert methods for regexp matching on strings
1422

README.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,18 +43,27 @@ func TestFoo(t *testing.T) {
4343
|---|---|
4444
| `True(t, condition)` | condition is true |
4545
| `False(t, condition)` | condition is false |
46+
| `Same(t, actual, expected)` | pointers reference the same object |
47+
| `NotSame(t, actual, expected)` | pointers reference different objects |
4648
| `Equal(t, actual, expected)` | values are equal (deep) |
4749
| `NotEqual(t, actual, expected)` | values are not equal |
4850
| `EqualDelta(t, actual, expected, delta)` | numeric values differ by at most delta |
49-
| `Same(t, actual, expected)` | pointers reference the same object |
50-
| `NotSame(t, actual, expected)` | pointers reference different objects |
51+
| `NotEqualDelta(t, actual, expected, delta)` | numeric values differ by more than delta |
52+
| `Greater(t, actual, expected)` | actual > expected |
53+
| `GreaterOrEqual(t, actual, expected)` | actual >= expected |
54+
| `Less(t, actual, expected)` | actual < expected |
55+
| `LessOrEqual(t, actual, expected)` | actual <= expected |
5156
| `Length(t, object, n)` | string/slice/map/channel has length n |
57+
| `Empty(t, object)` | string/slice/map/channel has zero length |
58+
| `NotEmpty(t, object)` | string/slice/map/channel has non-zero length |
5259
| `Contains(t, object, element)` | string/slice/map/channel contains element |
5360
| `NotContains(t, object, element)` | string/slice/map/channel does not contain element |
5461
| `Error(t, err)` | error is not nil |
5562
| `NoError(t, err)` | error is nil |
5663
| `ErrorIs(t, err, target)` | error unwraps to target |
5764
| `NotErrorIs(t, err, target)` | error does not unwrap to target |
65+
| `Matches(t, actual, pattern)` | string matches regular expression |
66+
| `NotMatches(t, actual, pattern)` | string does not match regular expression |
5867
| `EqualJSON(t, actual, expected)` | JSON strings are semantically equal |
5968
| `JSON(t, object, expected)` | object marshals to expected JSON string |
6069
| `Fail(t, message)` | always fails with message |

assert.go

Lines changed: 119 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,38 @@ func False(t Testing, condition bool, messages ...string) bool {
5252
return true
5353
}
5454

55+
// Same asserts that two pointers reference the same object.
56+
//
57+
// Both arguments must be pointer variables. Pointer variable equality is
58+
// determined based on the equality of both type and value.
59+
func Same[T Reference](t Testing, actual, expected T, messages ...string) bool {
60+
t.Helper()
61+
62+
if valid, ok := same[T](expected, actual); !ok {
63+
return Failf(t, "%sShould be pointers\n actual: %s\nexpected: %s", message(messages), print(actual), print(expected))
64+
} else if !valid {
65+
return Failf(t, "%sShould be same\n actual: %s\nexpected: %s", message(messages), print(actual), print(expected))
66+
}
67+
68+
return true
69+
}
70+
71+
// NotSame asserts that two pointers do NOT reference the same object.
72+
//
73+
// Both arguments must be pointer variables. Pointer variable equality is
74+
// determined based on the equality of both type and value.
75+
func NotSame[T Reference](t Testing, actual, expected T, messages ...string) bool {
76+
t.Helper()
77+
78+
if valid, ok := same(expected, actual); !ok {
79+
Failf(t, "%sShould be pointers\n actual: %s\nexpected: %s", message(messages), print(actual), print(expected))
80+
} else if valid {
81+
return Failf(t, "%sShould not be same\n actual: %s", message(messages), print(actual))
82+
}
83+
84+
return true
85+
}
86+
5587
// Equal asserts that two objects are equal.
5688
//
5789
// Pointer variable equality is determined based on the equality of the
@@ -93,33 +125,58 @@ func EqualDelta[T Numeric](t Testing, actual, expected, delta T, messages ...str
93125
return true
94126
}
95127

96-
// Same asserts that two pointers reference the same object.
128+
// NotEqualDelta asserts that two numeric values difference is greater than delta.
97129
//
98-
// Both arguments must be pointer variables. Pointer variable equality is
99-
// determined based on the equality of both type and value.
100-
func Same[T Reference](t Testing, actual, expected T, messages ...string) bool {
130+
// Panics if delta is negative.
131+
func NotEqualDelta[T Numeric](t Testing, actual, expected, delta T, messages ...string) bool {
101132
t.Helper()
102133

103-
if valid, ok := same[T](expected, actual); !ok {
104-
return Failf(t, "%sShould be pointers\n actual: %s\nexpected: %s", message(messages), print(actual), print(expected))
105-
} else if !valid {
106-
return Failf(t, "%sShould be same\n actual: %s\nexpected: %s", message(messages), print(actual), print(expected))
134+
if equalDelta(actual, expected, delta) {
135+
return Failf(t, "%sShould not be equal in delta:\n actual: %s\nexpected: %s", message(messages), print(actual), print(expected))
107136
}
108137

109138
return true
110139
}
111140

112-
// NotSame asserts that two pointers do NOT reference the same object.
113-
//
114-
// Both arguments must be pointer variables. Pointer variable equality is
115-
// determined based on the equality of both type and value.
116-
func NotSame[T Reference](t Testing, actual, expected T, messages ...string) bool {
141+
// Greater asserts that actual is greater than expected.
142+
func Greater[T Numeric](t Testing, actual, expected T, messages ...string) bool {
117143
t.Helper()
118144

119-
if valid, ok := same(expected, actual); !ok {
120-
Failf(t, "%sShould be pointers\n actual: %s\nexpected: %s", message(messages), print(actual), print(expected))
121-
} else if valid {
122-
return Failf(t, "%sShould not be same\n actual: %s", message(messages), print(actual))
145+
if actual <= expected {
146+
return Failf(t, "%sShould be greater\n actual: %s\nexpected: %s", message(messages), print(actual), print(expected))
147+
}
148+
149+
return true
150+
}
151+
152+
// GreaterOrEqual asserts that actual is greater than or equal to expected.
153+
func GreaterOrEqual[T Numeric](t Testing, actual, expected T, messages ...string) bool {
154+
t.Helper()
155+
156+
if actual < expected {
157+
return Failf(t, "%sShould be greater or equal\n actual: %s\nexpected: %s", message(messages), print(actual), print(expected))
158+
}
159+
160+
return true
161+
}
162+
163+
// Less asserts that actual is less than expected.
164+
func Less[T Numeric](t Testing, actual, expected T, messages ...string) bool {
165+
t.Helper()
166+
167+
if actual >= expected {
168+
return Failf(t, "%sShould be less\n actual: %s\nexpected: %s", message(messages), print(actual), print(expected))
169+
}
170+
171+
return true
172+
}
173+
174+
// LessOrEqual asserts that actual is less than or equal to expected.
175+
func LessOrEqual[T Numeric](t Testing, actual, expected T, messages ...string) bool {
176+
t.Helper()
177+
178+
if actual > expected {
179+
return Failf(t, "%sShould be less or equal\n actual: %s\nexpected: %s", message(messages), print(actual), print(expected))
123180
}
124181

125182
return true
@@ -136,6 +193,28 @@ func Length[S Iterable[any]](t Testing, object S, expected int, messages ...stri
136193
return true
137194
}
138195

196+
// Empty asserts that object has zero length.
197+
func Empty[S Iterable[any]](t Testing, object S, messages ...string) bool {
198+
t.Helper()
199+
200+
if length(object) != 0 {
201+
return Failf(t, "%sShould be empty\n object: %#v", message(messages), object)
202+
}
203+
204+
return true
205+
}
206+
207+
// NotEmpty asserts that object has non-zero length.
208+
func NotEmpty[S Iterable[any]](t Testing, object S, messages ...string) bool {
209+
t.Helper()
210+
211+
if length(object) == 0 {
212+
return Failf(t, "%sShould not be empty\n object: %#v", message(messages), object)
213+
}
214+
215+
return true
216+
}
217+
139218
// Contains asserts that object contains given element.
140219
//
141220
// Works with strings, arrays, slices, maps values and channels.
@@ -167,21 +246,25 @@ func NotContains[S Iterable[E], E Comparable](t Testing, object S, element E, me
167246
}
168247

169248
// Error asserts that error is NOT nil.
249+
//
250+
// A typed nil error (e.g. (*MyError)(nil)) is treated as nil.
170251
func Error(t Testing, err error, messages ...string) bool {
171252
t.Helper()
172253

173-
if err == nil {
254+
if isNilError(err) {
174255
return Failf(t, "%sShould be error", message(messages))
175256
}
176257

177258
return true
178259
}
179260

180261
// NoError asserts that error is nil.
262+
//
263+
// A typed nil error (e.g. (*MyError)(nil)) is treated as nil.
181264
func NoError(t Testing, err error, messages ...string) bool {
182265
t.Helper()
183266

184-
if err != nil {
267+
if !isNilError(err) {
185268
return Failf(t, "%sShould not be error\n error: %#v", message(messages), err)
186269
}
187270

@@ -210,23 +293,6 @@ func NotErrorIs(t Testing, err error, target error, messages ...string) bool {
210293
return true
211294
}
212295

213-
// EqualJSON asserts that JSON strings are equal.
214-
func EqualJSON(t Testing, actual, expected string, messages ...string) bool {
215-
t.Helper()
216-
217-
var actualJSON, expectedJSON any
218-
219-
if err := json.Unmarshal([]byte(actual), &actualJSON); err != nil {
220-
return Failf(t, "%sShould be valid JSON\n actual: %s\n err: %v", message(messages), actual, err)
221-
}
222-
223-
if err := json.Unmarshal([]byte(expected), &expectedJSON); err != nil {
224-
return Failf(t, "%sShould be valid JSON\nexpected: %s\n err: %v", message(messages), expected, err)
225-
}
226-
227-
return Equal(t, actualJSON, expectedJSON)
228-
}
229-
230296
// Matches asserts that a string matches the given regular expression.
231297
func Matches(t Testing, actual, pattern string, messages ...string) bool {
232298
t.Helper()
@@ -259,6 +325,23 @@ func NotMatches(t Testing, actual, pattern string, messages ...string) bool {
259325
return true
260326
}
261327

328+
// EqualJSON asserts that JSON strings are equal.
329+
func EqualJSON(t Testing, actual, expected string, messages ...string) bool {
330+
t.Helper()
331+
332+
var actualJSON, expectedJSON any
333+
334+
if err := json.Unmarshal([]byte(actual), &actualJSON); err != nil {
335+
return Failf(t, "%sShould be valid JSON\n actual: %s\n err: %v", message(messages), actual, err)
336+
}
337+
338+
if err := json.Unmarshal([]byte(expected), &expectedJSON); err != nil {
339+
return Failf(t, "%sShould be valid JSON\nexpected: %s\n err: %v", message(messages), expected, err)
340+
}
341+
342+
return Equal(t, actualJSON, expectedJSON)
343+
}
344+
262345
// JSON asserts that object can be marshaled to expected JSON string.
263346
func JSON(t Testing, actual any, expected string, messages ...string) bool {
264347
t.Helper()

0 commit comments

Comments
 (0)