{"id":23069078,"url":"https://github.com/danielmiladinov/burpless","last_synced_at":"2025-08-15T13:31:55.440Z","repository":{"id":201936124,"uuid":"707191588","full_name":"danielmiladinov/burpless","owner":"danielmiladinov","description":"An idiomatic Clojure wrapper for the latest version of cucumber-jvm, inspired by auxoncorp/clj-cucumber","archived":false,"fork":false,"pushed_at":"2024-11-10T21:40:31.000Z","size":88,"stargazers_count":18,"open_issues_count":8,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-12-15T21:42:03.493Z","etag":null,"topics":["clojure","cucumber","cucumber-jvm","library","testing","testing-tool"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/danielmiladinov.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-10-19T12:02:21.000Z","updated_at":"2024-12-13T17:35:32.000Z","dependencies_parsed_at":"2024-06-19T01:59:55.502Z","dependency_job_id":"5dd6a8e9-796e-4a03-ad67-af38216bf06b","html_url":"https://github.com/danielmiladinov/burpless","commit_stats":null,"previous_names":["danielmiladinov/burpless"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielmiladinov%2Fburpless","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielmiladinov%2Fburpless/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielmiladinov%2Fburpless/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielmiladinov%2Fburpless/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/danielmiladinov","download_url":"https://codeload.github.com/danielmiladinov/burpless/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":229880720,"owners_count":18138641,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clojure","cucumber","cucumber-jvm","library","testing","testing-tool"],"created_at":"2024-12-16T06:12:53.144Z","updated_at":"2025-08-15T13:31:55.405Z","avatar_url":"https://github.com/danielmiladinov.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# danielmiladinov/burpless\n\nAn idiomatic Clojure wrapper around [cucumber-jvm](https://github.com/cucumber/cucumber-jvm), for writing Cucumber feature tests.\n\nLibrary name inspired by [Roman Ostash](https://github.com/Romacoding).\n\n[![Clojars Project](https://img.shields.io/clojars/v/net.clojars.danielmiladinov/burpless.svg)](https://clojars.org/net.clojars.danielmiladinov/burpless)\n\n- [Usage](#usage)\n  - [Add the Dependency](#add-the-dependency)\n  - [Write a Feature File](#write-a-feature-file)\n  - [Write a Test File](#write-a-test-file)\n  - [Run the tests and copy the step definition snippets from the output](#run-the-tests-and-copy-the-step-definition-snippets-from-the-output)\n  - [Copy and Paste the Step Functions](#copy-and-paste-the-step-functions)\n  - [Burpless Step Functions](#burpless-step-functions)\n    - [State](#state)\n    - [Type Hints](#type-hints)\n  - [Update the Step Functions to Pass the Test](#update-the-step-functions-to-pass-the-test)\n- [License](#license)\n\n## Usage\n\n### Add the Dependency\n#### Deps:\nAdd it to your `deps.edn`:\n```clojure\n{:deps {net.clojars.danielmiladinov/burpless {:mvn/version \"1.0.0\"}}}\n```\n#### Lein/Boot:\nAdd it to your `project.clj`:\n```clojure\n[net.clojars.danielmiladinov/burpless \"1.0.0\"]\n```\n\n### Write a Feature File\n\nSave the following as `test/my-first.feature`:\n```gherkin\nFeature: My first feature\n\n  Scenario: Learning to use Burpless\n    Given I have a string value of \"Hello, Burpless!\" under the :message key in my state\n    And I have a long value of 5 under the :stars key in my state\n    And I have a table of the following high and low temperatures:\n      | 81 | 49 |\n      | 88 | 54 |\n      | 76 | 56 |\n      | 70 | 48 |\n      | 81 | 55 |\n    When I am ready to check my state\n    Then my state should be equal to the following Clojure literal:\n    \"\"\"edn\n    {:message \"Hello, Burpless!\"\n     :stars 5\n     :highs-and-lows [[81 49] [88 54] [76 56] [70 48] [81 55]]\n     :ready-to-check? true}\n    \"\"\"\n```\nYes, burpless supports `DataTable` and `DocString` step arguments! More on that later.\n\n### Write a Test File\nSave the following as `test/my-first-feature-test.clj`.\nFor now, it's relatively empty, but we'll be adding more to it shortly:\n```clojure\n(ns my-first-feature-test\n  (:require [clojure.test :refer [deftest is]]\n            [burpless :refer [run-cucumber step]]))\n\n(def steps\n  [])\n\n(deftest my-first-feature\n  (is (zero? (run-cucumber \"test/my-first.feature\" steps))))\n```\n\n### Run the tests and copy the step definition snippets from the output\nRun the test using your preferred test runner. Below is just one possible method:\n```bash\n$ clojure -T:build test\n```\n\nYou should see output similar to the following:\n```\nRunning tests in #{\"test\"}\n\nTesting my-first-feature-test\n\nScenario: Learning to use Burpless                                                     # test/my-first.feature:3\n  Given I have a string value of \"Hello, Burpless!\" under the :message key in my state\n  And I have a long value of 5 under the :stars key in my state\n  And I have a table of the following high and low temperatures:\n    | 81 | 49 |\n    | 88 | 54 |\n    | 76 | 56 |\n    | 70 | 48 |\n    | 81 | 55 |\n  When I am ready to check my state\n  Then my state should be equal to the following Clojure literal:\n    \"\"\"edn\n    {:message \"Hello, Burpless!\"\n     :stars 5\n     :highs-and-lows [[81 49] [88 54] [76 56] [70 48] [81 55]]\n     :ready-to-check? true}\n    \"\"\"\n\nUndefined scenarios:\nfile:///path/to/my-first.feature:3 # Learning to use Burpless\n\n1 Scenarios (1 undefined)\n5 Steps (4 skipped, 1 undefined)\n0m0.062s\n\n\nYou can implement missing steps with the snippets below:\n\n(step :Given \"I have a string value of {string} under the {keyword} key in my state\"\n      (fn [state ^String string ^clojure.lang.Keyword keyword]\n        ;; Write code here that turns the phrase above into concrete actions\n        (throw (io.cucumber.java.PendingException.))))\n\n(step :Given \"I have a long value of {int} under the {keyword} key in my state\"\n      (fn [state ^Integer int1 ^clojure.lang.Keyword keyword]\n        ;; Write code here that turns the phrase above into concrete actions\n        (throw (io.cucumber.java.PendingException.))))\n\n(step :Given \"I have a table of the following high and low temperatures:\"\n      (fn [state ^io.cucumber.datatable.DataTable dataTable]\n        ;; Write code here that turns the phrase above into concrete actions\n        (throw (io.cucumber.java.PendingException.))))\n\n(step :When \"I am ready to check my state\"\n      (fn [state]\n        ;; Write code here that turns the phrase above into concrete actions\n        (throw (io.cucumber.java.PendingException.))))\n\n(step :Then \"my state should be equal to the following Clojure literal:\"\n      (fn [state ^clojure.lang.IObj fromDocString]\n        ;; Write code here that turns the phrase above into concrete actions\n        (throw (io.cucumber.java.PendingException.))))\n\n\n\nFAIL in (my-first-feature) (my_first_feature_test.clj:9)\nexpected: (zero? (run-cucumber \"test/my-first.feature\" steps))\n  actual: (not (zero? 1))\n\nRan 1 tests containing 1 assertions.\n1 failures, 0 errors.\nExecution error (ExceptionInfo) at build/test (build.clj:19).\nTests failed\n\nFull report at:\n/path/to/full-report.edn\n```\n\n### Copy and Paste the Step Functions\nWhile these step functions in their current form definitely will not make the feature pass,\nthey will at least give us a good starting-off point to build towards a possible solution.\n```clojure\n(step :Given \"I have a string value of {string} under the {keyword} key in my state\"\n      (fn [state ^String string ^clojure.lang.Keyword keyword]\n        ;; Write code here that turns the phrase above into concrete actions\n        (throw (io.cucumber.java.PendingException.))))\n\n(step :Given \"I have a long value of {int} under the {keyword} key in my state\"\n      (fn [state ^Integer int1 ^clojure.lang.Keyword keyword]\n        ;; Write code here that turns the phrase above into concrete actions\n        (throw (io.cucumber.java.PendingException.))))\n\n(step :Given \"I have a table of the following high and low temperatures:\"\n      (fn [state ^io.cucumber.datatable.DataTable dataTable]\n        ;; Write code here that turns the phrase above into concrete actions\n        (throw (io.cucumber.java.PendingException.))))\n\n(step :When \"I am ready to check my state\"\n      (fn [state]\n        ;; Write code here that turns the phrase above into concrete actions\n        (throw (io.cucumber.java.PendingException.))))\n\n(step :Then \"my state should be equal to the following Clojure literal:\"\n      (fn [state ^clojure.lang.IObj fromDocString]\n        ;; Write code here that turns the phrase above into concrete actions\n        (throw (io.cucumber.java.PendingException.))))\n\n```\nIf you are following closely, you'll see some things that may not immediately make sense:\n- Why does each step function take a first argument called `state`?\n- Why are there type hints on all of the other step function argumnents? Are they necessary?\nLet's try to answer these questions in the next section.\n\n### Burpless Step Functions\nFor every call to `run-cucumber`, Burpless instantiates a Cucumber Backend as well as a Cucumber runtime, with which\nto test your feature. To keep things simple, it expects to run only a single feature at a time. For your convenience,\n`run-cucumber` also maintains an [`atom`](https://clojure.org/reference/atoms) to hold all of the state against which\nyour step functions will be executed.\n\nWe use the burpless macro, `step`, to define our step functions. It takes three parameters:\n- A Clojure keyword representing one of the [Gherkin keywords](https://cucumber.io/docs/gherkin/reference/#keywords)\n  for steps, e.g. [Given](https://cucumber.io/docs/gherkin/reference/#given),\n  [When](https://cucumber.io/docs/gherkin/reference/#when), [Then](https://cucumber.io/docs/gherkin/reference/#then),\n  [And](https://cucumber.io/docs/gherkin/reference/#and-but), [But](https://cucumber.io/docs/gherkin/reference/#and-but)\n- A string representing either a [CucumberExpression](https://github.com/cucumber/cucumber-expressions#readme) (preferred) or\n[RegularExpression](https://en.wikipedia.org/wiki/Regular_expression) pattern to match for the step\n  - (all regular expression patterns must start with `^` and end with `$` or they will be interpreted as\n    cucumber expressions by the Cucumber runtime.)\n- The function to call when executing the step. Every step function will receive the current value of the state atom\nas its first argument. Any output parameters (`CucumberExpression`) or capture groups (`RegularExpression`) matched in\nthe pattern, zero or more, are provided as additional arguments to the function. Finally, a step may also contain a\n[Step Argument](https://cucumber.io/docs/gherkin/reference/#step-arguments). The step argument can either be a\n[Doc String](https://cucumber.io/docs/gherkin/reference/#doc-strings) or a\n[Data Table](https://cucumber.io/docs/gherkin/reference/#data-tables).\nStep arguments are great for when you need to provide more data\nthan can comfortably fit into a single line of the feature file.\n\n#### State\nEach step function takes as its first argument the current value of the contents of the state atom. All the additional\narguments, if any, come from the arguments parsed from the feature file. It is your responsibility as the step function\nauthor to return the new / next value of the state from each step function.\n\nYou are free to write your feature tests any way you like, but a typical approach is to follow the\n[“Arrange, Act, Assert”](https://wiki.c2.com/?ArrangeActAssert) pattern.\n\n- Arrange: Some of your step functions will build up a certain value in state, or maybe perform certain external\n  initializations as side-effects.\n- Act: Using the current value of state, prepare your input data needed to pass into and / or call the system under test.\n  Typically you would observe the return value and add that to the state as well before returning.\n- Assert: Based on whatever rules in your system about the relationship between inputs into and outputs coming from the\n  system under test, make assertions about the expected output value, compared to the actual output value.\n\n#### Type Hints\nCucumber-JVM provides snippets for you, for every step in the feature file that wasn't matched to one of your step functions.\nThey are not generated by burpless but come from the underlying java code itself.\nBurpless receives the data about unmatched step, including, among other things:\n- the keyword\n- the step expression string\n- the suggested method / function name to use\n- the map of arguments that the function should accept (argument name -\u003e argument type)\n\nBurpless is responsible for taking these inputs and formatting them into a snippet you can use to quickly get started\nimplementing your step functions. Since burpless receives the arguments map, we know what type they will be when\nCucumber-JVM calls your step functions. For your convenience, the step function snippets contain\n[type hints](https://clojure.org/reference/java_interop#typehints) derived from the step argument information provided\nby Cucumber-JVM. Of course, they are optional and you a free to remove them, if you so choose. Just be aware that\nremoving the type hints might negatively impact test execution performance somewhat.\n\n### Update the Step Functions to Pass the Test\nWhile you might be able to come up with something slightly different, here's one possible implementation\nfor the step functions that makes the test pass:\n```clojure\n(ns my-first-feature-test\n  (:require [clojure.test :refer [deftest is]]\n            [burpless :refer [run-cucumber step]])\n  (:import (clojure.lang IObj Keyword)\n           (io.cucumber.datatable DataTable)\n           (java.lang.reflect Type)))\n\n(def steps\n  [(step :Given \"I have a string value of {string} under the {keyword} key in my state\"\n         (fn [state ^String string ^Keyword kw]\n           (assoc state kw string)))\n\n   (step :Given \"I have a long value of {long} under the {keyword} key in my state\"\n         (fn [state ^Long long-value ^Keyword kw]\n           (assoc state kw long-value)))\n\n   (step :Given \"I have a table of the following high and low temperatures:\"\n         (fn [state ^DataTable data-table]\n           (assoc state :highs-and-lows (.asLists data-table ^Type Long))))\n\n   (step :When \"I am ready to check my state\"\n         (fn [state]\n           (assoc state :ready-to-check? true)))\n\n   (step :Then \"my state should be equal to the following Clojure literal:\"\n         (fn [actual-state ^IObj expected-state]\n           (assert (= expected-state actual-state) (str \"Expected State: \" expected-state \"; \"\n                                                        \"Actual State: \" actual-state))))])\n\n(deftest my-first-feature\n  (is (zero? (run-cucumber \"test/my-first.feature\" steps))))\n\n```\n\nRun the tests again:\n```\n$ clojure -T:build test\n\nRunning tests in #{\"test\"}\n\nTesting my-first-feature-test\n\nScenario: Learning to use Burpless                                                     # test/my-first.feature:3\n  Given I have a string value of \"Hello, Burpless!\" under the :message key in my state # my_first_feature_test.clj:9\n  And I have a long value of 5 under the :stars key in my state                        # my_first_feature_test.clj:13\n  And I have a table of the following high and low temperatures:                       # my_first_feature_test.clj:17\n    | 81 | 49 |\n    | 88 | 54 |\n    | 76 | 56 |\n    | 70 | 48 |\n    | 81 | 55 |\n  When I am ready to check my state                                                    # my_first_feature_test.clj:21\n  Then my state should be equal to the following Clojure literal:                      # my_first_feature_test.clj:25\n    \"\"\"edn\n    {:message \"Hello, Burpless!\"\n     :stars 5\n     :highs-and-lows [[81 49] [88 54] [76 56] [70 48] [81 55]]\n     :ready-to-check? true}\n    \"\"\"\n\n1 Scenarios (1 passed)\n5 Steps (5 passed)\n0m0.052s\n\n\n\nRan 1 tests containing 1 assertions.\n0 failures, 0 errors.\n```\n\nHappy Cucumbering!\n\n\n## License\n\n[![License](https://img.shields.io/badge/License-Apache_2.0-yellowgreen.svg)](https://opensource.org/licenses/Apache-2.0)\n\n    Copyright 2025 Daniel Miladinov\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielmiladinov%2Fburpless","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanielmiladinov%2Fburpless","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielmiladinov%2Fburpless/lists"}