Documentation
¶
Overview ¶
Package assert provides functional assertion helpers for testing.
This package wraps testify/assert functions in a Reader monad pattern, allowing for composable and functional test assertions. Each assertion returns a Reader that takes a *testing.T and performs the assertion.
Data Last Principle ¶
This package follows the "data last" functional programming principle, where the data being operated on comes as the last parameter in a chain of function applications. This design enables several powerful functional programming patterns:
**Partial Application**: You can create reusable assertion functions by providing configuration parameters first, leaving the data and testing context for later.
**Function Composition**: Assertions can be composed and combined before being applied to actual data.
**Point-Free Style**: You can pass assertion functions around without immediately providing the data they operate on.
The general pattern is:
assert.Function(config)(data)(testingContext)
↑ ↑ ↑
expected actual *testing.T (always last)
For single-parameter assertions:
assert.Function(data)(testingContext)
↑ ↑
actual *testing.T (always last)
Examples of "data last" in action:
// Multi-parameter: expected value → actual value → testing context
assert.Equal(42)(result)(t)
assert.ArrayContains(3)(numbers)(t)
// Single-parameter: data → testing context
assert.NoError(err)(t)
assert.ArrayNotEmpty(arr)(t)
// Partial application - create reusable assertions
isPositive := assert.That(func(n int) bool { return n > 0 })
// Later, apply to different values:
isPositive(42)(t) // Passes
isPositive(-5)(t) // Fails
// Composition - combine assertions before applying data
validateUser := func(u User) assert.Reader {
return assert.AllOf([]assert.Reader{
assert.Equal("Alice")(u.Name),
assert.That(func(age int) bool { return age >= 18 })(u.Age),
})
}
validateUser(user)(t)
The package supports:
- Equality and inequality assertions
- Collection assertions (arrays, maps, strings)
- Error handling assertions
- Result type assertions
- Custom predicate assertions
- Composable test suites
Example:
func TestExample(t *testing.T) {
value := 42
assert.Equal(42)(value)(t) // Curried style
// Composing multiple assertions
arr := []int{1, 2, 3}
assertions := assert.AllOf([]assert.Reader{
assert.ArrayNotEmpty(arr),
assert.ArrayLength[int](3)(arr),
assert.ArrayContains(2)(arr),
})
assertions(t)
}
Example (AllOf) ¶
Example_allOf demonstrates combining multiple assertions
package main
import (
"testing"
"github.com/IBM/fp-go/v2/assert"
)
func main() {
var t *testing.T // placeholder for example
type User struct {
Name string
Age int
Active bool
}
user := User{Name: "Alice", Age: 30, Active: true}
// Combine multiple assertions
assertions := assert.AllOf([]assert.Reader{
assert.Equal("Alice")(user.Name),
assert.Equal(30)(user.Age),
assert.Equal(true)(user.Active),
})
assertions(t)
}
Example (ArrayAssertions) ¶
Example_arrayAssertions demonstrates array-related assertions
package main
import (
"testing"
"github.com/IBM/fp-go/v2/assert"
)
func main() {
var t *testing.T // placeholder for example
numbers := []int{1, 2, 3, 4, 5}
// Check array is not empty
assert.ArrayNotEmpty(numbers)(t)
// Check array length
assert.ArrayLength[int](5)(numbers)(t)
// Check array contains a value
assert.ArrayContains(3)(numbers)(t)
}
Example (BasicAssertions) ¶
Example_basicAssertions demonstrates basic equality and inequality assertions
package main
import (
"testing"
"github.com/IBM/fp-go/v2/assert"
)
func main() {
// This would be in a real test function
var t *testing.T // placeholder for example
// Basic equality
value := 42
assert.Equal(42)(value)(t)
// String equality
name := "Alice"
assert.Equal("Alice")(name)(t)
// Inequality
assert.NotEqual(10)(value)(t)
}
Example (ComposableAssertions) ¶
Example_composableAssertions demonstrates building complex assertions
package main
import (
"testing"
"github.com/IBM/fp-go/v2/assert"
)
func main() {
var t *testing.T // placeholder for example
type Config struct {
Host string
Port int
Timeout int
Retries int
}
config := Config{
Host: "localhost",
Port: 8080,
Timeout: 30,
Retries: 3,
}
// Create focused assertions for each field
validHost := assert.Local(func(c Config) string { return c.Host })(
assert.StringNotEmpty,
)
validPort := assert.Local(func(c Config) int { return c.Port })(
assert.That(func(p int) bool { return p > 0 && p < 65536 }),
)
validTimeout := assert.Local(func(c Config) int { return c.Timeout })(
assert.That(func(t int) bool { return t > 0 }),
)
validRetries := assert.Local(func(c Config) int { return c.Retries })(
assert.That(func(r int) bool { return r >= 0 }),
)
// Combine all assertions
validConfig := assert.AllOf([]assert.Reader{
validHost(config),
validPort(config),
validTimeout(config),
validRetries(config),
})
validConfig(t)
}
Example (ErrorAssertions) ¶
Example_errorAssertions demonstrates error-related assertions
package main
import (
"errors"
"testing"
"github.com/IBM/fp-go/v2/assert"
)
func main() {
var t *testing.T // placeholder for example
// Assert no error
err := doSomethingSuccessful()
assert.NoError(err)(t)
// Assert error exists
err2 := doSomethingThatFails()
assert.Error(err2)(t)
}
// Helper functions for examples
func doSomethingSuccessful() error {
return nil
}
func doSomethingThatFails() error {
return errors.New("operation failed")
}
Example (Local) ¶
Example_local demonstrates focusing assertions on specific properties
package main
import (
"testing"
"github.com/IBM/fp-go/v2/assert"
)
func main() {
var t *testing.T // placeholder for example
type User struct {
Name string
Age int
}
// Create an assertion that checks if age is positive
ageIsPositive := assert.That(func(age int) bool { return age > 0 })
// Focus this assertion on the Age field of User
userAgeIsPositive := assert.Local(func(u User) int { return u.Age })(ageIsPositive)
// Now we can test the whole User object
user := User{Name: "Alice", Age: 30}
userAgeIsPositive(user)(t)
}
Example (MapAssertions) ¶
Example_mapAssertions demonstrates map-related assertions
package main
import (
"testing"
"github.com/IBM/fp-go/v2/assert"
)
func main() {
var t *testing.T // placeholder for example
config := map[string]int{
"timeout": 30,
"retries": 3,
"maxSize": 1000,
}
// Check map is not empty
assert.RecordNotEmpty(config)(t)
// Check map length
assert.RecordLength[string, int](3)(config)(t)
// Check map contains key
assert.ContainsKey[int]("timeout")(config)(t)
// Check map does not contain key
assert.NotContainsKey[int]("unknown")(config)(t)
}
Example (PredicateAssertions) ¶
Example_predicateAssertions demonstrates custom predicate assertions
package main
import (
"strings"
"testing"
"github.com/IBM/fp-go/v2/assert"
)
func main() {
var t *testing.T // placeholder for example
// Test if a number is positive
isPositive := func(n int) bool { return n > 0 }
assert.That(isPositive)(42)(t)
// Test if a string is uppercase
isUppercase := func(s string) bool { return s == strings.ToUpper(s) }
assert.That(isUppercase)("HELLO")(t)
// Test if a number is even
isEven := func(n int) bool { return n%2 == 0 }
assert.That(isEven)(10)(t)
}
Example (ResultAssertions) ¶
Example_resultAssertions demonstrates Result type assertions
package main
import (
"errors"
"testing"
"github.com/IBM/fp-go/v2/assert"
"github.com/IBM/fp-go/v2/result"
)
func main() {
var t *testing.T // placeholder for example
// Assert success
successResult := result.Of(42)
assert.Success(successResult)(t)
// Assert failure
failureResult := result.Left[int](errors.New("something went wrong"))
assert.Failure(failureResult)(t)
}
Example (RunAll) ¶
Example_runAll demonstrates running named test cases
package main
import (
"testing"
"github.com/IBM/fp-go/v2/assert"
)
func main() {
var t *testing.T // placeholder for example
testcases := map[string]assert.Reader{
"addition": assert.Equal(4)(2 + 2),
"multiplication": assert.Equal(6)(2 * 3),
"subtraction": assert.Equal(1)(3 - 2),
"division": assert.Equal(2)(10 / 5),
}
assert.RunAll(testcases)(t)
}
Index ¶
- Variables
- func FromOptional[S, T any](opt Optional[S, T]) reader.Reader[S, Reader]
- func FromPrism[S, T any](p Prism[S, T]) reader.Reader[S, Reader]
- func Local[R1, R2 any](f func(R2) R1) func(Kleisli[R1]) Kleisli[R2]
- func LocalL[S, T any](l Lens[S, T]) func(Kleisli[T]) Kleisli[S]
- type Kleisli
- func ArrayContains[T any](expected T) Kleisli[[]T]
- func ArrayLength[T any](expected int) Kleisli[[]T]
- func ContainsKey[T any, K comparable](expected K) Kleisli[map[K]T]
- func Equal[T any](expected T) Kleisli[T]
- func NotContainsKey[T any, K comparable](expected K) Kleisli[map[K]T]
- func NotEqual[T any](expected T) Kleisli[T]
- func RecordLength[K comparable, T any](expected int) Kleisli[map[K]T]
- func StringLength[K comparable, T any](expected int) Kleisli[string]
- func That[T any](pred Predicate[T]) Kleisli[T]
- type Lens
- type Optional
- type Predicate
- type Prism
- type Reader
- func AllOf(readers []Reader) Reader
- func ArrayNotEmpty[T any](arr []T) Reader
- func Error(err error) Reader
- func Failure[T any](res Result[T]) Reader
- func NoError(err error) Reader
- func RecordNotEmpty[K comparable, T any](mp map[K]T) Reader
- func RunAll(testcases map[string]Reader) Reader
- func StringNotEmpty(s string) Reader
- func Success[T any](res Result[T]) Reader
- type Result
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // Eq is the equal predicate checking if objects are equal Eq = eq.FromEquals(assert.ObjectsAreEqual) )
Functions ¶
func FromOptional ¶
FromOptional creates an assertion that checks if an Optional can successfully extract a value. An Optional is an optic that represents an optional reference to a subpart of a data structure.
This function is useful when you have an Optional optic and want to assert that the optional value is present (Some) rather than absent (None). The assertion passes if the Optional's GetOption returns Some, and fails if it returns None.
This enables property-focused testing where you verify that a particular optional field or sub-structure exists and is accessible.
Parameters:
- opt: An Optional optic that focuses from type S to type T
Returns:
- A Reader that asserts the optional value is present when applied to a value of type S
Example:
type Config struct {
Database *DatabaseConfig // Optional field
}
type DatabaseConfig struct {
Host string
Port int
}
// Create an Optional that focuses on the Database field
dbOptional := optional.MakeOptional(
func(c Config) option.Option[*DatabaseConfig] {
if c.Database != nil {
return option.Some(c.Database)
}
return option.None[*DatabaseConfig]()
},
func(c Config, db *DatabaseConfig) Config {
c.Database = db
return c
},
)
// Assert that the database config is present
hasDatabaseConfig := assert.FromOptional(dbOptional)
config := Config{Database: &DatabaseConfig{Host: "localhost", Port: 5432}}
hasDatabaseConfig(config)(t) // Passes
emptyConfig := Config{Database: nil}
hasDatabaseConfig(emptyConfig)(t) // Fails
func FromPrism ¶
FromPrism creates an assertion that checks if a Prism can successfully extract a value. A Prism is an optic used to select part of a sum type (tagged union or variant).
This function is useful when you have a Prism optic and want to assert that a value matches a specific variant of a sum type. The assertion passes if the Prism's GetOption returns Some (meaning the value is of the expected variant), and fails if it returns None (meaning the value is a different variant).
This enables variant-focused testing where you verify that a value is of a particular type or matches a specific condition within a sum type.
Parameters:
- p: A Prism optic that focuses from type S to type T
Returns:
- A Reader that asserts the prism successfully extracts when applied to a value of type S
Example:
type Result interface{ isResult() }
type Success struct{ Value int }
type Failure struct{ Error string }
func (Success) isResult() {}
func (Failure) isResult() {}
// Create a Prism that focuses on Success variant
successPrism := prism.MakePrism(
func(r Result) option.Option[int] {
if s, ok := r.(Success); ok {
return option.Some(s.Value)
}
return option.None[int]()
},
func(v int) Result { return Success{Value: v} },
)
// Assert that the result is a Success
isSuccess := assert.FromPrism(successPrism)
result1 := Success{Value: 42}
isSuccess(result1)(t) // Passes
result2 := Failure{Error: "something went wrong"}
isSuccess(result2)(t) // Fails
func Local ¶
func Local[R1, R2 any](f func(R2) R1) func(Kleisli[R1]) Kleisli[R2]
Local transforms a Reader that works on type R1 into a Reader that works on type R2, by providing a function that converts R2 to R1. This allows you to focus a test on a specific property or subset of a larger data structure.
This is particularly useful when you have an assertion that operates on a specific field or property, and you want to apply it to a complete object. Instead of extracting the property and then asserting on it, you can transform the assertion to work directly on the whole object.
Parameters:
- f: A function that extracts or transforms R2 into R1
Returns:
- A function that transforms a Reader[R1, Reader] into a Reader[R2, Reader]
Example:
type User struct {
Name string
Age int
}
// Create an assertion that checks if age is positive
ageIsPositive := assert.That(func(age int) bool { return age > 0 })
// Focus this assertion on the Age field of User
userAgeIsPositive := assert.Local(func(u User) int { return u.Age })(ageIsPositive)
// Now we can test the whole User object
user := User{Name: "Alice", Age: 30}
userAgeIsPositive(user)(t)
func LocalL ¶
func LocalL[S, T any](l Lens[S, T]) func(Kleisli[T]) Kleisli[S]
LocalL is similar to Local but uses a Lens to focus on a specific property. A Lens is a functional programming construct that provides a composable way to focus on a part of a data structure.
This function is particularly useful when you want to focus a test on a specific field of a struct using a lens, making the code more declarative and composable. Lenses are often code-generated or predefined for common data structures.
Parameters:
- l: A Lens that focuses from type S to type T
Returns:
- A function that transforms a Reader[T, Reader] into a Reader[S, Reader]
Example:
type Person struct {
Name string
Email string
}
// Assume we have a lens that focuses on the Email field
var emailLens = lens.Prop[Person, string]("Email")
// Create an assertion for email format
validEmail := assert.That(func(email string) bool {
return strings.Contains(email, "@")
})
// Focus this assertion on the Email property using a lens
validPersonEmail := assert.LocalL(emailLens)(validEmail)
// Test a Person object
person := Person{Name: "Bob", Email: "bob@example.com"}
validPersonEmail(person)(t)
Types ¶
type Kleisli ¶
func ArrayContains ¶
func ArrayContains[T any](expected T) Kleisli[[]T]
ArrayContains tests if a value is contained in an array.
Example:
func TestArrayContains(t *testing.T) {
numbers := []int{1, 2, 3, 4, 5}
assert.ArrayContains(3)(numbers)(t) // Passes
assert.ArrayContains(10)(numbers)(t) // Fails
names := []string{"Alice", "Bob", "Charlie"}
assert.ArrayContains("Bob")(names)(t) // Passes
}
func ArrayLength ¶
ArrayLength tests if an array has the expected length.
Example:
func TestArrayLength(t *testing.T) {
numbers := []int{1, 2, 3, 4, 5}
assert.ArrayLength[int](5)(numbers)(t) // Passes
assert.ArrayLength[int](3)(numbers)(t) // Fails
}
func ContainsKey ¶
func ContainsKey[T any, K comparable](expected K) Kleisli[map[K]T]
ContainsKey tests if a key is contained in a map.
Example:
func TestContainsKey(t *testing.T) {
config := map[string]int{"timeout": 30, "retries": 3}
assert.ContainsKey[int]("timeout")(config)(t) // Passes
assert.ContainsKey[int]("maxSize")(config)(t) // Fails
}
func Equal ¶
func Equal[T any](expected T) Kleisli[T]
Equal tests if the expected and the actual values are equal.
This is one of the most commonly used assertions. It follows the "data last" principle - you provide the expected value first, then the actual value, and finally the testing.T context.
Example:
func TestEqual(t *testing.T) {
result := 2 + 2
assert.Equal(4)(result)(t) // Passes
name := "Alice"
assert.Equal("Alice")(name)(t) // Passes
// Can be composed with other assertions
user := User{Name: "Bob", Age: 30}
assertions := assert.AllOf([]assert.Reader{
assert.Equal("Bob")(user.Name),
assert.Equal(30)(user.Age),
})
assertions(t)
}
func NotContainsKey ¶
func NotContainsKey[T any, K comparable](expected K) Kleisli[map[K]T]
NotContainsKey tests if a key is not contained in a map.
Example:
func TestNotContainsKey(t *testing.T) {
config := map[string]int{"timeout": 30, "retries": 3}
assert.NotContainsKey[int]("maxSize")(config)(t) // Passes
assert.NotContainsKey[int]("timeout")(config)(t) // Fails
}
func NotEqual ¶
func NotEqual[T any](expected T) Kleisli[T]
NotEqual tests if the expected and the actual values are not equal.
This function follows the "data last" principle - you provide the expected value first, then the actual value, and finally the testing.T context.
Example:
func TestNotEqual(t *testing.T) {
value := 42
assert.NotEqual(10)(value)(t) // Passes: 42 != 10
assert.NotEqual(42)(value)(t) // Fails: 42 == 42
}
func RecordLength ¶
func RecordLength[K comparable, T any](expected int) Kleisli[map[K]T]
RecordLength tests if a map has the expected length.
Example:
func TestRecordLength(t *testing.T) {
config := map[string]string{"host": "localhost", "port": "8080"}
assert.RecordLength[string, string](2)(config)(t) // Passes
assert.RecordLength[string, string](3)(config)(t) // Fails
}
func StringLength ¶
func StringLength[K comparable, T any](expected int) Kleisli[string]
StringLength tests if a string has the expected length.
Example:
func TestStringLength(t *testing.T) {
message := "Hello"
assert.StringLength[any, any](5)(message)(t) // Passes
assert.StringLength[any, any](10)(message)(t) // Fails
}
func That ¶
func That[T any](pred Predicate[T]) Kleisli[T]
That asserts that a particular predicate matches.
This is a powerful function that allows you to create custom assertions using predicates.
Example:
func TestThat(t *testing.T) {
// Test if a number is positive
isPositive := func(n int) bool { return n > 0 }
assert.That(isPositive)(42)(t) // Passes
assert.That(isPositive)(-5)(t) // Fails
// Test if a string is uppercase
isUppercase := func(s string) bool { return s == strings.ToUpper(s) }
assert.That(isUppercase)("HELLO")(t) // Passes
assert.That(isUppercase)("Hello")(t) // Fails
// Can be combined with Local for property testing
type User struct { Age int }
ageIsAdult := assert.Local(func(u User) int { return u.Age })(
assert.That(func(age int) bool { return age >= 18 }),
)
user := User{Age: 25}
ageIsAdult(user)(t) // Passes
}
type Reader ¶
func AllOf ¶
func AllOf(readers []Reader) Reader
AllOf combines multiple assertion Readers into a single Reader that passes only if all assertions pass.
This function uses boolean AND logic (MonoidAll) to combine the results of all assertions. If any assertion fails, the combined assertion fails.
This is useful for grouping related assertions together and ensuring all conditions are met.
Parameters:
- readers: Array of assertion Readers to combine
Returns:
- A single Reader that performs all assertions and returns true only if all pass
Example:
func TestUser(t *testing.T) {
user := User{Name: "Alice", Age: 30, Active: true}
assertions := assert.AllOf([]assert.Reader{
assert.Equal("Alice")(user.Name),
assert.Equal(30)(user.Age),
assert.Equal(true)(user.Active),
})
assertions(t)
}
func ArrayNotEmpty ¶
func ArrayNotEmpty[T any](arr []T) Reader
ArrayNotEmpty checks if an array is not empty.
Example:
func TestArrayNotEmpty(t *testing.T) {
numbers := []int{1, 2, 3}
assert.ArrayNotEmpty(numbers)(t) // Passes
empty := []int{}
assert.ArrayNotEmpty(empty)(t) // Fails
}
func Error ¶
func Error(err error) Reader
Error validates that there is an error.
This is used to assert that operations fail as expected.
Example:
func TestError(t *testing.T) {
err := validateInput("")
assert.Error(err)(t) // Passes if err is not nil
err2 := validateInput("valid")
assert.Error(err2)(t) // Fails if err2 is nil
}
func Failure ¶
Failure checks if a Result represents failure.
This is a convenience function for testing Result types from the fp-go library.
Example:
func TestFailure(t *testing.T) {
res := result.Error[int](errors.New("something went wrong"))
assert.Failure(res)(t) // Passes
successRes := result.Of[int](42)
assert.Failure(successRes)(t) // Fails
}
func NoError ¶
func NoError(err error) Reader
NoError validates that there is no error.
This is commonly used to assert that operations complete successfully.
Example:
func TestNoError(t *testing.T) {
err := doSomething()
assert.NoError(err)(t) // Passes if err is nil
// Can be used with result types
result := result.TryCatch(func() (int, error) {
return 42, nil
})
assert.Success(result)(t) // Uses NoError internally
}
func RecordNotEmpty ¶
func RecordNotEmpty[K comparable, T any](mp map[K]T) Reader
RecordNotEmpty checks if a map is not empty.
Example:
func TestRecordNotEmpty(t *testing.T) {
config := map[string]int{"timeout": 30, "retries": 3}
assert.RecordNotEmpty(config)(t) // Passes
empty := map[string]int{}
assert.RecordNotEmpty(empty)(t) // Fails
}
func RunAll ¶
func RunAll(testcases map[string]Reader) Reader
RunAll executes a map of named test cases, running each as a subtest.
This function creates a Reader that runs multiple named test cases using Go's t.Run for proper test isolation and reporting. Each test case is executed as a separate subtest with its own name.
The function returns true only if all subtests pass. This allows for better test organization and clearer test output.
Parameters:
- testcases: Map of test names to assertion Readers
Returns:
- A Reader that executes all named test cases and returns true if all pass
Example:
func TestMathOperations(t *testing.T) {
testcases := map[string]assert.Reader{
"addition": assert.Equal(4)(2 + 2),
"multiplication": assert.Equal(6)(2 * 3),
"subtraction": assert.Equal(1)(3 - 2),
}
assert.RunAll(testcases)(t)
}
func StringNotEmpty ¶
func StringNotEmpty(s string) Reader
StringNotEmpty checks if a string is not empty.
Example:
func TestStringNotEmpty(t *testing.T) {
message := "Hello, World!"
assert.StringNotEmpty(message)(t) // Passes
empty := ""
assert.StringNotEmpty(empty)(t) // Fails
}
func Success ¶
Success checks if a Result represents success.
This is a convenience function for testing Result types from the fp-go library.
Example:
func TestSuccess(t *testing.T) {
res := result.Of[int](42)
assert.Success(res)(t) // Passes
failedRes := result.Error[int](errors.New("failed"))
assert.Success(failedRes)(t) // Fails
}