PageSourceSearch

https://kotest.io/assets/js/a3794b90.7bc26066.js

js kotest.io collected 2026-10-03 21:52:26 UTC 5,171 bytes, 1 lines download raw bytes

1"use strict";(globalThis.webpackChunkkotestdocs=globalThis.webpackChunkkotestdocs||[]).push([[2334],{50992(e,t,r){r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>h,frontMatter:()=>o,metadata:()=>n,toc:()=>l});const n=JSON.parse('{"id":"proptest/customgens","title":"Custom Generators","description":"To write your own generator for a type T, you just create an instance of Arb or Exhaustive.","source":"@site/versioned_docs/version-5.5.x/proptest/customgens.md","sourceDirName":"proptest","slug":"/proptest/custom-generators.html","permalink":"/docs/5.5.x/proptest/custom-generators.html","draft":false,"unlisted":false,"editUrl":"https://github.com/kotest/kotest/blob/master/documentation/versioned_docs/version-5.5.x/proptest/customgens.md","tags":[],"version":"5.5.x","frontMatter":{"id":"customgens","title":"Custom Generators","slug":"custom-generators.html"},"sidebar":"proptest","previous":{"title":"Configuration","permalink":"/docs/5.5.x/proptest/property-test-config.html"},"next":{"title":"Shrinking","permalink":"/docs/5.5.x/proptest/property-test-shrinking.html"}}');var s=r(74848),a=r(28453);const o={id:"customgens",title:"Custom Generators",slug:"custom-generators.html"},i=void 0,c={},l=[{value:"Arbitrary",id:"arbitrary",level:3},{value:"Exhaustive",id:"exhaustive",level:3}];function d(e){const t={a:"a",code:"code",h3:"h3",p:"p",pre:"pre",...(0,a.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)(t.p,{children:["To write your own generator for a type T, you just create an instance of ",(0,s.jsx)(t.code,{children:"Arb<T>"})," or ",(0,s.jsx)(t.code,{children:"Exhaustive<T>"}),"."]}),"\n",(0,s.jsx)(t.h3,{id:"arbitrary",children:"Arbitrary"}),"\n",(0,s.jsxs)(t.p,{children:["When writing a custom arbitrary we can use the ",(0,s.jsx)(t.code,{children:"arbitrary"})," builder which accepts a lambda that must return the type we are generating for.\nThe parameter to this lambda is a ",(0,s.jsx)(t.code,{children:"RandomSource"})," parameter which contains the seed and the ",(0,s.jsx)(t.code,{children:"Random"})," instance. We should typically\nuse the provided ",(0,s.jsx)(t.code,{children:"RandomSource"})," if we need access to a ",(0,s.jsx)(t.code,{children:"kotlin.Random"})," instance, as this instance will have been seeded by the framework to allow for repeatable tests."]}),"\n",(0,s.jsxs)(t.p,{children:["For example, here is a custom arb that generates a random int between 3 and 6 using the ",(0,s.jsx)(t.code,{children:"arbitrary"})," builder."]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-kotlin",children:"val sillyArb = arbitrary { rs: RandomSource ->\n   rs.random.nextInt(3..6)\n}\n\n"})}),"\n",(0,s.jsxs)(t.p,{children:["In addition to the ",(0,s.jsx)(t.code,{children:"RandomSource"})," parameter, the arbitrary builder lambda also provides the ",(0,s.jsx)(t.code,{children:"ArbitraryBuilderSyntax"})," context which we can leverage\nto compose other arbitraries when building ours."]}),"\n",(0,s.jsxs)(t.p,{children:["For example, here is an ",(0,s.jsx)(t.code,{children:"Arbitrary"})," that supports a custom class called ",(0,s.jsx)(t.code,{children:"Person"}),", delegating to a String arbitrary and an Int arbitrary."]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-kotlin",children:"data class Person(val name: String, val age: Int)\n\nval personArb = arbitrary {\n   val name = Arb.string(10..12).bind()\n   val age = Arb.int(21, 150).bind()\n   Person(name, age)\n}\n"})}),"\n",(0,s.jsxs)(t.p,{children:["The resulting arbitrary produced using this syntax is equivalent to using ",(0,s.jsx)(t.a,{href:"/docs/5.5.x/proptest/generator-operations.html#map",children:"map"}),",\n",(0,s.jsx)(t.a,{href:"/docs/5.5.x/proptest/generator-operations.html#flatmap",children:"flatMap"})," and ",(0,s.jsx)(t.a,{href:"/docs/5.5.x/proptest/generator-operations.html#bind",children:"bind"}),"."]}),"\n",(0,s.jsx)(t.h3,{id:"exhaustive",children:"Exhaustive"}),"\n",(0,s.jsxs)(t.p,{children:["When writing a custom exhaustive we can use the ",(0,s.jsx)(t.code,{children:"exhaustive()"}
1)," extension function on a List. Nothing more to it than that really!"]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-kotlin",children:"val singleDigitPrimes = listOf(2,3,5,7).exhaustive()\n"})}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-kotlin",children:'class PropertyExample: StringSpec({\n    "testing single digit primes" {\n        checkAll(singleDigitPrimes) { prime ->\n           isPrime(prime) shouldBe true\n           isPrime(prime * prime) shouldBe false\n        }\n    }\n})\n'})})]})}function h(e={}){const{wrapper:t}={...(0,a.R)(),...e.components};return t?(0,s.jsx)(t,{...e,children:(0,s.jsx)(d,{...e})}):d(e)}},28453(e,t,r){r.d(t,{R:()=>o,x:()=>i});var n=r(96540);const s={},a=n.createContext(s);function o(e){const t=n.useContext(a);return n.useMemo(function(){return"function"==typeof e?e(t):{...t,...e}},[t,e])}function i(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:o(e.components),n.createElement(a.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.