{"id":37134743,"url":"https://github.com/kairum-labs/should","last_synced_at":"2026-01-14T15:43:17.131Z","repository":{"id":299206308,"uuid":"1001489490","full_name":"Kairum-Labs/should","owner":"Kairum-Labs","description":"Expressive assertions for Go. Make your tests readable, elegant, and easy to debug.","archived":false,"fork":false,"pushed_at":"2025-11-02T21:05:20.000Z","size":2665,"stargazers_count":35,"open_issues_count":2,"forks_count":5,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-11-02T23:11:24.524Z","etag":null,"topics":["assert","golang","library","tdd","unittest"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Kairum-Labs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-06-13T13:20:20.000Z","updated_at":"2025-11-02T21:05:24.000Z","dependencies_parsed_at":"2025-07-09T19:41:13.496Z","dependency_job_id":"8161b85f-8aff-4c3e-a992-cf28d8c5fe8d","html_url":"https://github.com/Kairum-Labs/should","commit_stats":null,"previous_names":["kairum-labs/should"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/Kairum-Labs/should","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kairum-Labs%2Fshould","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kairum-Labs%2Fshould/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kairum-Labs%2Fshould/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kairum-Labs%2Fshould/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Kairum-Labs","download_url":"https://codeload.github.com/Kairum-Labs/should/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kairum-Labs%2Fshould/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28424473,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T15:24:48.085Z","status":"ssl_error","status_checked_at":"2026-01-14T15:23:41.940Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["assert","golang","library","tdd","unittest"],"created_at":"2026-01-14T15:43:16.382Z","updated_at":"2026-01-14T15:43:17.120Z","avatar_url":"https://github.com/Kairum-Labs.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Should - A Go Assertion Library\n\n[![go](https://img.shields.io/badge/go-1.22+-blue)](https://golang.com/)\n[![Go Reference](https://pkg.go.dev/badge/github.com/Kairum-Labs/should.svg)](https://pkg.go.dev/github.com/Kairum-Labs/should)\n[![codecov](https://codecov.io/gh/Kairum-Labs/should/branch/main/graph/badge.svg)](https://codecov.io/gh/Kairum-Labs/should)\n[![Go Report Card](https://goreportcard.com/badge/github.com/Kairum-Labs/should)](https://goreportcard.com/report/github.com/Kairum-Labs/should)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"assets/should.png\" alt=\"should project logo, a Go assertions library\" width=\"500\"\u003e\n\u003c/div\u003e\n\n`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.\n\n## Features\n\n- **Detailed Error Messages**: Get comprehensive, contextual error information for every assertion type.\n- **Smart String Handling**: Automatic multiline formatting for long strings and truncation with context.\n- **Numeric Comparisons**: Detailed difference calculations with helpful hints for numeric assertions.\n- **Time Comparisons**: Compare times with options to ignore timezone and/or nanoseconds with clear diffs.\n- **Empty/Non-Empty Checks**: Rich context about collection types, sizes, and content.\n- **String Similarity**: When a string assertion fails, `Should` suggests similar strings from your collection to help you spot typos.\n- **Type-Safe**: Uses Go generics for type safety while maintaining a clean API.\n\n## Installation\n\n**Requirements:** Go 1.22 or later\n\n```bash\ngo get github.com/Kairum-Labs/should\n```\n\n## Quick Start\n\n```go\npackage main\n\nimport (\n\t\"testing\"\n\t\"github.com/Kairum-Labs/should\"\n)\n\nfunc TestBasicAssertions(t *testing.T) {\n\t// Boolean assertions\n\tshould.BeTrue(t, true)\n\tshould.BeFalse(t, false)\n\n\t// Equality checks\n\tshould.BeEqual(t, \"hello\", \"hello\")\n\tshould.BeEqual(t, 42, 42)\n\n\t// Numeric comparisons\n\tshould.BeGreaterThan(t, 10, 5)\n\tshould.BeLessThan(t, 3, 7)\n\tshould.BeLessOrEqualTo(t, 5, 10)\n\n\t// Range validation\n\tshould.BeInRange(t, user.Age, 18, 65)\n\tshould.BeInRange(t, testScore, 0, 100)\n\tshould.BeInRange(t, response.StatusCode, 200, 299)\n\n\t// Time comparisons\n\tshould.BeSameTime(t, t1, t2)\n\tshould.BeSameTime(t, t1, t2, should.WithIgnoreTimezone())\n\tshould.BeSameTime(t, t1, t2, should.WithTruncate(time.Second))\n\n\t// Numeric comparisons with custom messages\n\tshould.BeGreaterThan(t, user.Age, 18, should.WithMessage(\"User must be adult\"))\n\tshould.BeGreaterOrEqualTo(t, score, 0, should.WithMessage(\"Score cannot be negative\"))\n\tshould.BeLessOrEqualTo(t, user.Age, 65, should.WithMessage(\"User must be under retirement age\"))\n\n\t// Empty/Non-empty checks\n\tshould.BeEmpty(t, \"\")\n\tshould.NotBeEmpty(t, []int{1, 2, 3})\n\n\t// String operations\n\tshould.StartWith(t, \"Hello, World!\", \"Hello\")\n\tshould.EndWith(t, \"Hello, World!\", \"World!\")\n\tshould.ContainSubstring(t, \"Hello, World!\", \"World\")\n\n\t// Collection operations\n\tusers := []string{\"Alice\", \"Bob\", \"Charlie\"}\n\tshould.Contain(t, users, \"Alice\")\n\tshould.NotContain(t, users, \"David\")\n\tshould.Contain(t, userIDs, targetID, should.WithMessage(\"User ID must exist in the system\"))\n\n\t// Sort check\n\tshould.BeSorted(t, []int{1, 2, 3, 4, 5})\n\tshould.BeSorted(t, []string{\"apple\", \"banana\", \"cherry\"})\n\tshould.BeSorted(t, scores, should.WithMessage(\"Test scores must be in ascending order\"))\n}\n```\n\n## Detailed Error Messages\n\n### Empty/Non-Empty Assertions\n\n`Should` provides rich context for empty and non-empty checks:\n\n```go\n// Short string\nshould.BeEmpty(t, \"Hello World!\")\n// Output:\n// Expected value to be empty, but it was not:\n//         Type    : string\n//         Length  : 12 characters\n//         Content : \"Hello World!\"\n\n// Long string (automatically formatted)\nlongText := \"Lorem ipsum dolor sit amet, consectetur adipiscing elit...\"\nshould.BeEmpty(t, longText)\n// Output:\n// Length: 516 characters, 9 lines\n// 1. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n// 2.  Sed do eiusmod tempor incididunt ut labore et dolore ma\n// 3. gna aliqua. Ut enim ad minim veniam, quis nostrud exerci\n// 4. tation ullamco laboris nisi ut aliquip ex ea commodo con\n// 5. sequat. Duis aute irure dolor in reprehenderit in volupt\n//\n// Last lines:\n// 7. xcepteur sint occaecat cupidatat non proident, sunt in c\n// 8. ulpa qui officia deserunt mollit anim id est laborum. Vi\n// 9. vamus sagittis lacus vel augue laoreet rutrum faucibus d\n\n// Large slice (shows truncated content)\nlargeSlice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}\nshould.BeEmpty(t, largeSlice)\n// Output:\n// Expected value to be empty, but it was not:\n//         Type    : []int\n//         Length  : 15 elements\n//         Content : [1, 2, 3, ...] (showing first 3 of 15)\n\n// Empty slice\nshould.NotBeEmpty(t, []int{})\n// Output:\n// Expected value to be not empty, but it was empty:\n//         Type    : []int\n//         Length  : 0 elements\n```\n\n### Numeric Comparisons\n\nGet detailed information about numeric comparison failures:\n\n```go\n// Basic comparison with custom message\nshould.BeGreaterThan(t, 5, 10, should.WithMessage(\"Score validation failed\"))\n// Output:\n// Score validation failed\n// Expected value to be greater than threshold:\n//         Value     : 5\n//         Threshold : 10\n//         Difference: -5 (value is 5 smaller)\n//         Hint   : Value should be larger than threshold\n\n// Equal values\nshould.BeGreaterThan(t, 42, 42)\n// Output:\n// Expected value to be greater than threshold:\n//         Value     : 42\n//         Threshold : 42\n//         Difference: 0 (values are equal)\n//         Hint   : Value should be larger than threshold\n\n// Float precision\nshould.BeLessThan(t, 3.14, 2.71)\n// Output:\n// Expected value to be less than threshold:\n//         Value     : 3.14\n//         Threshold : 2.71\n//         Difference: +0.43000000000000016 (value is 0.43000000000000016 greater)\n//         Hint   : Value should be smaller than threshold\n\n// Large numbers\nshould.BeLessThan(t, 1000000, 999999)\n// Output:\n// Expected value to be less than threshold:\n//         Value     : 1000000\n//         Threshold : 999999\n//         Difference: +1 (value is 1 greater)\n//         Hint   : Value should be smaller than threshold\n\n// Less than or equal (fails when value is greater)\nshould.BeLessOrEqualTo(t, 15, 10)\n// Output:\n// Expected value to be less than or equal to threshold:\n//         Value     : 15\n//         Threshold : 10\n//         Difference: +5 (value is 5 greater)\n//         Hint      : Value should be smaller than or equal to threshold\n\n// Fails because 3.142 is not within ±0.001 of 3.140.\nshould.BeWithin(t, 3.142, 3.14, 0.001)\n// Expected 3.142000 to be within ±0.001000 of 3.140000\n// Difference: 0.002000 (100.00% greater than tolerance)\n\n// Range validation (fails when value is below or above the range)\nshould.BeInRange(t, 16, 18, 65)\n// Output:\n// Expected value to be in range [18, 65], but it was below:\n//         Value    : 16\n//         Range    : [18, 65]\n//         Distance : 2 below minimum (16 \u003c 18)\n//         Hint     : Value should be \u003e= 18\n\n// Range validation (fails when value is above the range)\nshould.BeInRange(t, 105, 0, 100)\n// Output:\n// Expected value to be in range [0, 100], but it was above:\n//         Value    : 105\n//         Range    : [0, 100]\n//         Distance : 5 above maximum (105 \u003e 100)\n//         Hint     : Value should be \u003c= 100\n\n// Range validation with custom message\nshould.BeInRange(t, 150, 0, 100, should.WithMessage(\"Battery level must be valid percentage\"))\n// Output:\n// Battery level must be valid percentage\n// Expected value to be in range [0, 100], but it was above:\n//         Value    : 150\n//         Range    : [0, 100]\n//         Distance : 50 above maximum (150 \u003e 100)\n//         Hint     : Value should be \u003c= 100\n```\n\n### Struct and Object Comparisons\n\nWhen comparing complex objects, `Should` shows exactly what differs:\n\n```go\ntype Person struct {\n    Name string\n    Age  int\n}\n\np1 := Person{Name: \"John\", Age: 30}\np2 := Person{Name: \"Jane\", Age: 25}\nshould.BeEqual(t, p1, p2)\n\n// Output:\n// Differences found:\n// Not equal:\n// expected: {Name: \"Jane\", Age: 25}\n// actual  : {Name: \"John\", Age: 30}\n//\n// Field differences:\n//   └─ Name: \"Jane\" ≠ \"John\"\n//   └─ Age: 25 ≠ 30\n\n// Ensure values are NOT equal\np3 := Person{Name: \"John\", Age: 30}\nshould.NotBeEqual(t, p1, p3)\n// Output when values are equal:\n// Expected values to be different, but they are equal\n```\n\n### Length and Type Assertions\n\nGet clear feedback on length and type mismatches.\n\n```go\n// Incorrect length\nshould.HaveLength(t, []string{\"apple\", \"banana\"}, 3)\n// Output:\n// Expected collection to have specific length:\n// Type          : []string\n// Expected Length: 3\n// Actual Length : 2\n// Difference    : -1 (1 element(s) missing)\n\n// Incorrect type\ntype Dog struct{ Name string }\ntype Cat struct{ Name string }\nvar d Dog\nshould.BeOfType(t, Cat{Name: \"Whiskers\"}, d)\n// Output:\n// Expected value to be of specific type:\n// Expected Type: should_test.Dog\n// Actual Type  : should_test.Cat\n// Difference   : Different concrete types\n```\n\n### String Similarity Detection\n\nWhen checking for strings in slices, `Should` helps you find typos:\n\n```go\nusers := []string{\"user-one\", \"user_two\", \"UserThree\", \"user-3\", \"userThree\"}\nshould.Contain(t, users, \"user3\")\n\n// Output includes helpful suggestions:\n// Expected collection to contain element:\n//         Collection: [user-one, user_two, UserThree, user-3, userThree]\n//         Missing   : user3\n//\n//           Similar elements found:\n//           └─ user-3 (at index 3) - 1 extra character\n```\n\n### Numeric Context Information\n\nWhen checking for numeric in slices, `Should` shows where the value would fit:\n\n```go\nnumbers := []int{10, 80, 20, 70, 30, 60, 40, 50, 0, 100, 90, 120, 110} // 13 elements, unsorted\nshould.Contain(t, numbers, 55)\n\n// Output includes context information:\n// Expected collection to contain element:\n// Collection: [10, 80, 20, 70, 30, ..., 90, 120, 110] (showing first 5 and last 5 of 13 elements)\n// Missing   : 55\n//\n// Element 55 would fit between 50 and 60 in sorted order\n// └─ Sorted view: [..., 40, 50, 60, 70, ...]\n```\n\n### Set Membership Assertions\n\nCheck if a value is part of a set of allowed options.\n\n```go\nshould.BeOneOf(t, \"pending\", []string{\"active\", \"inactive\", \"suspended\"})\n// Output:\n// Expected value to be one of the allowed options:\n// Value   : \"pending\"\n// Options : [\"active\", \"inactive\", \"suspended\"]\n// Count   : 0 of 3 options matched\n```\n\n### String Prefix and Suffix Assertions\n\nCheck if strings start or end with specific substrings, with intelligent case handling:\n\n```go\n// Basic string prefix checking\nshould.StartWith(t, \"Hello, World!\", \"Hello\")\n\n// Case-sensitive by default\nshould.StartWith(t, \"Hello, World!\", \"hello\")\n// Output:\n// Expected string to start with 'hello', but it starts with 'Hello'\n// Expected : 'hello'\n// Actual   : 'Hello, World!'\n//             ^^^^^\n//           (actual prefix)\n// Note: Case mismatch detected (use should.WithIgnoreCase() if intended)\n\n// Case-insensitive option\nshould.StartWith(t, \"Hello, World!\", \"hello\", should.WithIgnoreCase())\n\n// String suffix checking\nshould.EndWith(t, \"Hello, World!\", \"World!\")\n\n// With custom messages\nshould.StartWith(t, filename, \"temp_\", should.WithMessage(\"Temporary files must have temp_ prefix\"))\nshould.EndWith(t, filename, \".log\", should.WithMessage(\"Log files must have .log extension\"))\n```\n\n### String Substring Assertions\n\nCheck if strings contain specific substrings, with intelligent formatting for long strings:\n\n```go\n// Basic substring checking\nshould.ContainSubstring(t, \"Hello, World!\", \"World\")\n\n// Case-sensitive by default\nshould.ContainSubstring(t, \"Hello, World!\", \"world\")\n// Output:\n// Expected string to contain \"world\", but found case difference\n// Substring: \"world\"\n// Found    : \"World\" at position 7\n// Note: Case mismatch detected (use should.WithIgnoreCase() if intended)\n\n// Case-insensitive option\nshould.ContainSubstring(t, \"Hello, World!\", \"world\", should.WithIgnoreCase())\n\n// Typo detection for short substrings (≤20 characters)\nshould.ContainSubstring(t, \"Hello, beautiful world!\", \"beatiful\")\n// Output:\n// Expected string to contain \"beatiful\", but it was not found\n// Substring   : \"beatiful\"\n// Actual   : \"Hello, beautiful world!\"\n//\n// Similar substring found:\n//   └─ 'beautiful' at position 7 - 1 character differs\n\n// Similar match with transposition\nshould.ContainSubstring(t, \"test testing tester\", \"tets\")\n// Output:\n// Expected string to contain \"tets\", but it was not found\n// Substring   : \"tets\"\n// Actual   : \"test testing tester\"\n//\n// Similar substring found:\n//   └─ 'test' at position 0 - 1 character differs\n\n// Long strings with multiline formatting\nlongText := `This is a very long text that spans multiple lines\nand contains various keywords and phrases that we might\nwant to search for in our test assertions.`\n\nshould.ContainSubstring(t, longText, \"nonexistent\")\n// Output:\n// Expected string to contain \"nonexistent\", but it was not found\n// Substring   : \"nonexistent\"\n// Actual   : (length: 153)\n// 1. This is a very long text that spans multiple lines\n// 2. and contains various keywords and phrases that we might\n// 3. want to search for in our test assertions.\n\n// With custom messages\nshould.ContainSubstring(t, logContent, \"ERROR\", should.WithMessage(\"Log should contain error messages\"))\nshould.ContainSubstring(t, apiResponse, \"success\", should.WithIgnoreCase(), should.WithMessage(\"API response should indicate success\"))\n```\n\n**Note**: Typo detection using Levenshtein distance is automatically enabled for substrings up to 20 characters to maintain good performance. For longer substrings, only exact matching is performed.\n\n### Duplicate Detection\n\nEnsure collections contain no duplicate values with detailed reporting:\n\n```go\n// Check for duplicates in slices\nshould.NotContainDuplicates(t, []int{1, 2, 3, 4, 5}) // passes\n\nshould.NotContainDuplicates(t, []int{1, 2, 2, 3, 3, 3})\n// Output:\n// Expected no duplicates, but found 2 duplicate values:\n// └─ 2 appears 2 times at indexes [1, 2]\n// └─ 3 appears 3 times at indexes [3, 4, 5]\n```\n\n### Error Assertions\n\nHandle error check with clear, informative messages:\n\n```go\n// Basic usage\nshould.BeError(t, err)\nshould.NotBeError(t, err)\n\n// Check specific error types\nvar pathErr *os.PathError\nshould.BeErrorAs(t, err, \u0026pathErr)\n\n// Check specific error values\nshould.BeErrorIs(t, err, io.EOF)\n\n// With custom messages\nshould.BeError(t, err, should.WithMessage(\"Expected operation to fail\"))\nshould.NotBeError(t, err, should.WithMessage(\"Expected operation to pass\"))\nshould.BeErrorAs(t, err, \u0026pathErr, should.WithMessage(\"Expected path error\"))\nshould.BeErrorIs(t, err, context.Canceled, should.WithMessage(\"Expected cancellation\"))\n\n// Outputs\n// BeError - error required\nshould.BeError(t, err)\n// Expected an error, but got nil\n\n// NotBeError - no error\nshould.NotBeError(t, err)\n// Expected no error, but got an error\n// Error: \"something went wrong\"\n// Type: *errors.errorString\n\n\n// BeErrorAs - type not found\nvar pathErr *os.PathError\nshould.BeErrorAs(t, err, \u0026pathErr)\n// Expected error to be *os.PathError, but type not found in error chain\n// Error: \"invalid character 'i' looking for beginning of value\"\n// Types: [*json.SyntaxError]\n\n// BeErrorIs - value not found\nshould.BeErrorIs(t, err, io.EOF)\n// Expected error to be \"EOF\", but value not found in error chain\n// Error: \"connection refused\"\n// Types: [*net.OpError, syscall.Errno]\n```\n\n### Time Assertions\n\nCompare times with flexible options:\n\n```go\n// Basic usage\nshould.BeSameTime(t, actual, expected)\n\n// With custom messages\nshould.BeSameTime(t, actual, expected, should.WithMessage(\"Expected times to match but they differ\"))\n\n// With options\nshould.BeSameTime(t, actual, expected,\n    should.WithIgnoreTimezone(),\n    should.WithTruncate(time.Second),\n)\n\ntime1 := time.Date(2024, 1, 15, 14, 30, 0, 0, time.UTC)\ntime2 := time.Date(2024, 1, 15, 14, 30, 2, 500000000, time.UTC)\n\nshould.BeSameTime(t, time1, time2)\n// Expected times to be the same, but difference is 2.5s\n// Expected: 2024-01-15 14:30:00.000000000 UTC\n// Actual  : 2024-01-15 14:30:02.500000000 UTC (2.5s later)\n\nshould.BeSameTime(t, time2, time1)\n// Expected times to be the same, but difference is 2.5s\n// Expected: 2024-01-15 14:30:02.500000000 UTC\n// Actual  : 2024-01-15 14:30:00.000000000 UTC (2.5s earlier)\n\n// Ignore timezone differences\nutc := time.Date(2024, 1, 15, 14, 30, 0, 0, time.UTC)\nest := time.Date(2024, 1, 15, 9, 30, 0, 0, time.FixedZone(\"EST\", -5*3600))\nshould.BeSameTime(t, utc, est, should.WithIgnoreTimezone()) // Pass\n```\n\n### Sorted Check\n\nVerify that collections are sorted in ascending order with detailed violation reporting:\n\n```go\n// Multiple violations with helpful summary\nshould.BeSorted(t, []int{5, 4, 3, 2, 1})\n// Output:\n// Expected collection to be in ascending order, but it is not:\n// Collection: (total: 5 elements)\n// Status    : 4 order violations found\n// Problems  :\n//   - Index 0: 5 \u003e 4\n//   - Index 1: 4 \u003e 3\n//   - Index 2: 3 \u003e 2\n//   - Index 3: 2 \u003e 1\n\n// Large collections with truncation (stops after 6 violations for performance)\nshould.BeSorted(t, []int{10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0})\n// Output:\n// Expected collection to be in ascending order, but it is not:\n// Collection: (total: 11 elements)\n// Status    : 6 order violations found\n// Problems  :\n//   - Index 0: 10 \u003e 9\n//   - Index 1: 9 \u003e 8\n//   - Index 2: 8 \u003e 7\n//   - Index 3: 7 \u003e 6\n//   - Index 4: 6 \u003e 5\n//   - ... and 1 more violation\n\n// Very large collections with special formatting\nshould.BeSorted(t, largeSlice)\n// Output:\n// Expected collection to be in ascending order, but it is not:\n// Collection: [Large collection] (total: 10000 elements)\n// Status    : 3 order violations found\n// Problems  :\n//   - Index 4567: 123 \u003e 115\n//   - Index 4702: 890 \u003e 456\n//   - Index 4833: 234 \u003e 111\n\nshould.BeSorted(t, []string{\"banana\", \"apple\", \"cherry\"})\n// Output:\n// Expected collection to be in ascending order, but it is not:\n// Collection: (total: 3 elements)\n// Status    : 1 order violation found\n// Problems  :\n//   - Index 0: banana \u003e apple\n\n// With custom messages\nshould.BeSorted(t, testScores, should.WithMessage(\"Test scores must be in ascending order\"))\nshould.BeSorted(t, timestamps, should.WithMessage(\"Events must be chronologically ordered\"))\n```\n\n### Map Key and Value Assertions\n\nCheck if maps contain specific keys or values with intelligent similarity detection:\n\n```go\nuserMap := map[string]int{\n    \"name\": 1,\n    \"age\":  2,\n    \"email\": 3,\n}\n\n// Check for map keys\nshould.ContainKey(t, userMap, \"name\") // passes\nshould.ContainKey(t, userMap, \"phone\")\n// Output:\n// Expected map to contain key 'phone', but key was not found\n// Available keys: ['name', 'age', 'email']\n// Missing: 'phone'\n\n// Check for map values\nshould.ContainValue(t, userMap, 2) // passes\nshould.ContainValue(t, userMap, 5)\n// Output:\n// Expected map to contain value 5, but value was not found\n// Available values: [1, 2, 3]\n// Missing: 5\n\n// With typo detection for string keys\nshould.ContainKey(t, userMap, \"nam\")\n// Output:\n// Expected map to contain key 'nam', but key was not found\n// Available keys: ['name', 'age', 'email']\n// Missing: 'nam'\n//\n// Similar key found:\n//   └─ 'name' - 1 missing char\n\n// Numeric maps with similarity\nscoreMap := map[int]string{1: \"first\", 2: \"second\", 10: \"tenth\"}\nshould.ContainKey(t, scoreMap, 9)\n// Output:\n// Expected map to contain key 9, but key was not found\n// Available keys: [1, 2, 10]\n// Missing: 9\n//\n// Similar key found:\n//   └─ 10 - differs by 1\n\n// With custom messages\nshould.ContainKey(t, config, \"database_url\", should.WithMessage(\"Database URL must be configured\"))\nshould.ContainValue(t, statusCodes, 200, should.WithMessage(\"Success status code must be present\"))\n\n// Check that maps do NOT contain specific keys or values\nshould.NotContainKey(t, userMap, \"password\") // passes if 'password' key is not present\nshould.NotContainValue(t, userMap, 999) // passes if value 999 is not found\n\n// When key is found in NotContainKey\nshould.NotContainKey(t, userSettings, \"admin_access\")\n// Output when key is found:\n// Expected map to NOT contain key, but it was found:\n// Map Type : map[string]string\n// Map Size : 3 entries\n// Found Key: \"admin_access\"\n// Found Value: \"true\"\n\n// When value is found in NotContainValue\nshould.NotContainValue(t, userRoles, 3)\n// Output when value is found:\n// Expected map to NOT contain value, but it was found:\n// Map Type : map[string]int\n// Map Size : 3 entries\n// Found Value: 3\n// Found At: key \"admin\"\n```\n\n## API Reference\n\n### Core Assertions\n\n- `BeTrue(t, actual)` / `BeFalse(t, actual)` - Boolean value checks\n- `BeEqual(t, actual, expected)` - Deep equality comparison with detailed diffs\n- `NotBeEqual(t, actual, unexpected)` - Ensure two values are not equal\n- `BeNil(t, actual)` / `NotBeNil(t, actual)` - Nil pointer checks\n- `BeOfType(t, actual, expected)` - Checks if a value is of a specific type\n- `BeSameTime(t, actual, expected, options...)` - Compare times with optional timezone/nanosecond ignoring\n- `HaveLength(t, collection, length)` - Checks if a collection has a specific length\n\n### Empty/Non-Empty Checks\n\n- `BeEmpty(t, actual)` - Checks if strings, slices, arrays, maps, channels, or pointers are empty\n- `NotBeEmpty(t, actual)` - Checks if values are not empty\n\n### Numeric Comparisons\n\n- `BeGreaterThan(t, actual, threshold)` - Numeric greater-than comparison\n- `BeLessThan(t, actual, threshold)` - Numeric less-than comparison\n- `BeGreaterOrEqualTo(t, actual, threshold)` - Numeric greater-than-or-equal comparison\n- `BeLessOrEqualTo(t, actual, threshold)` - Numeric less-than-or-equal comparison\n- `BeInRange(t, actual, minValue, maxValue)` - Check if value is within inclusive range [minValue, maxValue]\n- `BeWithin(t, actual, expected, tolerance)` - Check if numeric value is within the given tolerance of the expected value\n\n### String Operations\n\n- `StartWith(t, actual, expected)` - Check if string starts with expected substring\n- `EndWith(t, actual, expected)` - Check if string ends with expected substring\n- `ContainSubstring(t, actual, substring)` - Check if string contains expected substring\n\n### Collection Operations\n\n- `BeOneOf(t, actual, options)` - Check if a value is one of a set of options\n- `Contain(t, collection, element)` - Check if slice/array contains an element\n- `NotContain(t, collection, element)` - Check if slice/array does not contain an element\n- `NotContainDuplicates(t, collection)` - Check if slice/array contains no duplicate values\n- `AnyMatch(t, collection, predicate)` - Check if any element matches a custom predicate\n- `BeSorted(t, slice)` - Check if slice is sorted in ascending order (supports numeric types and strings)\n\n### Map Operations\n\n- `ContainKey(t, map, key)` - Check if map contains a specific key\n- `NotContainKey(t, map, key)` - Check if map does not contain a specific key\n- `ContainValue(t, map, value)` - Check if map contains a specific value\n- `NotContainValue(t, map, value)` - Check if map does not contain a specific value\n\n### Panic Handling\n\n- `Panic(t, func, opts ...Option)` - Assert that a function panics\n- `NotPanic(t, func, opts ...Option)` - Assert that a function does not panic\n\nExamples with custom messages and stack traces:\n\n```go\n// Assert function panics with custom message\nshould.Panic(t, func() {\n    divide(1, 0)\n}, should.WithMessage(\"Division by zero should panic\"))\n\n// Assert function doesn't panic with custom message\nshould.NotPanic(t, func() {\n    user.Save()\n}, should.WithMessage(\"Save operation should not panic\"))\n\n// Get detailed stack trace on panic\nshould.NotPanic(t, func() {\n    user.Save()\n}, should.WithStackTrace(), should.WithMessage(\"Save operation should not panic\"))\n```\n\n## Advanced Usage\n\n### Functional Options for Assertions\n\n`Should` uses functional options to provide a scalable way to configure assertions. This allows you to chain multiple configurations in a readable way.\n\n#### Custom Messages with `WithMessage`\n\nYou can add custom messages to any assertion using `should.WithMessage()`:\n\n```go\n// Basic usage with a custom message\nshould.BeGreaterThan(t, user.Age, 18, should.WithMessage(\"User must be at least 18 years old\"))\n\n// Another example\nshould.BeGreaterOrEqualTo(t, account.Balance, 0, should.WithMessage(\"Account balance cannot be negative\"))\n\n// Message with placeholders\nshould.BeGreaterOrEqualTo(t, account.Balance, 0, should.WithMessagef(\n    \"Account balance cannot be negative: current balance is %.2f\", account.Balance,\n))\n```\n\n#### Stack Traces with `WithStackTrace`\n\nFor `NotPanic` assert, you can capture detailed stack traces using `should.WithStackTrace()`:\n\n```go\n// Get stack trace when panic occurs\nshould.NotPanic(t, func() {\n    riskyOperation()\n}, should.WithStackTrace())\n```\n\n#### Time comparisons with options\n\nThese options customize time comparisons for `BeSameTime`.\n\n- `should.WithIgnoreTimezone()`: compares instants regardless of timezone/location\n- `should.WithTruncate(unit)`: truncates both times to specified precision before comparison\n\n```go\n// Ignore timezone while comparing the same instant represented in different locations\nt1 := time.Date(2024, 1, 15, 14, 30, 0, 0, time.UTC)\nt2 := time.Date(2024, 1, 15, 16, 30, 0, 0, time.FixedZone(\"UTC+2\", 2*3600))\nshould.BeSameTime(t, t1, t2, should.WithIgnoreTimezone())\n\n// Ignore nanoseconds precision\nt1 = time.Date(2024, 1, 15, 14, 30, 0, 123456789, time.UTC)\nt2 = time.Date(2024, 1, 15, 14, 30, 0, 987654321, time.UTC)\nshould.BeSameTime(t, t1, t2, should.WithTruncate(time.Second))\n\n// Compare only up to minute precision\nshould.BeSameTime(t, t1, t2, should.WithTruncate(time.Minute))\n```\n\n### Custom Predicate Functions\n\n```go\npeople := []Person{\n    {Name: \"Alice\", Age: 25},\n    {Name: \"Bob\", Age: 30},\n    {Name: \"Charlie\", Age: 35},\n}\n\n// Find people over 30\nshould.AnyMatch(t, people, func(item Person) bool {\n    return person.Age \u003e 30\n})\n\n// With custom error message\nshould.AnyMatch(t, people, func(item Person) bool {\n\treturn person.Age \u003e= 65\n}, should.WithMessage(\"No elderly users found\"))\n```\n\n## Contributing\n\nWe welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkairum-labs%2Fshould","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkairum-labs%2Fshould","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkairum-labs%2Fshould/lists"}