Skip to content

Commit

Permalink
Add a d.IsNot assertion
Browse files Browse the repository at this point in the history
  • Loading branch information
autarch committed Mar 27, 2021
1 parent 4a4ea9a commit acd4509
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 4 deletions.
1 change: 1 addition & 0 deletions Changes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## 0.0.6 2021-03-27

* Added a `d.IsNot` assertion.
* Clarified that `d.ValueIs` only accepts literal values as its second
argument, not a `Comparer`.

Expand Down
62 changes: 61 additions & 1 deletion pkg/detest/basic.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import (
"strconv"
)

// ExactEqualityComparer implements exact comparison of two values.
// ExactEqualityComparer implements exact comparison of two values, checking
// that they're equal.
type ExactEqualityComparer struct {
expect interface{}
}
Expand Down Expand Up @@ -52,6 +53,22 @@ func (d *D) Passes(actual interface{}, expect Comparer, args ...interface{}) boo
return d.ok(argsToName(args, "unnamed d.Passes call"))
}

// IsNot tests that two variables are not exactly equal. The first variable is
// the actual variable and the second is what is expected. The `expect` must
// be a literal value.
//
// The final arguments follow the same rules as `d.Is`.
//
// Under the hood this is implemented with the ExactInequalityComparer.
func (d *D) IsNot(actual, expect interface{}, args ...interface{}) bool {
d.ResetState()
d.PushActual(actual)
defer d.PopActual()

d.NotEqual(expect).Compare(d)
return d.ok(argsToName(args, "unnamed d.IsNot call"))
}

// Require takes a boolean and calls t.Fatal if it's false. The typical use is
// to write something like:
//
Expand Down Expand Up @@ -112,6 +129,49 @@ func (eec ExactEqualityComparer) Compare(d *D) {
d.AddResult(result)
}

// ExactInequalityComparer implements exact comparison of two values, checking
// that they're not equal.
type ExactInequalityComparer struct {
expect interface{}
}

// NotEqual takes an expected literal value and returns an
// ExactInequalityComparer for later use.
func (d *D) NotEqual(expect interface{}) ExactInequalityComparer {
return ExactInequalityComparer{expect}
}

// Compare compares the value in d.Actual() to the expected value passed to
// Equal().
func (eic ExactInequalityComparer) Compare(d *D) {
actual := d.Actual()
actualType := reflect.TypeOf(actual)
d.PushPath(d.NewPath(describeType(actualType), 1, "detest.(*D).NotEqual"))
defer d.PopPath()

expect := eic.expect
result := result{
actual: newValue(actual),
expect: newValue(expect),
op: "==",
}

expectType := reflect.TypeOf(expect)
if actualType == expectType {
result.pass = !exactCompare(actual, expect)
if !result.pass {
result.where = inValue
}
} else {
result.pass = !nilValuesAreEqual(actual, expect)
if result.pass {
result.where = inType
}
}

d.AddResult(result)
}

func nilValuesAreEqual(actual, expect interface{}) bool {
actualValue := reflect.ValueOf(actual)
expectValue := reflect.ValueOf(expect)
Expand Down
58 changes: 55 additions & 3 deletions pkg/detest/basic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func TestIs(t *testing.T) {
mT.AssertCalled(t, "Fail")
})

t.Run("Equivalent values do not compare as true", func(t *testing.T) {
t.Run("Equivalent values do not compare as equal", func(t *testing.T) {
mT := new(mockT)
d := NewWithOutput(mT, mT)
d.Is(int32(1), int64(2), "int32(1) == int64(2)")
Expand Down Expand Up @@ -59,6 +59,38 @@ func TestIs(t *testing.T) {
})
}

func TestIsNot(t *testing.T) {
t.Run("Passing test", func(t *testing.T) {
mT := new(mockT)
d := NewWithOutput(mT, mT)
d.IsNot(1, 42, "1 != 42")
mT.AssertNotCalled(t, "Fail")
mT.AssertCalled(t, "WriteString", "Assertion ok: 1 != 42\n")
})

t.Run("Failing test", func(t *testing.T) {
mT := new(mockT)
d := NewWithOutput(mT, mT)
d.IsNot(1, 1, "1 != 1")
mT.AssertCalled(t, "Fail")
})

t.Run("Equivalent values do not compare as equal", func(t *testing.T) {
mT := new(mockT)
d := NewWithOutput(mT, mT)
d.IsNot(int32(1), int64(2), "int32(1) != int64(2)")
mT.AssertNotCalled(t, "Fail")
mT.AssertCalled(t, "WriteString", "Assertion ok: int32(1) != int64(2)\n")
})

t.Run("Can handle nil", func(t *testing.T) {
mT := new(mockT)
d := NewWithOutput(mT, mT)
d.IsNot(nil, nil, "nil != nil")
mT.AssertCalled(t, "Fail")
})
}

// Go has this (so great) thing where there are different types of nils. A
// bare `nil` is not the same as a nil which comes from a types but
// uninitialized variable. But they should compare the same. Previously detest
Expand Down Expand Up @@ -95,6 +127,20 @@ func TestIsNilTypeHandling(t *testing.T) {
mT.AssertCalled(t, "WriteString", "Assertion ok: nil == uninit\n")
})

t.Run(fmt.Sprintf("IsNot(%s == nil)", desc), func(t *testing.T) {
mT := new(mockT)
d := NewWithOutput(mT, mT)

d.IsNot(v, nil, "uninit != nil")
mT.AssertCalled(t, "Fail")
})
t.Run(fmt.Sprintf("IsNot(nil == %s)", desc), func(t *testing.T) {
mT := new(mockT)
d := NewWithOutput(mT, mT)
d.IsNot(nil, v, "nil != uninit")
mT.AssertCalled(t, "Fail")
})

t.Run(fmt.Sprintf("ValueIs(%s == nil)", desc), func(t *testing.T) {
mT := new(mockT)
d := NewWithOutput(mT, mT)
Expand All @@ -120,13 +166,19 @@ func TestIsNilTypeHandling(t *testing.T) {
for _, v2 := range []interface{}{sl, ma, f, p, up, c} {
desc2 := describe(v2)
if desc != desc2 {
t.Run(fmt.Sprintf("Is(%s != %s)", desc, desc2), func(t *testing.T) {
t.Run(fmt.Sprintf("Is(%s == %s)", desc, desc2), func(t *testing.T) {
mT := new(mockT)
d := NewWithOutput(mT, mT)
d.Is(v, v2, "nil == uninit")
mT.AssertCalled(t, "Fail")
})
t.Run(fmt.Sprintf("ValueIs(%s != %s)", desc, desc2), func(t *testing.T) {
t.Run(fmt.Sprintf("IsNot(%s == %s)", desc, desc2), func(t *testing.T) {
mT := new(mockT)
d := NewWithOutput(mT, mT)
d.IsNot(v, v2, "nil != uninit")
mT.AssertNotCalled(t, "Fail")
})
t.Run(fmt.Sprintf("ValueIs(%s == %s)", desc, desc2), func(t *testing.T) {
mT := new(mockT)
d := NewWithOutput(mT, mT)
d.ValueIs(v, v2, "nil == uninit")
Expand Down

0 comments on commit acd4509

Please sign in to comment.