package module
Version: v1.0.1 Latest Latest

This package is not in the latest version of its module.

Go to latest
Published: Dec 9, 2021 License: Apache-2.0 Imports: 35 Imported by: 43


🐍 Venom

Venom execute "executors" (script, HTTP Request, etc. ) and assertions. It can also generate xUnit result files.

Venom Demonstration

Command Line

Download latest binary release from

$ venom run -h

$ venom run *.yml

Notice that variables initialized with -var-from-file argument can be overrided with -var argument.

  venom run [flags]

      --format string           --format:yaml, json, xml, tap (default "xml")
  -h, --help                    help for run
      --lib-dir string          Lib Directory: this directory can contain user executors. This overrides the default lib folder directory
      --output-dir string       Output Directory: create tests results file inside this directory
      --stop-on-failure         Stop running Test Suite on first Test Case failure
      --var stringArray         --var cds='cds -f config.json' --var cds2='cds -f config.json'
      --var-from-file strings   --var-from-file filename.yaml --var-from-file filename2.yaml: yaml, must contains a dictionnary
  -v, --verbose count           verbose. -vv to very verbose and -vvv to very verbose with CPU Profiling

Globstar support: venom run ./foo/b*/**/z*.yml

You can define the arguments with environment variables:

venom run my-test-suite.yml --format=json
# is the same as
VENOM_FORMAT=json venom run my-test-suite.yml
      --format           -  example: VENOM_FORMAT=json
      --output-dir       -  example: VENOM_OUTPUT_DIR=.
      --lib-dir          -  example: VENOM_LIB_DIR=/etc/venom/lib:$HOME/venom.d/lib
      --stop-on-failure  -  example: VENOM_STOP_ON_FAILURE=true
      --var              -  example: VENOM_VAR="foo=bar"
      --var-from-file    -  example: VENOM_VAR_FROM_FILE="fileA.yml fileB.yml"
      -v                 -  example: VENOM_VERBOSE=2 is the same as -vv

You can define the venom settings using a configuration file .venomrc. This configuration file should be placed in the current directory or in the home directory.

  - foo=bar
  - my_var_file.yaml
stop_on_failure: true
format: xml
output_dir: output
lib_dir: lib
verbosity: 3

Please note that command line flags overrides the configuration file. Configuration file overrides the environment variables.

Docker image

Venom can be started inside a docker image with:

$ git clone
$ cd venom
$ docker run -it $(docker build -q .) --rm -v $(pwd)/outputs:/outputs -v $(pwd):/tests run /tests/testsuite.yaml


A test suite is a collection of test cases that are intended to be used to test a software program to show that it has a specified set of behaviors. A test case is a specification of the inputs, execution conditions, testing procedure, and expected results that define a single test to be executed to achieve a particular software testing objective, such as to exercise a particular program path or to verify compliance with a specific requirement.

In venom the testcases are executed sequentially within a testsuite. Each testcase is an ordered set of steps. Each step is based on an executor that enable some specific kind of behavior.

In venom a testsuite is written in one yaml file respecting the following structure.

name: Title of TestSuite
- name: TestCase with default value, exec cmd. Check if exit code != 1
  - script: echo 'foo'
    type: exec

- name: Title of First TestCase
  - script: echo 'foo'
    - result.code ShouldEqual 0
  - script: echo 'bar'
    - result.systemout ShouldNotContainSubstring foo
    - result.timeseconds ShouldBeLessThan 1

- name: GET http testcase, with 5 seconds timeout
  - type: http
    method: GET
    timeout: 5
    - result.body ShouldContainSubstring /dedicated/server
    - result.body ShouldContainSubstring /ipLoadbalancing
    - result.statuscode ShouldEqual 200
    - result.timeseconds ShouldBeLessThan 1

- name: Test with retries and delay in seconds between each try
  - type: http
    method: GET
    retry: 3
    delay: 2
    - result.statuscode ShouldEqual 200


User defined executors

You can define an executor with a single yaml file. This is a good way to abstract technical or functional behaviors and reuse them in complex testsuites.


file lib/customA.yml

executor: hello
  myarg: {}
