MCPcopy Index your code
hub / github.com/Kairum-Labs/should

github.com/Kairum-Labs/should @v0.2.3

Chat with this repo
repository ↗ · DeepWiki ↗ · release v0.2.3 ↗ · + Follow
474 symbols 2,121 edges 11 files 221 documented · 47% 83 cross-repo links updated 13d agov0.2.3 · 2026-06-21★ 44
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Should - A Go Assertion Library

go Go Reference codecov Go Report Card License: MIT

should project logo, a Go assertions library

Should is a lightweight and intuitive assertion library for Go, designed to make your tests more readable and expressive. It provides exceptionally detailed error messages to help you debug failures faster and understand exactly what went wrong.

Features

  • Detailed Error Messages: Get comprehensive, contextual error information for every assertion type.
  • Smart String Handling: Automatic multiline formatting for long strings and truncation with context.
  • Numeric Comparisons: Detailed difference calculations with helpful hints for numeric assertions.
  • Time Comparisons: Compare times with options to ignore timezone and/or nanoseconds with clear diffs.
  • Empty/Non-Empty Checks: Rich context about collection types, sizes, and content.
  • String Similarity: When a string assertion fails, Should suggests similar strings from your collection to help you spot typos.
  • Type-Safe: Uses Go generics for type safety while maintaining a clean API.

Installation

Requirements: Go 1.22 or later

go get github.com/Kairum-Labs/should

Quick Start

package main

import (
    "testing"
    "github.com/Kairum-Labs/should"
)

func TestBasicAssertions(t *testing.T) {
    // Boolean assertions
    should.BeTrue(t, true)
    should.BeFalse(t, false)

    // Equality checks
    should.BeEqual(t, "hello", "hello")
    should.BeEqual(t, 42, 42)

    // Numeric comparisons
    should.BeGreaterThan(t, 10, 5)
    should.BeLessThan(t, 3, 7)
    should.BeLessOrEqualTo(t, 5, 10)

    // Range validation
    should.BeInRange(t, user.Age, 18, 65)
    should.BeInRange(t, testScore, 0, 100)
    should.BeInRange(t, response.StatusCode, 200, 299)

    // Time comparisons
    should.BeSameTime(t, t1, t2)
    should.BeSameTime(t, t1, t2, should.WithIgnoreTimezone())
    should.BeSameTime(t, t1, t2, should.WithTruncate(time.Second))

    // Numeric comparisons with custom messages
    should.BeGreaterThan(t, user.Age, 18, should.WithMessage("User must be adult"))
    should.BeGreaterOrEqualTo(t, score, 0, should.WithMessage("Score cannot be negative"))
    should.BeLessOrEqualTo(t, user.Age, 65, should.WithMessage("User must be under retirement age"))

    // Empty/Non-empty checks
    should.BeEmpty(t, "")
    should.NotBeEmpty(t, []int{1, 2, 3})

    // String operations
    should.StartWith(t, "Hello, World!", "Hello")
    should.EndWith(t, "Hello, World!", "World!")
    should.ContainSubstring(t, "Hello, World!", "World")

    // Collection operations
    users := []string{"Alice", "Bob", "Charlie"}
    should.Contain(t, users, "Alice")
    should.NotContain(t, users, "David")
    should.Contain(t, userIDs, targetID, should.WithMessage("User ID must exist in the system"))

    // Sort check
    should.BeSorted(t, []int{1, 2, 3, 4, 5})
    should.BeSorted(t, []string{"apple", "banana", "cherry"})
    should.BeSorted(t, scores, should.WithMessage("Test scores must be in ascending order"))
}

Detailed Error Messages

Empty/Non-Empty Assertions

Should provides rich context for empty and non-empty checks:

// Short string
should.BeEmpty(t, "Hello World!")
// Output:
// Expected value to be empty, but it was not:
//         Type    : string
//         Length  : 12 characters
//         Content : "Hello World!"

// Long string (automatically formatted)
longText := "Lorem ipsum dolor sit amet, consectetur adipiscing elit..."
should.BeEmpty(t, longText)
// Output:
// Expected value to be empty, but it was not:
// Length: 516 characters, 9 lines
// 1. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
// 2.  Sed do eiusmod tempor incididunt ut labore et dolore ma
// 3. gna aliqua. Ut enim ad minim veniam, quis nostrud exerci
// 4. tation ullamco laboris nisi ut aliquip ex ea commodo con
// 5. sequat. Duis aute irure dolor in reprehenderit in volupt
//
// Last lines:
// 7. xcepteur sint occaecat cupidatat non proident, sunt in c
// 8. ulpa qui officia deserunt mollit anim id est laborum. Vi
// 9. vamus sagittis lacus vel augue laoreet rutrum faucibus d

