SkillAgentSearch skills...

Scenari

Clojure BDD library - Executable Specification with Behavior-Driven Development

Install / Use

/learn @defsquare/Scenari
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<a href="https://github.com/jgrodziski/scenari"> <img src="https://cdn.rawgit.com/jgrodziski/scenari/68d74b6d/scenari.svg" width="100%" height="250"> </a>

Scenari - Executable Specification / BDD in Clojure

Scenari is an Executable Specification Clojure library aimed at writing and executing usage scenarios following the Behavior-Driven Development - BDD - style. It has an external DSL, following the gherkin grammar (in short: Given/When/Then), and execute each scenario's steps with associated Clojure code.

Installation

;;add this dependency to your project.clj file
[io.defsquare/scenari "2.0.2"]
;;or deps.edn
{
 io.defsquare/scenari {:mvn/version "2.0.2"}
}
;;then in your ns statement
(:require [scenari.v2.core :as scenari :refer [defgiven defwhen defthen deffeature]])

Clojars Project

Basic Usage

Write Scenarios in plain text

First, write your scenarios in plain text using the Gherkin grammar in a file or String : You can add a "narrrative" for all your scenarios with the story syntax at the beginning of the story file (As a role I want to do something In order to get value)

Scenario: create a new product
# this is a comment
When I create a new product with name "iphone 6" and description "awesome phone"
Then I receive a response with an id 56422 and a location URL
# this a second comment
# on two lines
When I invoke a GET request on location URL
Then I receive a 200 response

Scenario: get product info
When I invoke a GET request on location URL
Then I receive a 200 response

Declaring a specification

