1"use strict";(self.webpackChunkwoodpecker=self.webpackChunkwoodpecker||[]).push([["2598"],{45291(e,n,t){t.r(n),t.d(n,{metadata:()=>i,default:()=>p,frontMatter:()=>c,contentTitle:()=>l,toc:()=>a,assets:()=>d});var i=JSON.parse('{"id":"development/testing","title":"Testing","description":"Backend","source":"@site/versioned_docs/version-3.16/92-development/09-testing.md","sourceDirName":"92-development","slug":"/development/testing","permalink":"/docs/3.16/development/testing","draft":false,"unlisted":false,"editUrl":"https://github.com/woodpecker-ci/woodpecker/edit/main/docs/versioned_docs/version-3.16/92-development/09-testing.md","tags":[],"version":"3.16","sidebarPosition":9,"frontMatter":{},"sidebar":"tutorialSidebar","previous":{"title":"Swagger, API Spec and Code Generation","permalink":"/docs/3.16/development/openapi"},"next":{"title":"Packaging","permalink":"/docs/3.16/development/packaging"}}'),s=t(94686),o=t(23191);let c={},l="Testing",d={},a=[{value:"Backend",id:"backend",level:2},{value:"Unit Tests",id:"unit-tests",level:3},{value:"Integration Tests",id:"integration-tests",level:3},{value:"Dummy backend",id:"dummy-backend",level:3}];function r(e){let n={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.header,{children:(0,s.jsx)(n.h1,{id:"testing",children:"Testing"})}),"\n",(0,s.jsx)(n.h2,{id:"backend",children:"Backend"}),"\n",(0,s.jsx)(n.h3,{id:"unit-tests",children:"Unit Tests"}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.a,{href:"https://go.dev/doc/tutorial/add-a-test",children:"We use default golang unit tests"}),"\nwith ",(0,s.jsx)(n.a,{href:"https://pkg.go.dev/github.com/stretchr/testify/assert",children:(0,s.jsx)(n.code,{children:'"github.com/stretchr/testify/assert"'})})," to simplify testing."]}),"\n",(0,s.jsx)(n.h3,{id:"integration-tests",children:"Integration Tests"}),"\n",(0,s.jsx)(n.h3,{id:"dummy-backend",children:"Dummy backend"}),"\n",(0,s.jsxs)(n.p,{children:["There is a special backend called ",(0,s.jsx)(n.strong,{children:(0,s.jsx)(n.code,{children:"dummy"})})," which does not execute any commands, but emulates how a typical backend should behave.\nTo enable it you need to build the agent or cli with the ",(0,s.jsx)(n.code,{children:"test"})," build tag."]}),"\n",(0,s.jsx)(n.p,{children:"An example pipeline config would be:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:'when:\n event: manual\n\nsteps:\n - name: echo\n image: dummy\n commands: echo "hello woodpecker"\n environment:\n SLEEP: \'1s\'\n\nservices:\n echo:\n image: dummy\n commands: echo "i am a service"\n'})}),"\n",(0,s.jsxs)(n.p,{children:["This could be executed via ",(0,s.jsx)(n.code,{children:"woodpecker-cli --log-level trace exec --backend-engine dummy example.yaml"}),":"]}),"\n","\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-none",children:"9:18PM DBG pipeline/pipeline.go:94 > executing 2 stages, in order of: CLI=exec\n9:18PM DBG pipeline/pipeline.go:104 > stage CLI=exec StagePos=0 Steps=echo\n9:18PM DBG pipeline/pipeline.go:104 > stage CLI=exec StagePos=1 Steps=echo\n9:18PM TRC pipeline/backend/dummy/dummy.go:75 > create workflow environment taskUUID=01J10P578JQE6E25VV1EQF0745\n9:18PM DBG pipeline/pipeline.go:176 > prepare CLI=exec step=echo\n9:18PM DBG pipeline/pipeline.go:203 > executing CLI=exec step=echo\n9:18PM TRC pipeline/backend/dummy/dummy.go:81 > start step echo taskUUID=01J10P578JQE6E25VV1EQF0745\n9:18PM TRC pipeline/backend/dummy/dummy.go:167 > tail logs of step echo taskUUID=01J10P578JQE6E25VV1EQF0745\n9:18PM DBG pipeline/pipeline.go:209 > complete CLI=exec step=echo\n[echo:L0:0s] StepName: echo\n[echo:L1:0s] StepType: service\n[echo:L2:0s] StepUUID: 01J10P578JQE6E25VV1A2DNQN9\n[echo:L3:0s] StepCommands:\n[echo:L4:0s] ------------------\n[echo:L5:0s] echo ja\n[echo:L6:0s] ------------------\n[echo:L7:0s] 9:18PM DBG pipeline/pipeline.go:176 > prepare CLI=exec step=echo\n9:18PM DBG pipeline/pipeline.go:203 > executing CLI=exec step=echo\n9:18PM TRC pipeline/backend/dummy/dummy.go:81 > start step echo taskUUID=01J10P578JQE6E25VV1EQF0745\n9:18PM TRC pipeline/backend/dummy/dummy.go:167 > tail logs of step echo taskUUID=01J10P578JQE6E25VV1EQF0745\n[echo:L0:0s] StepName: echo\n[echo:L1:0s] StepType: commands\n[echo:L2:0s] StepUUID: 01J10P578JQE6E25VV1DFSXX1Y\n[echo:L3:0s] StepCommands:\n[echo:L4:0s] ------------------\n[echo:L5:0s] echo ja\n[echo:L6:0s] ------------------\n[echo:L7:0s] 9:18PM TRC pipeline/backend/dummy/dummy.go:108 > wait for step echo taskUUID=01J10P578JQE6E25VV1EQF0745\n9:18PM TRC pipeline/backend/dummy/dummy.go:187 > stop step echo taskUUID=01J10P578JQE6E25VV1EQF0745\n9:18PM DBG pipeline/pipeline.go:209 > complete CLI=exec step=echo\n9:18PM TRC pipeline/backend/dummy/dummy.go:208 > delete workflow environment taskUUID=01J10P578JQE6E25VV1EQF0745\n"})}),"\n","\n",(0,s.jsx)(n.p,{children:"There are also environment variables to alter step behavior:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"SLEEP: 10"})," will let the step wait 10 seconds"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"EXPECT_TYPE"})," allows to check if a step is a ",(0,s.jsx)(n.code,{children:"clone"}),", ",(0,s.jsx)(n.code,{children:"service"}),", ",(0,s.jsx)(n.code,{children:"plugin"})," or ",(0,s.jsx)(n.code,{children:"commands"})]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"STEP_START_FAIL: true"})," if set will simulate a step to fail before actually being started (e.g. happens when the container image can not be pulled)"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"STEP_TAIL_FAIL: true"})," if set will error when we simulate to read from stdout for logs"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"STEP_EXIT_CODE: 2"})," if set will be used as exit code, default is 0"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"STEP_OOM_KILLED: true"})," simulates a step being killed by memory constrains"]}),"\n"]}),"\n",(0,s.jsxs)(n.p,{children:["You can let the setup of a whole workflow fail by setting it's UUID to ",(0,s.jsx)(n.code,{children:"WorkflowSetupShouldFail"}),"."]})]})}function p(e={}){let{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(r,{...e})}):r(e)}},23191(e,n,t){t.d(n,{R:()=>c,x:()=>l});var i=t(92990);let s={},o=i.createContext(s);function c(e){let n=i.useContext(o);return i.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(s):e.components||s:c(e.components),i.createElement(o.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.