// Large slice (shows truncated content)
largeSlice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
should.BeEmpty(t, largeSlice)
// Output:
// Expected value to be empty, but it was not:
//         Type    : []int
//         Length  : 15 elements
//         Content : [1, 2, 3, ...] (showing first 3 of 15)

// Empty slice
should.NotBeEmpty(t, []int{})
// Output:
// Expected value to be not empty, but it was empty:
//         Type    : []int
//         Length  : 0 elements

Numeric Comparisons

Get detailed information about numeric comparison failures:

// Basic comparison with custom message
should.BeGreaterThan(t, 5, 10, should.WithMessage("Score validation failed"))
// Output:
// Score validation failed
// Expected value to be greater than threshold:
//         Value     : 5
//         Threshold : 10
//         Difference: -5 (value is 5 smaller)
//         Hint      : Value should be larger than threshold

// Equal values
should.BeGreaterThan(t, 42, 42)
// Output:
// Expected value to be greater than threshold:
//         Value     : 42
//         Threshold : 42
//         Difference: 0 (values are equal)
//         Hint      : Value should be larger than threshold

// Float precision
should.BeLessThan(t, 3.14, 2.71)
// Output:
// Expected value to be less than threshold:
//         Value     : 3.14
//         Threshold : 2.71
//         Difference: +0.43000000000000016 (value is 0.43000000000000016 greater)
//         Hint      : Value should be smaller than threshold

// Large numbers
should.BeLessThan(t, 1000000, 999999)
// Output:
// Expected value to be less than threshold:
//         Value     : 1000000
//         Threshold : 999999
//         Difference: +1 (value is 1 greater)
//         Hint      : Value should be smaller than threshold

// Less than or equal (fails when value is greater)
should.BeLessOrEqualTo(t, 15, 10)
// Output:
// Expected value to be less than or equal to threshold:
//         Value     : 15
//         Threshold : 10
//         Difference: +5 (value is 5 greater)
//         Hint      : Value should be smaller than or equal to threshold

// Fails because 3.142 is not within ±0.001 of 3.140.
should.BeWithin(t, 3.142, 3.14, 0.001)
// Expected 3.142000 to be within ±0.001000 of 3.140000
// Difference: 0.002000 (100.00% greater than tolerance)

// Range validation (fails when value is below or above the range)
should.BeInRange(t, 16, 18, 65)
// Output:
// Expected value to be in range [18, 65], but it was below:
//         Value    : 16
//         Range    : [18, 65]
//         Distance : 2 below minimum (16 < 18)
//         Hint     : Value should be >= 18

// Range validation (fails when value is above the range)
should.BeInRange(t, 105, 0, 100)
// Output:
// Expected value to be in range [0, 100], but it was above:
//         Value    : 105
//         Range    : [0, 100]
//         Distance : 5 above maximum (105 > 100)
//         Hint     : Value should be <= 100

// Range validation with custom message
should.BeInRange(t, 150, 0, 100, should.WithMessage("Battery level must be valid percentage"))
// Output:
// Battery level must be valid percentage
// Expected value to be in range [0, 100], but it was above:
//         Value    : 150
//         Range    : [0, 100]
//         Distance : 50 above maximum (150 > 100)
//         Hint     : Value should be <= 100

Struct and Object Comparisons

When comparing complex objects, Should shows exactly what differs:

type Person struct {
    Name string
    Age  int
}

p1 := Person{Name: "John", Age: 30}
p2 := Person{Name: "Jane", Age: 25}
should.BeEqual(t, p1, p2)

// Output:
// Differences found:
// Not equal:
// expected: {Name: "Jane", Age: 25}
// actual  : {Name: "John", Age: 30}
// Field differences:
//   └─ Name: "Jane" ≠ "John"
//   └─ Age: 25 ≠ 30

// Ensure values are NOT equal
p3 := Person{Name: "John", Age: 30}
should.NotBeEqual(t, p1, p3)
// Output when values are equal:
// Expected values to be different, but they are equal

Length and Type Assertions

Get clear feedback on length and type mismatches.

// Incorrect length
should.HaveLength(t, []string{"apple", "banana"}, 3)
// Output:
// Expected collection to have specific length:
// Type          : []string
// Expected Length: 3
// Actual Length : 2
// Difference    : -1 (1 element missing)

// Incorrect type
type Dog struct{ Name string }
type Cat struct{ Name string }
var d Dog
should.BeOfType(t, Cat{Name: "Whiskers"}, d)
// Output:
// Expected value to be of specific type:
// Expected Type: should_test.Dog
// Actual Type  : should_test.Cat
// Difference   : Different concrete types

String Similarity Detection

When checking for strings in slices, Should helps you find typos:

users := []string{"user-one", "user_two", "UserThree", "user-3", "userThree"}
should.Contain(t, users, "user3")

