PageSourceSearch

https://kotest.io/assets/js/dbfa573b.e022c411.js

js kotest.io collected 2026-10-03 21:51:15 UTC 13,820 bytes, 1 lines download raw bytes

1"use strict";(globalThis.webpackChunkkotestdocs=globalThis.webpackChunkkotestdocs||[]).push([[237],{31125(e,n,t){t.r(n),t.d(n,{assets:()=>o,contentTitle:()=>l,default:()=>h,frontMatter:()=>r,metadata:()=>s,toc:()=>d});const s=JSON.parse('{"id":"framework/tags","title":"Grouping Tests with Tags","description":"Sometimes you don\'t want to run all tests and Kotest provides tags to be able to determine which","source":"@site/versioned_docs/version-6.0/framework/tags.md","sourceDirName":"framework","slug":"/framework/tags.html","permalink":"/docs/6.0/framework/tags.html","draft":false,"unlisted":false,"editUrl":"https://github.com/kotest/kotest/blob/master/documentation/versioned_docs/version-6.0/framework/tags.md","tags":[],"version":"6.0","frontMatter":{"id":"tags","title":"Grouping Tests with Tags","slug":"tags.html","sidebar_label":"Grouping Tests"},"sidebar":"framework","previous":{"title":"Test Ordering","permalink":"/docs/6.0/framework/test-ordering.html"},"next":{"title":"Closing resources automatically","permalink":"/docs/6.0/framework/autoclose.html"}}');var i=t(74848),a=t(28453);const r={id:"tags",title:"Grouping Tests with Tags",slug:"tags.html",sidebar_label:"Grouping Tests"},l=void 0,o={},d=[{value:"Marking Tests",id:"marking-tests",level:2},{value:"Running with Tags",id:"running-with-tags",level:2},{value:"Tag Expression Operators",id:"tag-expression-operators",level:2},{value:"Tagging All Tests",id:"tagging-all-tests",level:2},{value:"Tagging a Spec",id:"tagging-a-spec",level:2},{value:"Inheriting tags",id:"inheriting-tags",level:3},{value:"Gradle",id:"gradle",level:2}];function c(e){const n={a:"a",admonition:"admonition",code:"code",em:"em",h2:"h2",h3:"h3",li:"li",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,a.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsxs)(n.p,{children:["Sometimes you don't want to run all tests and Kotest provides tags to be able to determine which\ntests are executed at runtime. Tags are objects inheriting from ",(0,i.jsx)(n.code,{children:"io.kotest.core.Tag"}),"."]}),"\n",(0,i.jsx)(n.p,{children:"For example, to group tests by operating system you could define the following tags:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-kotlin",children:"object Linux : Tag()\nobject Windows: Tag()\n"})}),"\n",(0,i.jsxs)(n.p,{children:["Alternatively, tags can be defined using the ",(0,i.jsx)(n.code,{children:"NamedTag"})," class. When using this class, observe the following rules:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"A tag must not be null or blank."}),"\n",(0,i.jsx)(n.li,{children:"A tag must not contain whitespace."}),"\n",(0,i.jsx)(n.li,{children:"A tag must not contain ISO control characters."}),"\n",(0,i.jsxs)(n.li,{children:["A tag must not contain any of the following characters:\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"!: exclamation mark"}),"\n",(0,i.jsx)(n.li,{children:"(: left paren"}),"\n",(0,i.jsx)(n.li,{children:"): right paren"}),"\n",(0,i.jsx)(n.li,{children:"&: ampersand"}),"\n",(0,i.jsx)(n.li,{children:"|: pipe"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"For example:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-kotlin",children:'val tag = NamedTag("Linux")\n'})}),"\n",(0,i.jsx)(n.h2,{id:"marking-tests",children:"Marking Tests"}),"\n",(0,i.jsxs)(n.p,{children:["Test cases can then be marked with tags using the ",(0,i.jsx)(n.code,{children:"config"})," function:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-kotlin",children:'class MyTest : FreeSpec() {\n  init {\n    "should run on Windows".config(tags = setOf(Windows)) {\n      // ...\n    }\n\n    "should run on Linux".config(tags = setOf(Linux)) {\n      // ...\n    }\n\n    "should run on Windows and Linux".config(tags = setOf(Windows, Linux)) {\n      // ...\n    }\n  }\n}\n'})}),"\n",(0,i.jsx)(n.h2,{id:"running-with-tags",children:"Running with Tags"}),"\n",(0,i.jsxs)(n.p,{children:["Then by invoking the test runner with a system property of ",(0,i.jsx)(n.code,{children:"kotest.tags"})," you can control which tests are run. The expression to be\npassed in is a simple boolean expression using boolean operators: ",(0,i.jsx)(n.code,{children:"&"}),", ",(0,i.jsx)(n.code,{children:"|"}),", ",(0,i.jsx)(n.code,{children:"!"}),", with parenthesis for association."]}),"\n",(0,i.jsxs)(n.p,{children:["For example, ",(0,i.jsx)(n.code,{children:"Tag1 & (Tag2 | Tag3)"})]}),"\n",(0,i.jsx)(n.p,{children:"Provide the simple names of tag object (without package) when you run the tests.\nPlease pay attention to the use of upper case and lower case! If two tag objects have the same simple name (in different name spaces) they are treated as the same tag."}),"\n",(0,i.jsxs)(n.p,{children:["Example: To run only test tagged with ",(0,i.jsx)(n.code,{children:"Linux"}),", but not tagged with ",(0,i.jsx)(n.code,{children:"Database"}),", you would invoke\nGradle like this:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:'gradle test -Dkotest.tags="Linux & !Database"\n'})}),"\n",(0,i.jsxs)(n.p,{children:["Tags can also be included/excluded in runtime (for example, if you're running a project configuration instead of properties) through the ",(0,i.jsx)(n.code,{children:"RuntimeTagExtension"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-kotlin",children:'RuntimeTagExpressionExtension.expression = "Linux & !Database"\n'})}),"\n",(0,i.jsx)(n.h2,{id:"tag-expression-operators",children:"Tag Expression Operators"}),"\n",(0,i.jsx)(n.p,{children:"Operators (in descending order of precedence)"}),"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",(0,i.jsxs)(n.table,{children:[(0,i.jsx)(n.thead,{children:(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.th,{children:"Operator"}),(0,i.jsx)(n.th,{children:"Description"}),(0,i.jsx)(n.th,{children:"Example"})]})}),(0,i.jsxs)(n.tbody,{children:[(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"!"}),(0,i.jsx)(n.td,{children:"not"}),(0,i.jsx)(n.td,{children:"!macos"})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"&"}),(0,i.jsx)(n.td,{children:"and"}),(0,i.jsx)(n.td,{children:"linux & integration"})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"|"}),(0,i.jsx)(n.td,{children:"or"}),(0,i.jsx)(n.td,{children:"windows | microservice"})]})]})]}),"\n",(0,i.jsx)(n.h2,{id:"tagging-all-tests",children:"Tagging All Tests"}),"\n",(0,i.jsx)(n.p,{children:"You can add a tag to all tests in a spec using the tags function in the spec itself. For example:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-kotlin",children:'class MyTestClass : FunSpec({\n\n  tags(Linux, Mysql)\n\n  test("my test") { } // automatically marked with the above tags\n})\n'})}),"\n",(0,i.jsx)(n.admonition,{type:"caution",children:(0,i.jsx)(n.p,{children:"When tagging tests in this way, the spec class will still need to be instantiated in order to examine the tags on each test, because the test itself may define further tags."})}),"\n",(0,i.jsx)(n.admonition,{type:"note",children:(0,i.jsxs)(n.p,{children:["If no root tests are active at runtime, the ",(0,i.jsx)(n.a,{href:"/docs/6.0/framework/lifecycle-hooks.html",children:"beforeSpec"})," and ",(0,i.jsx)(n.a,{href:"/docs/6.0/framework/lifecycle-hooks.html",children:"afterSpec"})," callbacks will ",(0,i.jsx)(n.em,{children:"not"})," be invoked."]})}),"\n",(0,i.jsx)(n.h2,{id:"tagging-a-spec",children:"Tagging a Spec"}),"\n",(0,i.jsx)(n.p,{children:"There are two annotations you can add to a 
1spec class itself - @Tags and @RequiresTag - which accept one or more tag names as their arguments."}),"\n",(0,i.jsx)(n.p,{children:"The first tag - @Tags - will be applied to all tests in the class, however this will only stop a spec from being instantiated if we can guarantee\nthat no tests would be executed (because a tag is being explicitly excluded)."}),"\n",(0,i.jsx)(n.p,{children:"Consider the following example:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-kotlin",children:'@Tags("Linux")\nclass MyTestClass : FunSpec({\n\n  tags(UnitTest)\n\n  beforeSpec { println("Before") }\n\n  test("A").config(tags = setOf(Mysql)) {}\n  test("B").config(tags = setOf(Postgres)) {}\n  test("C") {}\n})\n'})}),"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",(0,i.jsxs)(n.table,{children:[(0,i.jsx)(n.thead,{children:(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.th,{children:"Runtime Tags"}),(0,i.jsx)(n.th,{children:"Spec Created"}),(0,i.jsx)(n.th,{children:"Callbacks"}),(0,i.jsx)(n.th,{children:"Outcome"})]})}),(0,i.jsxs)(n.tbody,{children:[(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"kotest.tags=Linux"}),(0,i.jsx)(n.td,{children:"yes"}),(0,i.jsx)(n.td,{children:"yes"}),(0,i.jsxs)(n.td,{children:["A, B, C are executed because all tests inherit the ",(0,i.jsx)(n.code,{children:"Linux"})," tag from the annotation"]})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"kotest.tags=Linux & Mysql"}),(0,i.jsx)(n.td,{children:"yes"}),(0,i.jsx)(n.td,{children:"yes"}),(0,i.jsxs)(n.td,{children:["A is executed only because all tests have the ",(0,i.jsx)(n.code,{children:"Linux"})," tag, but only A has the ",(0,i.jsx)(n.code,{children:"Mysql"})," tag"]})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"kotest.tags=!Linux"}),(0,i.jsx)(n.td,{children:"no"}),(0,i.jsx)(n.td,{children:"no"}),(0,i.jsx)(n.td,{children:"No tests are executed, and the MyTestClass is not instantiated because we can exclude it based on the tags annotation"})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"kotest.tags=!UnitTest"}),(0,i.jsx)(n.td,{children:"yes"}),(0,i.jsx)(n.td,{children:"no"}),(0,i.jsxs)(n.td,{children:["No tests are executed because all tests inherit ",(0,i.jsx)(n.code,{children:"UnitTest"})," from the tags function. MyTestClass is instantiated in order to retrieve the tags defined in the class. The beforeSpec callback is not executed because there are no active tests."]})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"kotest.tags=Mysql"}),(0,i.jsx)(n.td,{children:"yes"}),(0,i.jsx)(n.td,{children:"yes"}),(0,i.jsxs)(n.td,{children:["A is executed only, because that is the only test marked with ",(0,i.jsx)(n.code,{children:"Mysql"})]})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"kotest.tags=!Mysql"}),(0,i.jsx)(n.td,{children:"yes"}),(0,i.jsx)(n.td,{children:"yes"}),(0,i.jsxs)(n.td,{children:["B, C are executed only, because A is excluded by being marked with ",(0,i.jsx)(n.code,{children:"Mysql"})]})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"kotest.tags=Linux & !Mysql"}),(0,i.jsx)(n.td,{children:"yes"}),(0,i.jsx)(n.td,{children:"yes"}),(0,i.jsxs)(n.td,{children:["B, C are executed only, because all tests inherit ",(0,i.jsx)(n.code,{children:"Linux"})," from the annotation, but A is excluded by the ",(0,i.jsx)(n.code,{children:"Mysql"})," tag"]})]})]})]}),"\n",(0,i.jsx)(n.p,{children:"The second tag - @RequiresTag - only checks that all the referenced tags are present and if not, will skip the spec."}),"\n",(0,i.jsx)(n.p,{children:"For example, the following spec would be skipped and not instantiated unless the Linux and Mysql tags were\nspecified at runtime."}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-kotlin",children:'@RequiresTag("Linux", "Mysql")\nclass MyTestClass : FunSpec()\n'})}),"\n",(0,i.jsx)(n.admonition,{type:"note",children:(0,i.jsx)(n.p,{children:'Note that when you use these annotations you pass the tag string name, not the tag itself. This is due to Kotlin annotations only allow "primitive" arguments'})}),"\n",(0,i.jsx)(n.h3,{id:"inheriting-tags",children:"Inheriting tags"}),"\n",(0,i.jsxs)(n.p,{children:["By default, the ",(0,i.jsx)(n.code,{children:"@Tags"})," annotation will only be considered on the immediate Spec which it was applied to. However, a Spec can also inherit tags from superclasses and superinterfaces. To enable this, toggle ",(0,i.jsx)(n.code,{children:"tagInheritance = true"})," in your ",(0,i.jsx)(n.a,{href:"./project-config.html",children:"project config"})]}),"\n",(0,i.jsx)(n.h2,{id:"gradle",children:"Gradle"}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.strong,{children:"Special attention is needed in your gradle configuration"})}),"\n",(0,i.jsx)(n.p,{children:"To use System Properties (-Dx=y), your gradle must be configured to propagate them to the test executors, and an extra configuration must be added to your tests:"}),"\n",(0,i.jsx)(n.p,{children:"Groovy:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-groovy",children:"test {\n    //... Other configurations ...\n    systemProperties = System.properties\n}\n"})}),"\n",(0,i.jsx)(n.p,{children:"Kotlin Gradle DSL:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-kotlin",children:"val test by tasks.getting(Test::class) {\n    // ... Other configurations ...\n    systemProperties = System.getProperties().asIterable().associate { it.key.toString() to it.value }\n}\n"})}),"\n",(0,i.jsx)(n.p,{children:"This will guarantee that the system property is correctly read by the JVM."})]})}function h(e={}){const{wrapper:n}={...(0,a.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(c,{...e})}):c(e)}},28453(e,n,t){t.d(n,{R:()=>r,x:()=>l});var s=t(96540);const i={},a=s.createContext(i);function r(e){const n=s.useContext(a);return s.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:r(e.components),s.createElement(a.Provider,{value:n},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.