- script: echo "{\"hello\":\"{{.input.myarg}}\"}"
  - result.code ShouldEqual 0
    hello: "{{.result.systemoutjson.hello}}"
  all: "{{.result.systemoutjson}}"

file testsuite.yml

name: testsuite with a user executor
- name: testA
  - type: hello
    myarg: World
    - result.display.hello ShouldContainSubstring World
    - result.alljson.hello ShouldContainSubstring World

Notice the variable alljson. All variables declared in output are automatically converted in a json format with the suffix json. In the example above, two implicit variables are available: displayjson.hello and alljson.

Venom will load user's executors from the directory lib/ relative to the testsuite path. You add executors source path using the flag --lib-dir. Note that all folders listed with --lib-dir will be scanned recursively to find .yml files as user executors.

# lib/*.yml files will be loaded as executors.
$ venom run testsuite.yml 

# executors will be loaded from /etc/venom/lib, $HOME/venom.d/lib and lib/ directory relative to testsuite.yml file.
$ venom run --lib-dir=/etc/venom/lib:$HOME/venom.d/lib testsuite.yml 


Testsuite variables

You can define variable on the testsuite level.

name: myTestSuite
  foo: foo
    bar: bar
  aString: '{"foo": "bar"}'

- name: first-test-case
  - type: exec
    script: echo '{{.foo}} {{}}'
    - result.code ShouldEqual 0
    - result.systemout ShouldEqual "foo bar"

- name: foobar
  - script: echo '{{.aString}}'
    info: value of aString is {{.aString}}
    - ShouldEqual bar

Each user variable used in testsuite must be declared in this section. You can override its value at runtime in a number of ways:

  • Individually, with the --var command line option.
  • In variable definitions files, either specified on the command line --var-from-file.
  • As environment variables.
Variable on Command Line

To specify individual variables on the command line, use the --var option when running the venom run commands:

venom run --var="foo=bar"
venom run --var='foo_list=["biz","buz"]'
venom run --var='foo={"biz":"bar","biz":"barr"}'

The --var option can be used many times in a single command.

Variable Definitions Files

To set lots of variables, it is more convenient to specify their values in a variable definitions file. This file is a YAML dictionnary and you have specify that file on the command line with --var-from-file

Environment Variables

As a fallback for the other ways of defining variables, venom searches the environment of its own process for environment variables named VENOM_VAR_ followed by the name of a declared variable.

$ export VENOM_VAR_foo=bar
$ venom run *.yml
Variable helpers

Available helpers and some examples:

  • abbrev
  • abbrevboth
  • trunc
  • trim
  • upper: {{.myvar | upper}}
  • lower: {{.myvar | lower}}
  • title
  • untitle
  • substr
  • repeat
  • trimall
  • trimAll
  • trimSuffix
  • trimPrefix
  • nospace
  • initials
  • randAlphaNum
  • randAlpha
  • randASCII
  • randNumeric
  • swapcase
  • shuffle
  • snakecase
  • camelcase
  • quote
  • squote
  • indent
  • nindent
  • replace: {{.myvar | replace "_" "."}}
  • plural
  • default: {{.myvar | default ""}}
  • empty
  • coalesce
  • toJSON
  • toPrettyJSON
  • b64enc
  • b64dec {{.result.bodyjson | b64enc}}
  • escape: replace ‘_‘, ‘/’, ‘.’ by ‘-’

How to use outputs from a test step as input of another test step

To be able to reuse a property from a teststep in a following testcase or step, you have to extract the variable, as the following example.

After the first step execution, venom extracts a value using a regular expression foo with a ([a-z]+) here from the content of the result.systemout property returned by the executor. Then it is able to reuse this variable with the name testA.myvariable with testA corresponding to the name of the testcase.

name: MyTestSuite
- name: testA
  - type: exec
    script: echo 'foo with a bar here'
        from: result.systemout
        regex: foo with a ([a-z]+) here

- name: testB
  - type: exec
    script: echo {{.testA.myvariable}}
    - result.code ShouldEqual 0
    - result.systemout ShouldContainSubstring bar

Builtin venom variables

