{"id":22750045,"url":"https://github.com/pwall567/should-test","last_synced_at":"2025-07-07T21:14:48.025Z","repository":{"id":264454870,"uuid":"893419240","full_name":"pwall567/should-test","owner":"pwall567","description":"Kotlin testing functions","archived":false,"fork":false,"pushed_at":"2025-03-02T11:37:21.000Z","size":64,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-02T12:28:51.999Z","etag":null,"topics":["kotlin","unit-testing"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","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/pwall567.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"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}},"created_at":"2024-11-24T12:01:40.000Z","updated_at":"2025-03-02T11:37:24.000Z","dependencies_parsed_at":"2025-01-26T10:18:56.309Z","dependency_job_id":"3dbd01c0-274a-4b2a-a2cf-b3c43c536ce2","html_url":"https://github.com/pwall567/should-test","commit_stats":null,"previous_names":["pwall567/should-test"],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pwall567%2Fshould-test","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pwall567%2Fshould-test/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pwall567%2Fshould-test/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pwall567%2Fshould-test/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pwall567","download_url":"https://codeload.github.com/pwall567/should-test/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246282700,"owners_count":20752439,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["kotlin","unit-testing"],"created_at":"2024-12-11T04:11:49.371Z","updated_at":"2025-07-07T21:14:48.012Z","avatar_url":"https://github.com/pwall567.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# should-test\n\n[![Build Status](https://github.com/pwall567/should-test/actions/workflows/build.yml/badge.svg)](https://github.com/pwall567/should-test/actions/workflows/build.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Kotlin](https://img.shields.io/static/v1?label=Kotlin\u0026message=v2.0.21\u0026color=7f52ff\u0026logo=kotlin\u0026logoColor=7f52ff)](https://github.com/JetBrains/kotlin/releases/tag/v2.0.21)\n[![Maven Central](https://img.shields.io/maven-central/v/io.kstuff/should-test?label=Maven%20Central)](https://central.sonatype.com/artifact/io.kstuff/should-test)\n\nKotlin testing functions\n\n## Background\n\n### Problem\n\nA while ago, someone introduced me to a testing library that used the syntax:\n```kotlin\n    testValue shouldBe expectedvalue\n```\nThis seemed like a great idea, and good use of the capabilities of Kotlin.\nThe `shouldBe` function is an `infix` function, meaning that it doesn\u0026rsquo;t require a dot before the function name or\nparentheses around the RHS argument.\nAnd as a generic extension function, it could be applied to a wide range of values \u0026ndash; `Int`s, `String`s, or even\ncomplex user-defined classes.\n\nBut then I encountered a problem where my test result was not just the wrong value, but the wrong type, and the\n`shouldBe` function call had compiled without error.\nThe function had been written in a way that didn\u0026rsquo;t allow the Kotlin compiler (or the IDE) to perform strong\ntype-checking on the arguments.\n\nThat shook my confidence in the library, and I went back to using the `kotlin.test` functions, but the style of tests\nseemed too good to give up on.\n\n### Solution\n\nThis library is an attempt to provide testing functions with an attractive syntax, but also retaining the strong typing\nof the official Kotlin libraries.\nAnd as a bonus, the library is very simple and lightweight, and like the `kotlin.test` functions, it works equally well\nwith JUnit 4 and 5.\n\n### \u0026ldquo;should\u0026rdquo;\n\nA quick note on the use of the word \u0026ldquo;should\u0026rdquo; \u0026ndash; the long-established standard on the interpretation of\ntechnical documents [`RFC2119`](https://www.rfc-editor.org/rfc/rfc2119.html) declares that SHOULD is to be understood as\na recommendation (albeit a very strong one).\nThe correct word to use in a situation where a particular outcome is mandatory is MUST \u0026ndash; this is the most\nappropriate word to use for a unit test, where the result must be as specified.\n\nBut \u0026ldquo;should\u0026rdquo; is so well-established in the terminology of unit testing that rather than fighting against it,\nI have decided to embrace it.\nAll of the function names of the public API start with `should`, and it is even incorporated into the name of the\nlibrary itself.\n\n## Generic Test Functions\n\n### `shouldBe`\n\nThe main function is the one that performs a simple equality test:\n```kotlin\n    testValue shouldBe expectedValue\n```\nAs a generic function, it expects both sides of the comparison to be the same type.\n\nBoolean values may be tested:\n```kotlin\n    booleanValue shouldBe true\n```\n\nArrays may be tested, including `IntArray`, `BooleanArray` _etc._ (the `contentEquals` function will be used for the\ncomparison):\n```kotlin\n    arrayValue shouldBe arrayOf(111, 222, 333)\n```\n\nAnd nullable values may be tested for `null`:\n```kotlin\n    nullableValue shouldBe null\n```\n\n### `shouldNotBe`\n\nThe complement to `shouldBe` performs a simple inequality test:\n```kotlin\n    testValue shouldNotBe wrongValue\n```\nLike `shouldBe`, it expects both sides of the comparison to be the same type, and it may be applied to boolean, array\nand nullable values.\n\n### `shouldBe` with Predicate\n\nA variation on `shouldBe` takes a predicate returning a `Boolean` (see [Named Predicates](#named-predicates) below):\n```kotlin\n    stringValue shouldBe { it.isNotEmpty() }\n```\n\n### `shouldNotBe` with Predicate\n\nThe complement of `shouldBe` with a predicate:\n```kotlin\n    stringValue shouldNotBe { it.isEmpty() }\n```\n\n### `shouldBeOneOf`\n\nTests whether a value is one of a number of possibilities:\n```kotlin\n    testValue shouldBeOneOf listOf(\"alpha\", \"beta\", \"gamma\")\n```\n\n### `shouldNotBeOneOf`\n\nTests whether a value is not one of a number:\n```kotlin\n    testValue shouldNotBeOneOf listOf(\"chi\", \"psi\", \"omega\")\n```\n\n### `shouldBeSameInstance`\n\nPerforms an identity comparison (`===`) on two values:\n```kotlin\n    testValue shouldBeSameInstance original\n```\n\n### `shouldNotBeSameInstance`\n\nThe complement of `shouldBeSameInstance`:\n```kotlin\n    testValue shouldNotBeSameInstance original\n```\n\n### `shouldBeNonNull`\n\nTests whether a value is non-null:\n```kotlin\n    testValue.shouldBeNonNull()\n```\nBecause this doesn't have a parameter value, the infix notation isn\u0026rsquo;t available, but it has the advantage that it\nuses the Kotlin \u0026ldquo;contract\u0026rdquo; mechanism to indicate to the compiler that if this test succeeds, the value may\nsafely be considered as non-null in subsequent operations:\n```kotlin\n    nullableString.shouldBeNonNull()\n    nullableString shouldStartWith \"Hello\"\n```\nThe function also returns the non-null value which can be used in a chained test:\n```kotlin\n    nullableString.shouldBeNonNull().length shouldBe 10\n```\n\n### `shouldBeType`\n\nTests whether a value is of a specified type:\n```kotlin\n    testValue.shouldBeType\u003cString\u003e()\n```\nLike `shouldBeNonNull`, this function doesn\u0026rsquo;t have access to the infix notation, but also like `shouldBeNonNull`,\nit uses the Kotlin \u0026ldquo;contract\u0026rdquo; mechanism to indicate to the compiler that the value may be\n\u0026ldquo;smart cast\u0026rdquo; to the specified type:\n```kotlin\n    testValue.shouldBeType\u003cString\u003e()\n    testValue shouldStartWith \"Hello\"\n```\nIt also returns the type-checked value that may be used in a chained test:\n```kotlin\n    testValue.shouldBeType\u003cString\u003e().length shouldBe 8\n```\n\n### `shouldBeEqual`\n\nThere is a problem with the `shouldBe` syntax.\nAs the LHS (the actual value) is being evaluated, the RHS (the expected value) is still unknown, so type inference can\nnot be used to infer the type of the LHS.\n\nSuppose you have a function that returns a `List` of a generic type; when testing the result of this function, the older\n`assertEquals` function would allow type inference to be used to determine the type of the result.\nWhere type inference is not available, the type must be specified in full, and that can be tedious if the actual type is\ncomplex:\n```kotlin\n    testFunction\u003cList\u003cPair\u003cString, Int\u003e\u003e\u003e() shouldBe listOf(\"name\" to 123)\n```\n\nThe `shouldBeEqual` function allows the use of type inference to determine the inferred type, but of course it loses the\nadvantage of the infix syntax.\n```kotlin\n    shouldBeEqual(listOf(\"name\" to 123), testFunction())\n```\n\nThe `shouldNotBeEqual` function is the complement to the `shouldBeEqual` function.\n\n## Collection Tests\n\n### `shouldContain` and `shouldNotContain`\n\nTests that a collection (an `Iterable`,  _e.g._ a `List`, `Set` _etc._) or an array (including `IntArray` _etc._)\ncontains (or does not contain) a given value:\n```kotlin\n    testList shouldContain \"alpha\"\n```\n\n### `shouldContainKey` and `shouldNotContainKey`\n\nTests that a `Map` contains (or does not contain) a given key:\n```kotlin\n    testMap shouldContainKey \"first\"\n```\n\n## String Tests\n\n### `shouldContain` and `shouldNotContain`\n\nTests that a string contains (or does not contain) the given substring.\n\n### `shouldStartWith` and `shouldNotStartWith`\n\nTests that a string starts with (or does not start with) the given prefix.\n\n### `shouldEndWith` and `shouldNotEndWith`\n\nTests that a string ends with (or does not end with) the given suffix.\n\n### `shouldMatch` and `shouldNotMatch`\n\nTests that a string matches (or does not match) a given `Regex`.\n\n## Exception Tests\n\n### `shouldThrow`\n\nTests whether a block of code results in a nominated exception type being thrown:\n```kotlin\n    shouldThrow\u003cNumberFormatException\u003e {\n        numericString.toInt()\n    }\n```\nAn alternative form includes a check of the message in the exception:\n```kotlin\n    shouldThrow\u003cNumberFormatException\u003e(\"For input string: \\\"$numericString\\\"\") {\n        numericString.toInt()\n    }\n```\nBoth forms of `shouldThrow` return the exception (`Throwable`) instance, which may be used in further tests:\n```kotlin\n    shouldThrow\u003cNumberFormatException\u003e(\"For input string: \\\"$numericString\\\"\") {\n        numericString.toInt()\n    }.let {\n        it.cause shouldBe null\n    }\n```\n\nIt is also possible to check that a nominated exception type is NOT thrown.\n```kotlin\n    shouldNotThrow\u003cNumberFormatException\u003e {\n        numericString.toInt()\n    }\n```\nObviously, an unexpected exception will cause a test to fail, regardless of the use of this function, but it can help by\ndocumenting a specific block of code in which the exception is expected not to be thrown.\n\n## Named Predicates\n\nThe generic `shouldBe` and `shouldNotBe` functions have forms that take a predicate \u0026ndash; a function that, when\napplied to the value, returns a `Boolean`.\nThe example given above tests whether a string value is empty:\n```kotlin\n    stringValue shouldBe { it.isNotEmpty() }\n```\nbut the problem with using predicates in this way is that the library does not have sufficient information to form a\nuseful error message if the predicate returns `false`.\n\nThe library includes a `NamedPredicate` interface, which allows a name to be given to the condition being tested; the\n`shouldBe` (and `shouldNotBe`) function will use that name when reporting an error.\n\nImplementations of the `NamedPredicate` interface must implement the `name` value, along with the usual `invoke()`\nfunction, as in this example:\n```kotlin\n    val even = object : NamedPredicate\u003cInt\u003e {\n        override val name: String = \"even\"\n        override fun invoke(value: Int): Boolean = (value and 1) == 0\n    }\n```\nThen, a test may be written:\n```kotlin\n    value1 shouldBe even\n```\nAnd an error will be reported as \u0026ldquo;`Value should be even, was 123`\u0026rdquo;.\n\n## Comparable Predicates\n\nThere is a set of functions that will create a `NamedPredicate` to perform comparisons on `Comparable` values\nautomatically.\n\n- `lt(value)`: check that the original value (the value to the left of the `shouldBe`) is less than the value given here\n- `le(value)`: check that the original value is less than or equal to the value given here\n- `ge(value)`: check that the original value is greater than or equal to the value given here\n- `gt(value)`: check that the original value is greater than the value given here\n\nThis allows tests such as the following:\n```kotlin\n    value1 shouldBe gt(100)\n```\nAnd these tests will give helpful error messages like \u0026ldquo;`Value should be \u003e 100, was 99`\u0026rdquo;.\n\nThese tests are available only on values of classes that implement the `Comparable` interface, _e.g._ `Int`, `Long`.\n\n## Combined Tests\n\nSome developers prefer to see the results of an entire set of tests, rather than having the test framework stop on the\nfirst failing test.\nThis technique, sometimes referred to as \u0026ldquo;soft assertions\u0026rdquo;, is available using the `shouldCombineTests`\nfunction:\n```kotlin\n    shouldCombineTests {\n        value1 shouldBe 123\n        value2 shouldNotBe \"whatever\"\n    }\n```\nAny tests within the lambda following `shouldCombineTests` will use modified versions of the test functions.\nIf any of these modified test functions fail, then instead of throwing an `AssertionError`, they will simply add the\nerror details to an internal list and allow the function to continue.\nThen, at the end of the whole block of tests, if there are any errors in the list, a `CombinedAssertionError` will be\nthrown \u0026ndash; this includes a formatted message detailing all of the failing tests, along with a list of error results\n(named `errors`) that may be checked programmatically.\n\nNot all of the test functions may be nested within `shouldCombineTests`.\nThe `shouldThrow`, `shouldBeNonNull` and `shouldBeType` functions all return a value, and it is not possible to create a\nversion that returns a value of the required type following an error.\nIf these test functions are used with `shouldCombineTests` they will throw an individual `AssertionError` on failure as\nusual.\n\n## Dependency Specification\n\nThe latest version of the library is 4.6, and it may be obtained from the Maven Central repository.\n(The following dependency declarations assume that the library will be included for test purposes; this is\nexpected to be its principal use.)\n\n### Maven\n```xml\n    \u003cdependency\u003e\n      \u003cgroupId\u003eio.kstuff\u003c/groupId\u003e\n      \u003cartifactId\u003eshould-test\u003c/artifactId\u003e\n      \u003cversion\u003e4.6\u003c/version\u003e\n      \u003cscope\u003etest\u003c/scope\u003e\n    \u003c/dependency\u003e\n```\n### Gradle\n```groovy\n    testImplementation 'io.kstuff:should-test:4.6'\n```\n### Gradle (kts)\n```kotlin\n    testImplementation(\"io.kstuff:should-test:4.6\")\n```\n\nPeter Wall\n\n2025-06-15\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpwall567%2Fshould-test","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpwall567%2Fshould-test","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpwall567%2Fshould-test/lists"}