Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,19 @@

# gunit

## v2

[![GoDoc](https://godoc.org/github.com/smarty/gunit/v2?status.svg)](http://godoc.org/github.com/smarty/gunit/v2)

Installation:

```
$ go get github.com/smarty/gunit/v2
```


## v1

[![GoDoc](https://godoc.org/github.com/smarty/gunit?status.svg)](http://godoc.org/github.com/smarty/gunit)

Installation:
Expand Down Expand Up @@ -39,8 +52,8 @@ import (
"time"
"testing"

"github.com/smarty/gunit/"
"github.com/smarty/gunit/assert/should"
"github.com/smarty/gunit/v2"
"github.com/smarty/gunit/v2/should"
)

func TestExampleFixture(t *testing.T) {
Expand Down
1 change: 1 addition & 0 deletions fixture.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Package gunit provides "testing" package hooks and convenience
// functions for writing tests in an xUnit style.
// See the README file and the examples folder for examples.
// Deprecated: use github.com/smarty/gunit/v2 instead.
package gunit

import (
Expand Down
12 changes: 12 additions & 0 deletions v2/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Contributing

In general, the code posted to the [Smarty github organization](https://github.com/smarty) is created to solve specific problems at Smarty that are ancillary to our core products in the address verification industry and may or may not be useful to other organizations or developers. Our reason for posting said code isn't necessarily to solicit feedback or contributions from the community but more as a showcase of some of the approaches to solving problems we have adopted.

Having stated that, we do consider issues raised by other githubbers as well as contributions submitted via pull requests. When submitting such a pull request, please follow these guidelines:

- _Look before you leap:_ If the changes you plan to make are significant, it's in everyone's best interest for you to discuss them with a Smarty team member prior to opening a pull request.
- _License and ownership:_ If modifying the `LICENSE.md` file, limit your changes to fixing typographical mistakes. Do NOT modify the actual terms in the license or the copyright by **Smarty, LLC**. Code submitted to Smarty projects becomes property of Smarty and must be compatible with the associated license.
- _Testing:_ If the code you are submitting resides in packages/modules covered by automated tests, be sure to add passing tests that cover your changes and assert expected behavior and state. Submit the additional test cases as part of your change set.
- _Style:_ Match your approach to **naming** and **formatting** with the surrounding code. Basically, the code you submit shouldn't stand out.
- "Naming" refers to such constructs as variables, methods, functions, classes, structs, interfaces, packages, modules, directories, files, etc...
- "Formatting" refers to such constructs as whitespace, horizontal line length, vertical function length, vertical file length, indentation, curly braces, etc...
25 changes: 25 additions & 0 deletions v2/LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
MIT License

Copyright (c) 2026 Smarty

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.

NOTE: Various optional and subordinate components carry their own licensing
requirements and restrictions. Use of those components is subject to the terms
and conditions outlined the respective license of each component.
14 changes: 14 additions & 0 deletions v2/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/make -f

test: fmt
go test -timeout=1s -race -covermode=atomic ./...

fmt:
go mod tidy && go fmt ./...

compile:
go build ./...

build: test compile

.PHONY: fmt test compile build
38 changes: 38 additions & 0 deletions v2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#### SMARTY DISCLAIMER: Subject to the terms of the associated license agreement, this software is freely available for your use. This software is FREE, AS IN PUPPIES, and is a gift. Enjoy your new responsibility. This means that while we may consider enhancement requests, we may or may not choose to entertain requests at our sole and absolute discretion.

[![GoDoc](https://godoc.org/github.com/smarty/gunit/v2?status.svg)](http://godoc.org/github.com/smarty/gunit/v2)

# gunit (v2)

## Installation

```
$ go get github.com/smarty/gunit/v2
```

## Usage

For users of JetBrains IDEs, here's LiveTemplate you can use for generating the scaffolding for a new fixture:

- Abbreviation: `fixture`
- Description: `Generate gunit Fixture boilerplate`
- Template Text:

```
func Test$NAME$Fixture(t *testing.T) {
gunit.Run(new($NAME$Fixture), t)
}

type $NAME$Fixture struct {
*gunit.Fixture
}

func (this *$NAME$Fixture) Setup() {
}

func (this *$NAME$Fixture) Test$END$() {
}

```

**NOTE:** _Be sure to specify that this LiveTemplate is applicable in Go files._
74 changes: 74 additions & 0 deletions v2/assert/better/better.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Package better provides the same assertions as the should package, but the
// error returned in failure conditions results in a call to *testing.T.Fatal(),
// halting the currently running test.
package better

import (
"fmt"

"github.com/smarty/gunit/v2/assert"
"github.com/smarty/gunit/v2/assert/should"
)

var (
BeChronological = wrapFatal(should.BeChronological)
BeEmpty = wrapFatal(should.BeEmpty)
BeFalse = wrapFatal(should.BeFalse)
BeGreaterThan = wrapFatal(should.BeGreaterThan)
BeGreaterThanOrEqualTo = wrapFatal(should.BeGreaterThanOrEqualTo)
BeIn = wrapFatal(should.BeIn)
BeLessThan = wrapFatal(should.BeLessThan)
BeLessThanOrEqualTo = wrapFatal(should.BeLessThanOrEqualTo)
BeNil = wrapFatal(should.BeNil)
BeTrue = wrapFatal(should.BeTrue)
Contain = wrapFatal(should.Contain)
EndWith = wrapFatal(should.EndWith)
Equal = wrapFatal(should.Equal)
HappenAfter = wrapFatal(should.HappenAfter)
HappenBefore = wrapFatal(should.HappenBefore)
HappenOn = wrapFatal(should.HappenOn)
HappenWithin = wrapFatal(should.HappenWithin)
HaveLength = wrapFatal(should.HaveLength)
Panic = wrapFatal(should.Panic)
StartWith = wrapFatal(should.StartWith)
WrapError = wrapFatal(should.WrapError)
)

// NOT constrains all negated assertions to their own 'namespace'.
var NOT = struct {
BeChronological assert.Assertion
BeEmpty assert.Assertion
BeGreaterThan assert.Assertion
BeGreaterThanOrEqualTo assert.Assertion
BeIn assert.Assertion
BeLessThan assert.Assertion
BeLessThanOrEqualTo assert.Assertion
BeNil assert.Assertion
Contain assert.Assertion
Equal assert.Assertion
HappenOn assert.Assertion
Panic assert.Assertion
}{
BeChronological: wrapFatal(should.NOT.BeChronological),
BeEmpty: wrapFatal(should.NOT.BeEmpty),
BeGreaterThan: wrapFatal(should.NOT.BeGreaterThan),
BeGreaterThanOrEqualTo: wrapFatal(should.NOT.BeGreaterThanOrEqualTo),
BeIn: wrapFatal(should.NOT.BeIn),
BeLessThan: wrapFatal(should.NOT.BeLessThan),
BeLessThanOrEqualTo: wrapFatal(should.NOT.BeLessThanOrEqualTo),
BeNil: wrapFatal(should.NOT.BeNil),
Contain: wrapFatal(should.NOT.Contain),
Equal: wrapFatal(should.NOT.Equal),
HappenOn: wrapFatal(should.NOT.HappenOn),
Panic: wrapFatal(should.NOT.Panic),
}

func wrapFatal(original assert.Assertion) assert.Assertion {
return func(actual any, expected ...any) error {
err := original(actual, expected...)
if err != nil {
return fmt.Errorf("%w %w", should.ErrFatalAssertionFailure, err)
}
return nil
}
}
28 changes: 28 additions & 0 deletions v2/assert/better/better_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package better_test

import (
"testing"

"github.com/smarty/gunit/v2/assert"
"github.com/smarty/gunit/v2/assert/better"
"github.com/smarty/gunit/v2/assert/should"
)

func TestWrapFatalSuccess(t *testing.T) {
err := better.Equal(1, 1)
assert.So(t, err, should.BeNil)
}
func TestWrapFatalFailure(t *testing.T) {
err := better.Equal(1, 2)
assert.So(t, err, should.WrapError, should.ErrFatalAssertionFailure)
assert.So(t, err, should.WrapError, should.ErrAssertionFailure)
}
func TestWrapFatalSuccess_NOT(t *testing.T) {
err := better.NOT.Equal(1, 2)
assert.So(t, err, should.BeNil)
}
func TestWrapFatalFailure_NOT(t *testing.T) {
err := better.NOT.Equal(1, 1)
assert.So(t, err, should.WrapError, should.ErrFatalAssertionFailure)
assert.So(t, err, should.WrapError, should.ErrAssertionFailure)
}
60 changes: 60 additions & 0 deletions v2/assert/should/assert_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package should_test

import (
"errors"
"fmt"
"path/filepath"
"runtime"
"testing"

"github.com/smarty/gunit/v2/assert"
"github.com/smarty/gunit/v2/assert/should"
)

type Assertion struct{ *testing.T }

func NewAssertion(t *testing.T) *Assertion {
return &Assertion{T: t}
}
func (this *Assertion) ExpectedCountInvalid(actual any, assertion assert.Assertion, expected ...any) {
this.Helper()
this.err(actual, assertion, expected, should.ErrExpectedCountInvalid)
}
func (this *Assertion) TypeMismatch(actual any, assertion assert.Assertion, expected ...any) {
this.Helper()
this.err(actual, assertion, expected, should.ErrTypeMismatch)
}
func (this *Assertion) KindMismatch(actual any, assertion assert.Assertion, expected ...any) {
this.Helper()
this.err(actual, assertion, expected, should.ErrKindMismatch)
}
func (this *Assertion) Fail(actual any, assertion assert.Assertion, expected ...any) {
this.Helper()
this.err(actual, assertion, expected, should.ErrAssertionFailure)
}
func (this *Assertion) Pass(actual any, assertion assert.Assertion, expected ...any) {
this.Helper()
this.err(actual, assertion, expected, nil)
}
func (this *Assertion) err(actual any, assertion assert.Assertion, expected []any, expectedErr error) {
this.Helper()
_, file, line, _ := runtime.Caller(2)
subTest := fmt.Sprintf("%s:%d", filepath.Base(file), line)
this.Run(subTest, func(t *testing.T) {
t.Helper()
err := assertion(actual, expected...)
if !errors.Is(err, expectedErr) {
t.Errorf("[FAIL]\n"+
"expected: %v\n"+
"actual: %v",
expected,
actual,
)
} else if testing.Verbose() {
t.Log(
"\n", err, "\n",
"(above error report printed for visual inspection)",
)
}
})
}
40 changes: 40 additions & 0 deletions v2/assert/should/be_chronological.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package should

import (
"errors"
"sort"
"time"
)

// BeChronological asserts whether actual is a []time.Time and
// whether the values are in chronological order.
func BeChronological(actual any, expected ...any) error {
err := validateExpected(0, expected)
if err != nil {
return err
}

var t []time.Time
err = validateType(actual, t)
if err != nil {
return err
}

times := actual.([]time.Time)
if sort.SliceIsSorted(times, func(i, j int) bool { return times[i].Before(times[j]) }) {
return nil
}
return failure("expected to be chronological: %v", times)
}

// BeChronological (negated!)
func (negated) BeChronological(actual any, expected ...any) error {
err := BeChronological(actual, expected...)
if errors.Is(err, ErrAssertionFailure) {
return nil
}
if err != nil {
return err
}
return failure("want non-chronological times, got chronological times:", actual)
}
44 changes: 44 additions & 0 deletions v2/assert/should/be_chronological_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package should_test

import (
"testing"
"time"

"github.com/smarty/gunit/v2/assert/should"
)

func TestShouldBeChronological(t *testing.T) {
assert := NewAssertion(t)

assert.ExpectedCountInvalid("actual", should.BeChronological, "EXTRA")

assert.TypeMismatch(42, should.BeChronological)

var (
a = time.Now()
b = a.Add(time.Nanosecond)
c = b.Add(time.Nanosecond)
)
assert.Pass([]time.Time{}, should.BeChronological)
assert.Pass([]time.Time{a, a, a}, should.BeChronological)
assert.Pass([]time.Time{a, b, c}, should.BeChronological)
assert.Fail([]time.Time{a, c, b}, should.BeChronological)
}

func TestShouldNOTBeChronological(t *testing.T) {
assert := NewAssertion(t)

assert.ExpectedCountInvalid("actual", should.NOT.BeChronological, "EXTRA")

assert.TypeMismatch(42, should.NOT.BeChronological)

var (
a = time.Now()
b = a.Add(time.Nanosecond)
c = b.Add(time.Nanosecond)
)
assert.Fail([]time.Time{}, should.NOT.BeChronological)
assert.Fail([]time.Time{a, a, a}, should.NOT.BeChronological)
assert.Fail([]time.Time{a, b, c}, should.NOT.BeChronological)
assert.Pass([]time.Time{a, c, b}, should.NOT.BeChronological)
}
Loading