name: MyTestSuite
- name: testA
  - type: exec
    script: echo '{{.venom.testsuite}} {{.venom.testsuite.filename}} {{.venom.testcase}} {{.venom.teststep.number}} {{.venom.datetime}} {{.venom.timestamp}}'
    # will display something as: MyTestSuite MyTestSuiteWithVenomBuiltinVar.yml testA 0 2018-08-05T21:38:24+02:00 1533497904

Builtin variables:

  • {{.venom.testsuite}}
  • {{.venom.testsuite.filename}}
  • {{.venom.testsuite.shortName}}
  • {{.venom.testsuite.workdir}}
  • {{.venom.testcase}}
  • {{.venom.teststep.number}}
  • {{.venom.datetime}}
  • {{.venom.timestamp}}

Tests Report

venom run --format=xml --output-dir="."

Available formats: jUnit (xml), json, yaml, tap reports



Advanced usage

Debug your testsuites

There is two ways to debug a testsuite:

  • use -v flag on venom binary.
    • $ venom run -v test.yml will output a venom.log file
    • $ venom run -vv test.yml will output a venom.log file and dump.json files for each teststep.
  • use info keyword your teststep: test.yml file:
name: Exec testsuite
- name: testA
  - type: exec
    script: echo 'foo with a bar here'
      - this a first info
      - and a second...
- name: cat json
  - script: cat exec/testa.json
    info: "the value of result.systemoutjson is {{.result.systemoutjson}}"
    - ShouldContainSubstrin bar
$ venom run test.yml

# output:

 • Exec testsuite (exec.yml)
 	• testA SUCCESS
	  [info] this a first info (exec.yml:8)
	  [info] and a second... (exec.yml:9)
 	• testB SUCCESS
 	• sleep 1 SUCCESS
 	• cat json SUCCESS
	  [info] the value of result.systemoutjson is map[foo:bar] (exec.yml:34)

Skip testcase

It is possible to skip testcase according to some assertions. For instance, the following example will skip the last testcase.

name: "Skip testsuite"
  foo: bar

- name: init
  - type: exec
    script: echo {{.foo}}
    - result.code ShouldEqual 0
    - result.systemout ShouldContainSubstring bar

- name: do-not-skip-this
  - foo ShouldNotBeEmpty
  - type: exec
    script: exit 0

- name: skip-this
    - foo ShouldBeEmpty
  - type: exec
    script: command_not_found
    - result.code ShouldEqual 0


Common errors with quotes

If you have this kind of error:

err:unable to parse file "foo.yaml": error converting YAML to JSON: yaml: line 8: did not find expected key

this is probably because you try to use a json value instead of a string. You should have more details in venom.log file.


  body: >-
        "the-attribute": "the-value"
- type: http
  body: "{{.body}}"


  body: >-
        "the-attribute": "the-value"
- type: http
  body: '{{.body}}'

Note the simple quote on the value of body.

Use venom in CI

Venom can be use on dev environement or your CI server. To display correctly the venom output, you probably will have to export the environment variable IS_TTY=true before running venom.


How to write your own executor?

How to compile?

$ make build


Copyright 2021 OVH SAS

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.




This section is empty.