(require 'scenari.v2.core :refer [deffeature])

(deffeature my-specification "./path/to/feature/file") ;; define deftest bound to symbol 'my-specification', put the specification as clojure data-structure in metadata and return the specification
;;=> 
;;{:scenarios [{:id "0ef9b8a9-e035-4ae2-96c4-662c0b8988de",
;;		:pre-run (),
;;		:post-run (),
;;		:scenario-name " create a new product",
;;		:steps [{:sentence-keyword :when,
;;			 :sentence "I create a new product with name \"iphone 6\" and description \"awesome phone\"",
;;			 :raw "When I create a new product with name \"iphone 6\" and description \"awesome phone\"",
;;			 :params [{:type :value, :val "iphone 6"} {:type :value, :val "awesome phone"}],
;;			 :order 0,
;;			 :glue nil}
;;			 ...steps]}
;;	        ...scenarios ],
;; :pre-run ()}

Write glue-code

Then write the code that will get executed for each scenario steps:


(require 'scenari.v2.core :refer [defwhen defthen])

(defwhen "I create a new product with name {string} and description {string}"
[_ name desc]
  (println "executing my product creation function with params " name desc)
  (let [id (UUID/next.)]
  	{:id (UUID/next. ) 
  	 :name name 
  	 :desc desc 
  	 :qty (rand-int 50) 
  	 :location-url (str "http://example.com/product/" id)}))


(defthen "I receive a response with an id {string}"
  [_ id]
  (println (str "executing the assertion that the product has been created with the id " id))
  id)

Tips: you can get a function snippet generated for you when executing the spec without step function. Think about enclosing with quote 'your data' in step sentence to get them detected by the parser and it'll generate a step function skeleton in the output with the correct sentence matcher group. Example:

Executing the specification with the step sentence without any matching function:

When I create a new product with name "iphone 6" and description "awesome phone"

will generate in the stdout the following step function skeleton:

Missing step for : When I create a new product with name "iphone 6" and description "awesome phone"
(defwhen "I create a new product with name {string} and description {string}"  [state arg0 arg1]  (do "something"))

how to get data from the scenario into your step function

Every group the sentence matcher will find (everything enclosed in curly braces in your sentence matcher) will be transmitted as a string to your step function params with the same left-to-right order, BUT the data is first evaluated as clojure.edn data string (see clojure.edn/read-string) and IF it is a Clojure data structure ((coll? evaluated-data) returns true), THEN it will be transmitted evaluated as a param to the step function.

Tips: the map will be detected by the parser and it'll generate a step function skeleton in the output with the correct sentence matcher.

Execute scenario(s)

There is three-way to execute scenarios, depending on your situation

Tree execution

Declaring your specification using scenari.v2.core/deffeature returns the parsed specification as clojure data structure. By using scenari.v2.core/run-scenario, the specification as data will be ran

(require 'scenari.v2.core :refer [run-feature run-features])
(run-feature #'my-specification)

;;OR
(require 'scenari.v2.core :refer [run-scenarios])
(run-features #'my-specification)

The execution report will be returned, rely on same clojure data-structure returned by scenari.v2.core/deffeature. Will set :

  • final :status of scenario(s) execution
  • step :status as pending when not executed, failed when assertions fail or exception thrown, success at last
  • step :input-state as the value returned by the previous step executed (empty map for the first one)
  • step :output-state as the value returned by the current step

This method is useful for debugging.

Clojure-test execution

Use clojure-test reporting system by printing execution.

(require 'scenari.v2.test :refer [run-feature])
(run-feature #'my-specification)
;; ________________________
;; Feature :
;; 
;; Testing scenario :
;; When I create a new product with name "iphone 6" and description "awesome phone"         (from /")
;; Step failed
;; create a new product failed at step  of
;; 
;; Testing scenario :
;; When I invoke a GET request on location URL         (from scenari.v2.glue/"I invoke a GET request on location URL")
;; =====> {:kix "lol"}
;; Then I receive a 200 response         (from /"")
;; Step failed
;; get product info failed at step  of
;; 
;; ________________________
;;

Useful to integrate a feature in a clojure test namespace

Kaocha runner

Kaocha is a test runner and handle test phase lifecycle.

By defining a test type in your kaocha configuration file (tests.edn by default) like this

#kaocha/v1
        {:tests [{:id                                :scenario
                  :type                              :kaocha.type/scenari
                  :kaocha/source-paths               ["src"]
                  :kaocha/test-paths                 ["test/scenario"]
                  :kaocha.type.scenari/glue-paths    ["test/scenario/glue"]}]}

You are able to launch your scenario using kaocha repl utility function

(require 'kaocha.repl :as krepl)
(krepl/run :scenario)

;; Testing scenario :  create a new product
;;   When I invoke a GET request on location URL         (from scenari.v2.glue/"I invoke a GET request on location URL")
;;   When I create a new product with name "iphone 6" and description "awesome phone" with properties         (from scenari.v2.glue/"I create a new product with name \"(.*)\" and description \"(.*)\" with properties")
;;   Then I receive a response with an id 56422         (from scenari.v2.glue/"I receive a response with an id 56422")
;;   Then a location URL         (from scenari.v2.glue/"a location URL")
;; 
;; 
;; 1 tests, 1 assertions, 0 failures.
;; => #:kaocha.result{:count 1, :pass 1, :error 0, :fail 0, :pending 0}

Suitable when using kaocha to manage test lifecycle.

Using hooks

By providing an options maps in scenari.v2.core/deffeature, you can specify function which execute :

  • :pre-run before feature execution
  • :post-run after feature executed
  • :pre-scenario-run before each scenario execution
  • :post-scenaro-run after each scenario executed Example:
(require 'scenari.v2.core :refer [deffeature])

(defn before-all [] (prn "init feature components"))
(defn before-each [] (prn "init scenario components"))
(defn after-each [] (prn "clean scenario side effects"))
(defn clean [] (prn "reset and shut down components"))

(deffeature my-specification "./path/to/feature/file"
			{:pre-run           [#'before-all]
			 :pre-scenario-run  [#'before-each]
			 :post-scenario-run [#'after-each]
			 :post-run          [#'clean]})

Provide an initial state

For each scenario execution, an initial state can be provided within the options map of deffeature.

Example:

(deffeature my-specification "./path/to/feature/file"
			{:default-scenario-state {:foo "bar"}})

By default, the scenario state is an empty map {}.

Documentation

Development Workflow

The Development Workflow Guide provides a comprehensive overview of the full development cycle when using Scenari. It covers writing feature files, defining features in code, implementing step definitions, and understanding how execution works.

Internal Feature Structure

The Feature Structure Documentation provides a detailed explana

View on GitHub
GitHub Stars70
CategoryDevelopment
Updated19d ago
Forks5

Languages

Clojure

Security Score

100/100

Audited on Mar 13, 2026

No findings