// Output includes helpful suggestions:
// Expected collection to contain element:
//         Collection: [user-one, user_two, UserThree, user-3, userThree]
//         Missing   : user3
//
//         Found similar: user-3 (at index 3) - 1 extra character

Numeric Context Information

When checking for numeric in slices, Should shows where the value would fit:

numbers := []int{10, 80, 20, 70, 30, 60, 40, 50, 0, 100, 90, 120, 110} // 13 elements, unsorted
should.Contain(t, numbers, 55)

// Output includes context information:
// Expected collection to contain element:
// Collection: [10, 80, 20, 70, 30, ..., 0, 100, 90, 120, 110] (showing first 5 and last 5 of 13 elements)
// Missing  : 55
//
// Element 55 would fit between 50 and 60 in sorted order
// └─ Sorted view: [..., 40, 50, 60, 70, ...]

Set Membership Assertions

Check if a value is part of a set of allowed options.

should.BeOneOf(t, "pending", []string{"active", "inactive", "suspended"})
// Output:
// Expected value to be one of the allowed options:
// Value   : "pending"
// Options : ["active", "inactive", "suspended"]
// Count   : 0 of 3 options matched

String Prefix and Suffix Assertions

Check if strings start or end with specific substrings, with intelligent case handling:

// Basic string prefix checking
should.StartWith(t, "Hello, World!", "Hello")

// Case-sensitive by default
should.StartWith(t, "Hello, World!", "hello")
// Output:
// Expected string to start with 'hello', but it starts with 'Hello'
// Expected : 'hello'
// Actual   : 'Hello, World!'
//             ^^^^^
//           (actual prefix)
// Note: Case mismatch detected (use should.WithIgnoreCase() if intended)

// Case-insensitive option
should.StartWith(t, "Hello, World!", "hello", should.WithIgnoreCase())

// Long strings: only the beginning is shown (where the prefix appears)
actual := strings.Repeat("a", 200) + "_suffix"
should.StartWith(t, actual, "different")
// Output:
// Expected string to start with 'different', but it starts with 'aaaaaaaaa'
// Expected : 'different'
// Actual   : 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa… (truncated)'
//             ^^^^^^^^^
//           (actual prefix)

// String suffix checking
should.EndWith(t, "Hello, World!", "World!")

// Case-sensitive by default
should.EndWith(t, "Hello, World!", "Hello")
// Output:
// Expected string to end with 'Hello', but it ends with 'orld!'
// Expected : 'Hello'
// Actual   : 'Hello, World!'
//                     ^^^^^
//                     (actual suffix)

// Case mismatch detection
should.EndWith(t, "Hello, World", "world")
// Output:
// Expected string to end with 'world', but it ends with 'World'
// Expected : 'world'
// Actual   : 'Hello, World'
//                    ^^^^^
//                    (actual suffix)
// Note: Case mismatch detected (use should.WithIgnoreCase() if intended)

// Case-insensitive option
should.EndWith(t, "Hello, World!", "world!", should.WithIgnoreCase())

// Long strings: only the tail is shown (where the suffix appears)
actual = "prefix_" + strings.Repeat("a", 200)
should.EndWith(t, actual, "different")
// Output:
// Expected string to end with 'different', but it ends with 'aaaaaaaaa'
// Expected : 'different'
// Actual   : '(truncated) …aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
//                                                                         ^^^^^^^^^
//                                                                        (actual suffix)

// With custom messages
should.StartWith(t, filename, "temp_", should.WithMessage("Temporary files must have temp_ prefix"))
should.EndWith(t, filename, ".log", should.WithMessage("Log files must have .log extension"))

String Substring Assertions

Check if strings contain specific substrings, with intelligent formatting for long strings:

```go // Basic substring checking should.ContainSubstring(t, "Hello, World!", "World")

// Case-sensitive by default should.ContainSubstring(t, "Hello, World!", "world") // Output: // Expected string to contain "world", but found case difference // Substring: "world" // Found : "World" at position 7 // Note: Case mismatch detected (use should.WithIgnoreCase() if intended)

// Case-insensitive option should.ContainSubstring(t, "Hello, World!", "world", should.WithIgnoreCase())

// Typo detection for short substrings (≤20 chara

Extension points exported contracts — how you extend this code

Core symbols most depended-on inside this repo

Shape

Function 402
Struct 41
Method 22
TypeAlias 5
Interface 4

Languages

Go100%

Modules by API surface

assert/assertions_test.go164 symbols
assert/utils_test.go83 symbols
assert/utils.go72 symbols
assert/assertions.go45 symbols
should.go42 symbols
assert/options.go20 symbols
assert/types.go14 symbols
assert/find_differences_test.go13 symbols
should_test.go11 symbols
assert/mock.go6 symbols
wrapper_parity_test.go4 symbols

For agents

$ claude mcp add should \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page