View Source
var (
	//Version is set with -ldflags "-X$(VERSION)"
	Version = "snapshot"


func BoolVarFromCtx added in v1.0.0

func BoolVarFromCtx(ctx context.Context, varname string) bool

func Debug added in v1.0.0

func Debug(ctx context.Context, format string, args ...interface{})

func Dump added in v1.0.0

func Dump(v interface{}) (map[string]interface{}, error)

Dump dumps v as a map[string]interface{}.

func DumpString added in v1.0.0

func DumpString(v interface{}) (map[string]string, error)

DumpString dumps v as a map[string]string{}, key in lowercase

func DumpStringPreserveCase added in v1.0.0

func DumpStringPreserveCase(v interface{}) (map[string]string, error)

DumpStringPreserveCase dumps v as a map[string]string{}

func Error added in v1.0.0

func Error(ctx context.Context, format string, args ...interface{})

func Fatal added in v1.0.0

func Fatal(ctx context.Context, format string, args ...interface{})

func GetExecutorResult added in v1.0.0

func GetExecutorResult(r interface{}) map[string]interface{}

func Info added in v1.0.0

func Info(ctx context.Context, format string, args ...interface{})

func InitTestLogger added in v1.0.0

func InitTestLogger(t *testing.T)

func IntVarFromCtx added in v1.0.0

func IntVarFromCtx(ctx context.Context, varname string) int

func JSONUnmarshal added in v1.0.0

func JSONUnmarshal(btes []byte, i interface{}) error

func RemoveNotPrintableChar added in v0.16.0

func RemoveNotPrintableChar(in string) string

RemoveNotPrintableChar removes not printable chararacter from a string

func StringMapInterfaceVarFromCtx added in v1.0.0

func StringMapInterfaceVarFromCtx(ctx context.Context, varname string) map[string]interface{}

func StringMapStringVarFromCtx added in v1.0.0

func StringMapStringVarFromCtx(ctx context.Context, varname string) map[string]string

func StringSliceVarFromCtx added in v1.0.0

func StringSliceVarFromCtx(ctx context.Context, varname string) []string

func StringVarFromCtx added in v1.0.0

func StringVarFromCtx(ctx context.Context, varname string) string

func VarFromCtx added in v1.0.0

func VarFromCtx(ctx context.Context, varname string) interface{}

func Warn added in v1.0.0

func Warn(ctx context.Context, format string, args ...interface{})

func Warning added in v1.0.0

func Warning(ctx context.Context, format string, args ...interface{})


type AssignStep added in v0.27.0

type AssignStep struct {
	Assignments map[string]Assignment `json:"vars" yaml:"vars" mapstructure:"vars"`

type Assignment added in v0.27.0

type Assignment struct {
	From  string `json:"from" yaml:"from"`
	Regex string `json:"regex" yaml:"regex"`

type ContextKey

type ContextKey string

ContextKey can be added in context to store contextual infos. Also used by logger.

type Executor

type Executor interface {
	// Run run a Test Step
	Run(context.Context, TestStep) (interface{}, error)

Executor execute a testStep.

type ExecutorRunner added in v1.0.0

type ExecutorRunner interface {

	Name() string
	Retry() int
	Delay() int
	Timeout() int
	Info() []string
	Type() string
	GetExecutor() Executor
	// contains filtered or unexported methods

type ExecutorWithSetup added in v1.0.0

type ExecutorWithSetup interface {
	Setup(ctx context.Context, vars H) (context.Context, error)
	TearDown(ctx context.Context) error

type Failure

type Failure struct {
	TestcaseClassname  string `xml:"-" json:"-" yaml:"-"`
	TestcaseName       string `xml:"-" json:"-" yaml:"-"`
	TestcaseLineNumber int    `xml:"-" json:"-" yaml:"-"`
	StepNumber         int    `xml:"-" json:"-" yaml:"-"`
	Assertion          string `xml:"-" json:"-" yaml:"-"`
	Error              error  `xml:"-" json:"-" yaml:"-"`

	Value   string `xml:",cdata" json:"value" yaml:"value,omitempty"`
	Type    string `xml:"type,attr,omitempty" json:"type" yaml:"type,omitempty"`
	Message string `xml:"message,attr,omitempty" json:"message" yaml:"message,omitempty"`

Failure contains data related to a failed test.

func (Failure) String added in v1.0.0

func (f Failure) String() string

type H added in v0.27.0

type H map[string]interface{}

func AllVarsFromCtx added in v1.0.0

func AllVarsFromCtx(ctx context.Context) H

func (*H) Add added in v0.27.0

func (h *H) Add(k string, v interface{})

func (*H) AddAll added in v0.27.0

func (h *H) AddAll(h2 H)

func (*H) AddAllWithPrefix added in v0.27.0

func (h *H) AddAllWithPrefix(p string, h2 H)

func (*H) AddWithPrefix added in v0.27.0

func (h *H) AddWithPrefix(p, k string, v interface{})

func (H) Clone added in v0.27.0

func (h H) Clone() H

type InnerResult

type InnerResult struct {
	Value string `xml:",cdata" json:"value" yaml:"value"`

InnerResult is used by TestCase

type Property

type Property struct {
	XMLName xml.Name `xml:"property" json:"-" yaml:"-"`
	Name    string   `xml:"name,attr" json:"name" yaml:"-"`
	Value   string   `xml:"value,attr" json:"value" yaml:"-"`

Property represents a key/value pair used to define properties.

type Skipped added in v0.16.0

type Skipped struct {
	Value string `xml:",cdata" json:"value" yaml:"value,omitempty"`

Skipped contains data related to a skipped test.

type StepAssertions

type StepAssertions struct {
	Assertions []string `json:"assertions,omitempty" yaml:"assertions,omitempty"`

StepAssertions contains step assertions

type TestCase

type TestCase struct {
	XMLName   xml.Name  `xml:"testcase" json:"-" yaml:"-"`
	Classname string    `xml:"classname,attr,omitempty" json:"classname" yaml:"-"`
	Errors    []Failure `xml:"error,omitempty" json:"errors" yaml:"errors,omitempty"`
	Failures  []Failure `xml:"failure,omitempty" json:"failures" yaml:"failures,omitempty"`
	Name      string    `xml:"name,attr" json:"name" yaml:"name"`

	Skipped      []Skipped         `xml:"skipped,omitempty" json:"skipped" yaml:"skipped,omitempty"`
	Status       string            `xml:"status,attr,omitempty" json:"status" yaml:"status,omitempty"`
	Systemout    InnerResult       `xml:"system-out,omitempty" json:"systemout" yaml:"systemout,omitempty"`
	Systemerr    InnerResult       `xml:"system-err,omitempty" json:"systemerr" yaml:"systemerr,omitempty"`
	Time         float64           `xml:"time,attr,omitempty" json:"time" yaml:"time,omitempty"`
	RawTestSteps []json.RawMessage `xml:"-" json:"steps" yaml:"steps"`

	TestSuiteVars H `xml:"-" json:"-" yaml:"-"`
	Vars          H `xml:"-" json:"-" yaml:"vars"`

	Skip        []string `xml:"-" json:"skip" yaml:"skip"`
	IsExecutor  bool     `xml:"-" json:"-" yaml:"-"`
	IsEvaluated bool     `xml:"-" json:"-" yaml:"-"`
	// contains filtered or unexported fields

TestCase is a single test case with its result.

func (*TestCase) AppendError added in v1.0.0

func (tc *TestCase) AppendError(err error)

type TestStep

type TestStep map[string]interface{}

TestStep represents a testStep

func (TestStep) IntValue added in v1.0.0

func (t TestStep) IntValue(name string) (int, error)

func (TestStep) StringSliceValue added in v1.0.0

func (t TestStep) StringSliceValue(name string) ([]string, error)

func (TestStep) StringValue added in v1.0.0

func (t TestStep) StringValue(name string) (string, error)

type TestSuite

type TestSuite struct {
	XMLName      xml.Name   `xml:"testsuite" json:"-" yaml:"-"`
	Disabled     int        `xml:"disabled,attr,omitempty" json:"disabled" yaml:""`
	Errors       int        `xml:"errors,attr,omitempty" json:"errors" yaml:"-"`
	Failures     int        `xml:"failures,attr,omitempty" json:"failures" yaml:"-"`
	Hostname     string     `xml:"hostname,attr,omitempty" json:"hostname" yaml:"-"`
	ID           string     `xml:"id,attr,omitempty" json:"id" yaml:"-"`
	Name         string     `xml:"name,attr" json:"name" yaml:"name"`
	Filename     string     `xml:"-" json:"-" yaml:"-"`
	Package      string     `xml:"package,attr,omitempty" json:"package" yaml:"-"`
	Properties   []Property `xml:"-" json:"properties" yaml:"-"`
	Skipped      int        `xml:"skipped,attr,omitempty" json:"skipped" yaml:"skipped,omitempty"`
	Total        int        `xml:"tests,attr" json:"total" yaml:"total,omitempty"`
	TestCases    []TestCase `xml:"testcase" json:"testcases" yaml:"testcases"`
	Version      string     `xml:"version,omitempty" json:"version" yaml:"version,omitempty"`
	Time         string     `xml:"time,attr,omitempty" json:"time" yaml:"-"`
	Timestamp    string     `xml:"timestamp,attr,omitempty" json:"timestamp" yaml:"-"`
	Vars         H          `xml:"-" json:"-" yaml:"vars"`
	ComputedVars H          `xml:"-" json:"-" yaml:"-"`
	WorkDir      string     `xml:"-" json:"-" yaml:"-"`

TestSuite is a single JUnit test suite which may contain many testcases.

type Tests

type Tests struct {
	XMLName      xml.Name    `xml:"testsuites" json:"-" yaml:"-"`
	Total        int         `xml:"-" json:"total"`
	TotalOK      int         `xml:"-" json:"ok"`
	TotalKO      int         `xml:"-" json:"ko"`
	TotalSkipped int         `xml:"-" json:"skipped"`
	TestSuites   []TestSuite `xml:"testsuite" json:"test_suites"`

Tests contains all informations about tests in a pipeline build

type UserExecutor added in v1.0.0

type UserExecutor struct {
	Executor     string            `json:"executor" yaml:"executor"`
	Input        H                 `json:"input" yaml:"input"`
	RawTestSteps []json.RawMessage `json:"steps" yaml:"steps"`
	Output       json.RawMessage   `json:"output" yaml:"output"`
	Filename     string            `json:"-" yaml:"-"`

func (UserExecutor) Run added in v1.0.0

func (ux UserExecutor) Run(ctx context.Context, step TestStep) (interface{}, error)

Run is not implemented on user executor

func (UserExecutor) ZeroValueResult added in v1.0.0

func (ux UserExecutor) ZeroValueResult() interface{}

type Venom added in v0.17.0

type Venom struct {
	LogOutput io.Writer

	PrintFunc func(format string, a ...interface{}) (n int, err error)

	LibDir        string
	OutputFormat  string
	OutputDir     string
	StopOnFailure bool
	Verbose       int
	// contains filtered or unexported fields

func New added in v0.17.0

func New() *Venom

New instanciates a new venom on venom run cmd

func (*Venom) AddVariables added in v0.17.0

func (v *Venom) AddVariables(variables map[string]interface{})

func (*Venom) GetExecutorRunner added in v1.0.0

func (v *Venom) GetExecutorRunner(ctx context.Context, ts TestStep, h H) (context.Context, ExecutorRunner, error)

GetExecutorRunner initializes a test by name no type -> exec is default

func (*Venom) InitLogger added in v1.0.0

func (v *Venom) InitLogger() error

InitLogger initializes venom logger

func (*Venom) OutputResult added in v0.17.0

func (v *Venom) OutputResult(tests Tests, elapsed time.Duration) error

OutputResult output result to sdtout, files...

func (*Venom) Parse added in v0.17.0

func (v *Venom) Parse(ctx context.Context, path []string) error

Parse parses tests suite to check context and variables

func (*Venom) Print added in v1.0.0

func (v *Venom) Print(format string, a ...interface{})

func (*Venom) Println added in v1.0.0

func (v *Venom) Println(format string, a ...interface{})

func (*Venom) PrintlnTrace added in v1.0.0

func (v *Venom) PrintlnTrace(s string)

func (*Venom) Process added in v0.17.0

func (v *Venom) Process(ctx context.Context, path []string) (*Tests, error)

Process runs tests suite and return a Tests result

func (*Venom) RegisterExecutorBuiltin added in v1.0.0

func (v *Venom) RegisterExecutorBuiltin(name string, e Executor)

RegisterExecutorBuiltin register builtin executors

func (*Venom) RegisterExecutorPlugin added in v1.0.0

func (v *Venom) RegisterExecutorPlugin(name string, e Executor)

RegisterExecutorPlugin register plugin executors

func (*Venom) RegisterExecutorUser added in v1.0.0

func (v *Venom) RegisterExecutorUser(name string, e Executor)

RegisterExecutorUser register User sxecutors

func (*Venom) RunTestStep added in v0.17.0

func (v *Venom) RunTestStep(ctx context.Context, e ExecutorRunner, tc *TestCase, stepNumber int, step TestStep) interface{}

RunTestStep executes a venom testcase is a venom context

func (*Venom) RunUserExecutor added in v1.0.0

func (v *Venom) RunUserExecutor(ctx context.Context, runner ExecutorRunner, tcIn *TestCase, step TestStep) (interface{}, error)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL