PageSourceSearch

https://kotest.io/assets/js/b061065a.e8085f37.js

js kotest.io collected 2026-10-03 21:54:05 UTC 8,808 bytes, 1 lines download raw bytes

1"use strict";(globalThis.webpackChunkkotestdocs=globalThis.webpackChunkkotestdocs||[]).push([[6066],{19769(e,t,n){n.r(t),n.d(t,{assets:()=>o,contentTitle:()=>l,default:()=>h,frontMatter:()=>r,metadata:()=>s,toc:()=>c});const s=JSON.parse('{"id":"framework/writing_tests","title":"Writing Tests","description":"By using the language features available in Kotlin, Kotest is able to provide a more powerful and yet simple approach","source":"@site/versioned_docs/version-5.9.x/framework/writing_tests.md","sourceDirName":"framework","slug":"/framework/writing-tests.html","permalink":"/docs/5.9.x/framework/writing-tests.html","draft":false,"unlisted":false,"editUrl":"https://github.com/kotest/kotest/blob/master/documentation/versioned_docs/version-5.9.x/framework/writing_tests.md","tags":[],"version":"5.9.x","frontMatter":{"id":"writing_tests","title":"Writing Tests","slug":"writing-tests.html","sidebar_label":"Writing Tests"},"sidebar":"framework","previous":{"title":"Setup","permalink":"/docs/5.9.x/framework/project-setup.html"},"next":{"title":"Testing Styles","permalink":"/docs/5.9.x/framework/testing-styles.html"}}');var a=n(74848),i=n(28453);const r={id:"writing_tests",title:"Writing Tests",slug:"writing-tests.html",sidebar_label:"Writing Tests"},l=void 0,o={},c=[{value:"Nested Tests",id:"nested-tests",level:3},{value:"Dynamic Tests",id:"dynamic-tests",level:3},{value:"Lifecycle Callbacks",id:"lifecycle-callbacks",level:3}];function d(e){const t={a:"a",code:"code",em:"em",h3:"h3",p:"p",pre:"pre",...(0,i.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(t.p,{children:"By using the language features available in Kotlin, Kotest is able to provide a more powerful and yet simple approach\nto defining tests. Gone are the days when tests need to be methods defined in a Java file."}),"\n",(0,a.jsxs)(t.p,{children:["In Kotest a test is essentially just a function ",(0,a.jsx)(t.code,{children:"TestContext -> Unit"})," which contains your test logic.\nAny assert statements (",(0,a.jsx)(t.em,{children:"matchers"})," in Kotest nomenclature) invoked in this function that throw an exception\nwill be intercepted by the framework and used to mark that test as failed or success."]}),"\n",(0,a.jsxs)(t.p,{children:["Test functions are not defined manually, but instead using the Kotest DSL, which provides several ways in which these functions\ncan be created and nested. The DSL is accessed by creating a class that extends from a class that implements a particular\n",(0,a.jsx)(t.a,{href:"/docs/5.9.x/framework/testing-styles.html",children:"testing style"}),"."]}),"\n",(0,a.jsxs)(t.p,{children:["For example, using the ",(0,a.jsx)(t.em,{children:"Fun Spec"})," style, we create test functions using the ",(0,a.jsx)(t.code,{children:"test"})," keyword, providing a name, and the\nactual test function."]}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-kotlin",children:'class MyFirstTestClass : FunSpec({\n\n   test("my first test") {\n      1 + 2 shouldBe 3\n   }\n\n})\n'})}),"\n",(0,a.jsxs)(t.p,{children:["Note that tests must be defined inside an ",(0,a.jsx)(t.code,{children:"init {}"})," block or an init lambda as in the previous example."]}),"\n",(0,a.jsx)(t.h3,{id:"nested-tests",children:"Nested Tests"}),"\n",(0,a.jsx)(t.p,{children:"Most styles offer the ability to nest tests. The actual syntax varies from style to style,\nbut is essentially just a different keyword used for the outer tests."}),"\n",(0,a.jsxs)(t.p,{children:["For example, in ",(0,a.jsx)(t.em,{children:"Describe Spec"}),", the outer tests are created using the ",(0,a.jsx)(t.code,{children:"describe"})," function and\ninner tests using the ",(0,a.jsx)(t.code,{children:"it"})," function.\nJavaScript and Ruby developers will instantly recognize this style as it is commonly used in testing frameworks\nfor those languages."]}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-kotlin",children:'class NestedTestExamples : DescribeSpec({\n\n   describe("an outer test") {\n\n      it("an inner test") {\n        1 + 2 shouldBe 3\n      }\n\n      it("an inner test too!") {\n        3 + 4 shouldBe 7\n      }\n   }\n\n})\n'})}),"\n",(0,a.jsxs)(t.p,{children:["In Kotest nomenclature, tests that can contain other tests are called ",(0,a.jsx)(t.em,{children:"test containers"})," and tests\nthat are terminal or leaf nodes are called ",(0,a.jsx)(t.em,{children:"test cases"}),". Both can contain test logic and assertions."]}),"\n",(0,a.jsx)(t.h3,{id:"dynamic-tests",children:"Dynamic Tests"}),"\n",(0,a.jsx)(t.p,{children:"Since tests are just functions, they are evaluated at runtime."}),"\n",(0,a.jsx)(t.p,{children:"This approach offers a huge advantage - tests can be dynamically created. Unl
1ike traditional JVM test frameworks,\nwhere tests are always methods and therefore declared at compile time, Kotest can add tests conditionally at runtime."}),"\n",(0,a.jsx)(t.p,{children:"For example, we could add tests based on elements in a list."}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-kotlin",children:'class DynamicTests : FunSpec({\n\n    listOf(\n      "sam",\n      "pam",\n      "tim",\n    ).forEach {\n       test("$it should be a three letter name") {\n           it.shouldHaveLength(3)\n       }\n    }\n})\n'})}),"\n",(0,a.jsx)(t.p,{children:"This would result in three tests being created at runtime. It would be the equivalent to writing:"}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-kotlin",children:'class DynamicTests : FunSpec({\n\n   test("sam should be a three letter name") {\n      "sam".shouldHaveLength(3)\n   }\n\n   test("pam should be a three letter name") {\n      "pam".shouldHaveLength(3)\n   }\n\n   test("tim should be a three letter name") {\n     "tim".shouldHaveLength(3)\n   }\n})\n'})}),"\n",(0,a.jsx)(t.h3,{id:"lifecycle-callbacks",children:"Lifecycle Callbacks"}),"\n",(0,a.jsx)(t.p,{children:"Kotest provides several callbacks which are invoked at various points during a test's lifecycle.\nThese callbacks are useful for resetting state, setting up and tearing down resources that a test might use, and so on."}),"\n",(0,a.jsxs)(t.p,{children:["As mentioned earlier, test functions in Kotest are labelled either ",(0,a.jsx)(t.em,{children:"test containers"})," or ",(0,a.jsx)(t.em,{children:"test cases"}),", in addition to\nthe containing class being labelled a ",(0,a.jsx)(t.em,{children:"spec"}),". We can register callbacks that are invoked before or after any test function, container, test case, or a spec itself."]}),"\n",(0,a.jsx)(t.p,{children:"To register a callback, we just pass a function to one of the callback methods."}),"\n",(0,a.jsxs)(t.p,{children:["For example, we can add a callback before and after any ",(0,a.jsx)(t.em,{children:"test case"})," using a function literal:"]}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-kotlin",children:'class Callbacks : FunSpec({\n\n   beforeEach {\n      println("Hello from $it")\n   }\n\n   test("sam should be a three letter name") {\n      "sam".shouldHaveLength(3)\n   }\n\n   afterEach {\n      println("Goodbye from $it")\n   }\n})\n'})}),"\n",(0,a.jsxs)(t.p,{children:["Note that the order of the callbacks in the file is not important.\nFor example, an ",(0,a.jsx)(t.code,{children:"afterEach"})," block can be placed first in the class if you so desired."]}),"\n",(0,a.jsx)(t.p,{children:"If we want to extract common code, we can create a named function and re-use it for multiple files.\nFor example, say we wanted to reset a database before every test in more than one file, we could do this:"}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-kotlin",children:'val resetDatabase: BeforeTest = {\n  // truncate all tables here\n}\n\nclass ReusableCallbacks : FunSpec({\n\n   beforeTest(resetDatabase)\n\n   test("this test will have a sparkling clean database!") {\n       // test logic here\n   }\n})\n'})}),"\n",(0,a.jsxs)(t.p,{children:["For details of all callbacks and when they are invoked, see ",(0,a.jsx)(t.a,{href:"/docs/5.9.x/framework/lifecycle-hooks.html",children:"here"})," and ",(0,a.jsx)(t.a,{href:"/docs/5.9.x/framework/extensions/extensions-introduction.html",children:"here"}),"."]})]})}function h(e={}){const{wrapper:t}={...(0,i.R)(),...e.components};return t?(0,a.jsx)(t,{...e,children:(0,a.jsx)(d,{...e})}):d(e)}},28453(e,t,n){n.d(t,{R:()=>r,x:()=>l});var s=n(96540);const a={},i=s.createContext(a);function r(e){const t=s.useContext(i);return s.useMemo(function(){return"function"==typeof e?e(t):{...t,...e}},[t,e])}function l(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:r(e.components),s.createElement(i.Provider,{value:t},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.