
Product
Introducing Repository Labels and Security Policies
Socket is introducing a new way to organize repositories and apply repository-specific security policies.
github.com/rdrdr/hamcrest
This has not been maintained and/or updated since 2011. Perhaps consider corbym/gocrest, instead.
Hamcrest is a fluent framework for declarative Matcher objects that, when applied to input values, produce self-describing results.
To install, run make install
from the same directory as this
README.md
file.
hamcrest.go
comes in several packages that you assemble to fit your needs:
hamcrest/base
: Defines the types Matcher
, Result
and SelfDescribing
and provides factory functions to create them. (Unless you want to define
your own custom Matchers, you'll only use this indirectly.)
hamcrest/core
: Defines a set of Matchers for doing basic comparisons,
equality testing, nil checking, and grouping/composition matchers.
hamcrest/slices
: Matchers on slices, such as EachElem
, AnyElem
,
ToLen
, Empty
.
hamcrest/reflect
: Matchers using type reflection, such as ToType
,
SameTypeAs
, SliceOf
, MapOf
, etc.
hamcrest/strings
: Matchers for strings.
hamcrest/asserter
: Defines an Asserter
that can be used in conjunction
with Hamcrest Matchers to produce helpful logging messages at runtime
(to stdout, stderr, or any object that implements io.Writer) or in
unit tests (using testing.T
from Go's standard testing
package).
Note: the asserter
package isn't really part of Hamcrest: it's just
a handy way of using the Hamcrest results in conjunction with the
standard Go testing package.
You may also choose to write your own Matchers (see Custom matchers, below).
To use Hamcrest matchers, create an Asserter
and use it to
Check
or Assert
that values meet the criteria of those
matchers:
func TestPoint(t *testing.T) {
p := Point(3, 4)
we := asserter.Using(t)
we.AssertThat(p.X, EqualTo(3).Comment("x coord"))
we.AssertThat(p.Y, EqualTo(4).Comment("y coord"))
we.CheckThat(p, ToString(EqualTo("[3, 4]")))
we.CheckThat(p.Magnitude(), EqualTo(5).Comment("magnitude"))
}
(Assert
methods fail immediately, as testing.T.FailNow()
does,
while Check
methods defer failure, as testing.T.Fail()
does.)
The AssertThat
and CheckThat
functions are designed to create
conditional checks that read fluently as self-commenting code, and
are self-describing when failures occur. For example, the above
test might fail with this message:
FAILURE on input &Point{X:3, Y:4}
Did not match ToString(EqualTo([3, 4]))
Because: String() was "[4, 3]"
Did not match EqualTo[[3, 4]]
Because: "[4, 3]" was not equal to "[3, 4]"
Or: FAILURE on input 5 Did not match EqualTo(5) Because: uint 5 could not be compared to int 5 Comment: magnitude
Note that the majority of the text descriptions are generated automatically by the matchers. For typical uses of Hamcrest matchers, the code is largely self-documenting, and the error messages are detailed.
Effort invested in good self-describing matchers can be leveraged across many tests.
Just as in the testing example, create an Asserter
, but use a factory
method such as UsingStderr()
, which returns an Asserter
that logs
problems to stderr and calls panic
on FailNow
:
import (
"github.com/rdrdr/hamcrest/asserter"
)
var we = asserter.UsingStderr()
Use that asserter during init() to make sure globals are properly initialized:
import (
"github.com/rdrdr/hamcrest/asserter"
"github.com/rdrdr/hamcrest/slices"
. "github.com/rdrdr/hamcrest/core"
"github.com/rdrdr/hamcrest/strings"
)
var we = asserter.UsingStderr()
type Server struct {
hostname string
port uint16
}
var servers = []Server {
{ "news.foo.com", 8000 },
{ "news.bar.com", 8888 },
}
func init() {
ToHostname := Applying(func(s Server) string {
return s.hostname
}, "TransformServerToHostname")
IsInOneOfOurDomains := AnyOf(strings.HasSuffix(".foo.com"),
strings.HasSuffix(".bar.com"))
we.FailNowUnless(servers,
slices.EachElem(ToHostname(IsInOneOfOurDomains)))
}
Or use the asserter at runtime to guarantee that a method's preconditions are met:
var IsValidFilenameForTextFile = AllOf(
strings.HasSuffix(".txt").Comment("Must have .txt extension."),
Not(strings.HasPattern("[ \\t\\n\\r]")).Comment("whitespace not permitted"),
Not(strings.HasPattern("[:////\\\\]")).Comment("path separators not permitted"))
func WriteTo(filename string) bool {
we.AssertThat(filename, IsValidFilenameForTextFile)
// Use filename here.
}
Or use the asserter with defer
to program by contract, knowing that you
can later swap out your typical Asserter
with an
asserter.ThatDoesNothing()
to bypass those checks:
IsWellFormedMD5 := AllOf(
ToLen(EqualTo(32)).Comment("MD5 hashes have length 32"),
strings.HasPattern("^([0-9][A-F][a-f])+$").Comment("hex digits")))
func MD5(data []byte) (result string) {
we.AssertNonNil(data)
defer we.AssertThat(result, IsWellFormedMD5)
// calculate MD5 of data
}
Or use it during development to write your tests in the same file as your code:
func EncodePigLatin(input string) string {
...implementation...
}
func init() {
we := asserter.UsingStderr()
ToEncoded := Applying(EncodePigLatin, "in PigLatin form")
we.AssertThat("simple", ToEncoded(EqualTo("imple-say")))
we.AssertThat("Capital"), ToEncoded(EqualTo("Apital-Cay")))
we.AssertThat("prefix"), ToEncoded(EqualTo("efix-pray")))
we.AssertThat("oops"), ToEncoded(EqualTo("oops-ay")))
we.AssertThat("your psychotic y!"),
ToEncoded(EqualTo("our-yay ychotic-psay y-ay!")))
}
At some future point, this structure will make it easier to cut-and-paste
each init()
block into your testing suite. While moving the block over,
replace:
func init() {
we := asserter.UsingStderr()
...
With an Asserter
that uses the testing infrastructure, instead:
func Test_EncodePigLatin(t *testing.T) {
we := asserter.Using(t)
...
Hamcrest is designed to be a fluent library. The Go syntax requires external symbols be preceded by their package name, which can lead to occasionally awkward constructions: we.CheckThat("foobar", core.AllOf(strings.HasPrefix("foo"), strings.HasSuffix("bar")))
To avoid this clunkiness, Hamcrest matchers are generated by functions that you can assign to local names: AllOf := core.AllOf StartsWith := strings.HasPrefix EndsWith := strings.HasSuffix we.CheckThat("foobar", AllOf(StartsWith("foo"), EndsWith("bar")))
Note: Hamcrest matchers allocate Description and Result objects to explain in great detail why they did or did not match. However, these objects are lazily evaluated. Users should be generally aware that there is an object allocation cost to using Hamcrest matchers, but there is (generally) no string construction cost unless a Hamcrest Matcher or Result is explicitly asked to self-describe.
Still, users who are particularly sensitive to performance concerns may wish to think carefully before using Hamcrest matchers in performance-critical bottlenecks.
Hamcrest comes with a library of useful matchers. Here are some of the most common ones.
Anything
- matches any input.True
- only matches bool true
.False
- only matches bool false
.Not(matcher)
- logical not of matcher
.If(m1).Then(m2)
- checks that whenever m1
matches, so does m2
.IfAndOnlyIf(m1).Then(m2)
- checks that m1
and m2
both match/don't match.EqualTo(y)
- matches any value x
where x == y
is legal and true.NotEqualTo(y)
- matches any value x
where x != y
is legal and true.DeepEqualTo(y)
- matches any value x
where reflect.DeepEquals(x, y)
is true.GreaterThan(y)
- matches any value x
where x > y
is legal and true.GreaterThanOrEqualTo(y)
- matches any value x
where x <= y
is legal and true.LessThan(y)
- matches any value x
where x < y
is legal and true.LessThanOrEqualTo(y)
- matches any value x
where x <= y
is legal and true.Nil
- matches values of any type with an IsNil() bool
method that returns true for the given object.NonNil
- matches values of any type with an IsNil() bool
method that returns false for the given object.AnyOf(matchers...)
- short-circuiting n-ary logical Or.AllOf(matchers...)
- short-circuiting n-ary logical And.Hamcrest strives to make your tests as readable as possible. For example,
the Is
matcher is a wrapper that doesn't add any extra behavior to the
underlying matcher. The following assertions are equivalent:
we.AssertThat(x, EqualTo(y));
we.AssertThat(x, Is(EqualTo(y)))
Similarly, it is possible to simulate common logical conditions using the logic
package for readability:
Is(matcher)
- equivalent to matcher
Both(matcher1).And(matcher2)
- short-circuiting logical And
, equivalent to AllOf(matcher1, matcher2)
Either(matcher1).Or(matcher2)
- short-circuiting logical Or
, equivalent to AnyOf(matcher1, matcher2)
Neither(matcher1).Nor(matcher2)
- short-circuiting logical Nor
If(matcher1).Then(matcher2)
- short-circuiting logical If/Then
Iff(matcher1).Then(matcher2)
- logical IfAndOnlyIf
(note: iff never short-circuits)
Either(matcher1).Xor(matcher)
- logical Xor
(note: xor never short-circuits)
Example:
func IsMultipleOf(k int) *base.Matcher {
match := func(n int) bool {
return n % k == 0
}
return base.NewMatcherf(match, "multiple of %v", k)
}
And used:
we.CheckThat(recordSize, IsMultipleOf(8).Comment(
"profiling suggests better performance than multiple of 4"))
Or, if you want more control over the error messages:
func IsMultipleOf(k int) *base.Matcher {
match := func(actual interface{}) *base.Result {
if n, ok := actual.(int); ok {
if remainder := n % k; remainder != 0 {
return base.NewResultf(false,
"was not a multiple of %v (remainder %v)", k, remainder)
}
return base.NewResultf(true, "was a multiple of %v", k)
}
return base.NewResultf(false, "was a %T, expected an int!", actual)
}
return base.NewMatcherf(match, "multiple of %v", k)
}
FAQs
Unknown package
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Product
Socket is introducing a new way to organize repositories and apply repository-specific security policies.
Research
Security News
Socket researchers uncovered malicious npm and PyPI packages that steal crypto wallet credentials using Google Analytics and Telegram for exfiltration.
Product
Socket now supports .NET, bringing supply chain security and SBOM accuracy to NuGet and MSBuild-powered C# projects.