All Projects β†’ amperity β†’ Greenlight

amperity / Greenlight

Licence: other
Clojure integration testing framework

Programming Languages

clojure
4091 projects

Projects that are alternatives of or similar to Greenlight

portman
Port OpenAPI Specs to Postman Collections, inject test suite and run via Newman πŸ‘¨πŸ½β€πŸš€
Stars: ✭ 530 (+356.9%)
Mutual labels:  integration-testing, test-framework
pysys-test
PySys System Test Framework
Stars: ✭ 14 (-87.93%)
Mutual labels:  integration-testing, test-framework
Dredd
Language-agnostic HTTP API Testing Tool
Stars: ✭ 3,770 (+3150%)
Mutual labels:  integration-testing, test-framework
Openmock
Mock all the services. Intuitive YAML DSL for HTTP, gRPC, Kafka, and AMQP mocks.
Stars: ✭ 81 (-30.17%)
Mutual labels:  integration-testing
Testcafe
A Node.js tool to automate end-to-end web testing.
Stars: ✭ 9,176 (+7810.34%)
Mutual labels:  test-framework
Unit Threaded
Advanced unit test framework for D
Stars: ✭ 100 (-13.79%)
Mutual labels:  integration-testing
Frisby
Frisby is a REST API testing framework built on Jest that makes testing API endpoints easy, fast, and fun.
Stars: ✭ 1,484 (+1179.31%)
Mutual labels:  integration-testing
Httparrot
HTTP Request & Response Server. An incomplete clone of http://httpbin.org
Stars: ✭ 79 (-31.9%)
Mutual labels:  integration-testing
Infection
PHP Mutation Testing library
Stars: ✭ 1,575 (+1257.76%)
Mutual labels:  test-framework
Mytested.aspnetcore.mvc
Fluent testing library for ASP.NET Core MVC.
Stars: ✭ 1,358 (+1070.69%)
Mutual labels:  test-framework
Zh Pywinauto Doc
pywinautoζ–‡ζ‘£δΈ­ζ–‡ηΏ»θ―‘
Stars: ✭ 93 (-19.83%)
Mutual labels:  test-framework
Coyote
Environment, operations and runtime-meta testing tool.
Stars: ✭ 84 (-27.59%)
Mutual labels:  integration-testing
Atoum
The modern, simple and intuitive PHP unit testing framework.
Stars: ✭ 1,382 (+1091.38%)
Mutual labels:  test-framework
Cavy
An integration test framework for React Native.
Stars: ✭ 1,239 (+968.1%)
Mutual labels:  integration-testing
Gest
πŸ‘¨β€πŸ’» A sensible GraphQL testing tool - test your GraphQL schema locally and in the cloud
Stars: ✭ 109 (-6.03%)
Mutual labels:  integration-testing
Dockest
Docker + Jest integration testing for Node.js
Stars: ✭ 81 (-30.17%)
Mutual labels:  integration-testing
Automation Arsenal
Curated list of popular Java and Kotlin frameworks, libraries and tools related to software testing, quality assurance and adjacent processes automation.
Stars: ✭ 105 (-9.48%)
Mutual labels:  integration-testing
Godog
Cucumber for golang
Stars: ✭ 1,287 (+1009.48%)
Mutual labels:  integration-testing
Dex Test Parser
Find all test methods in an Android instrumentation APK
Stars: ✭ 87 (-25%)
Mutual labels:  test-framework
Hsac Fitnesse Fixtures
An environment to define and run integration tests. It contains Fitnesse fixture (base) classes and a baseline FitNesse installation.
Stars: ✭ 99 (-14.66%)
Mutual labels:  integration-testing

Greenlight

CircleCI codecov

This library provides an integration testing framework for Clojure. Running a suite of tests against your systems gives you the confidence to greenlight them for promotion to production. The primary goals of this framework are:

  • Steps should be composable to drive down repetition in similar tests.
  • Tests should support parallelization out-of-the-box.
  • Results should be actionable and easy to understand.

Installation

Library releases are published on Clojars. To use the latest version with Leiningen, add the following to your project dependencies:

Clojars Project

Usage

A quick overview of greenlight usage:

(require
  '[greenlight.test :as test :refer [deftest]]
  '[greenlight.step :as step :refer [defstep]]
  '[greenlight.runner :as runner]
  '[clojure.test :refer [is]])

;; Greenlight tests are built from a collection of steps

(defstep math-test
  "A simple test step"
  :title "Simple Math Test"
  :test (fn [_] (is (= 3 (+ 1 2)))))
=> #'user/math-test

(deftest simple-test
  "A simple test of addition"
  (math-test))
=> #'user/simple-test

;; Tests and steps are just data

(simple-test)
=> {:greenlight.test/description "A simple test of addition",
    :greenlight.test/line 14,
    :greenlight.test/ns user,
    :greenlight.test/steps [{:greenlight.step/inputs {},
                             :greenlight.step/name math-test,
                             :greenlight.step/test #<Fn@7bb15aaa user/math_test[fn]>,
                             :greenlight.step/title "Simple Math Test"}],
    :greenlight.test/title "simple-test"}


;; Tests can be ran individually

(test/run-test! {} (simple-test))
=> {:greenlight.test/context {},
    :greenlight.test/description "A simple test of addition",
    :greenlight.test/ended-at #<java.time.Instant@55e7469c 2018-07-01T17:03:29.811Z>,
    :greenlight.test/line 14,
    :greenlight.test/ns user,
    :greenlight.test/outcome :pass,
    :greenlight.test/started-at #<java.time.Instant@224450d6 2018-07-01T17:03:29.808Z>,
    :greenlight.test/steps [{:greenlight.step/cleanup [],
                             :greenlight.step/elapsed 0.002573744,
                             :greenlight.step/inputs {},
                             :greenlight.step/message "1 assertions (1 pass)",
                             :greenlight.step/name math-test,
                             :greenlight.step/outcome :pass,
                             :greenlight.step/reports [{:actual (3),
                                                        :expected 3,
                                                        :message nil,
                                                        :type :pass}],
                             :greenlight.step/test #<Fn@2be25eaa user/math_test[fn]>,
                             :greenlight.step/title "Simple Math Test"}],
    :greenlight.test/title "simple-test"}

;; Or as part of a suite with configurable reporters

(runner/run-tests! (constantly {}) [(simple-test)] {})

; Starting test system...
; Running 1 tests...
;
;  * Testing simple-test
;  | user:124
;  | A simple test of addition
;  |
;  +->> Simple Math Test
;  | 1 assertions (1 pass)
;  | [PASS] (0.000 seconds)
;  |
;  |
;  * PASS (0.001 seconds)
;
;
; Ran 1 tests containing 1 steps with 1 assertions:
; * 1 pass

=> true

Test System

Tests are executed with a system of components. The component lifecycle is managed by the test runner: components are started on test startup and then stopped on test completion.

(require '[com.stuartsierra.component :as component])

(defrecord TestComponent
  []

  component/Lifecycle
  (start [this]
    (println "Starting test component")
    this)

  (stop [this]
    (println "Stopping test component")
    this))

(runner/run-tests!
  (constantly (component/system-map ::component (->TestComponent)))
  [(simple-test)]
  {})

; Starting test system...
; Starting test component
; Running 1 tests...
;
;  * Testing simple-test
;  | user:690
;  | A simple test of addition
;  |
;  +->> Simple Math Test
;  | 1 assertions (1 pass)
;  | [PASS] (0.000 seconds)
;  |
;  |
;  * PASS (0.001 seconds)
;
;
; Ran 1 tests containing 1 steps with 1 assertions:
; * 1 pass
;
; Stopping test component
=> true

If you wish to hook into another system library such as integrant, use the ManagedSystem protocol provider in the runner namespace, which supports extension via metadata:

(extend-protocol runner/ManagedSystem
  java.util.Map
  (start-system [this] (integrant/init this))
  (stop-system [this] (integrant/halt! this)))

(runner/run-tests! (constantly {:some-system-map :with-components})
                   tests {})

;;;Alternatively:

(runner/run-tests! (constantly
                     (with-meta {:some-system-map :with-components}
                       {`runner/start-system (fn [this] (println "Starting test system...") this)
                        `runner/stop-system (fn [this] (println "Stopping test system...") nil)}))
                   tests {})

Step Inputs and Outputs

Test steps support parameterization through their inputs. Inputs can be statically configured, pull components from the test system, or pull values built up from previous steps in the test context.

Steps can additionally have outputs. These outputs are registered in the test context and passed to subsequent steps. Outputs can be registered under a key, a collection of keys, or as a function of the previous context.

(defstep step-with-output-keyword
  "A step demonstrating a keyword output"
  :title "Keyword output"
  :test (constantly 1)
  :output :foo)

(defstep step-with-collection-output
  "A step demonstrating a nested output"
  :title "Nested output"
  :test (constantly 2)
  :output [:a :b :c])

(defstep step-with-inputs
  "A step demonstrating different input types"
  :title "Step with inputs"
  :inputs {;; For defaulting values
           :a 1
           ;; Extracting from test system
           :b (step/component ::component)
           ;; Extracting from test context
           :c (step/lookup :foo)
           ;; Context lookups can be a collection
           :d (step/lookup [:a :b :c])
           ;; Or for ultimate flexibility: a function
           :e (step/lookup (fn [ctx] (:bar ctx)))}
  :test (fn [{:keys [a b c d e]}]
          (is (every? some? [a b c d e]))))

(deftest inputs-and-outputs
  "A test demonstrating inputs and outputs"
  (step-with-output-keyword)
  (step-with-collection-output)
  ;; Steps can also be defined inline
  #::step{:title "function output"
          :name 'step-with-function-output
          :test (constantly 3)
          :output (fn [ctx test-output]
                    (assoc ctx :bar (* 2 test-output)))}
  (step-with-inputs
    ;; Override step inputs
    {:a :override-default}
    ;; Override step configuration
    :output :new-output
    :title "New Title"))

(runner/run-tests!
  (constantly (component/system-map ::component (->TestComponent)))
  [(inputs-and-outputs)]
  {})

; Starting test system...
; Starting test component
; Running 1 tests...
;
;  * Testing inputs-and-outputs
;  | user:36
;  | A test demonstrating inputs and outputs
;  |
;  +->> Keyword output
;  | 0 assertions ()
;  | [PASS] (0.000 seconds)
;  |
;  +->> Nested output
;  | 0 assertions ()
;  | [PASS] (0.000 seconds)
;  |
;  +->> Function output
;  | 0 assertions ()
;  | [PASS] (0.000 seconds)
;  |
;  +->> New Title
;  | 1 assertions (1 pass)
;  | [PASS] (0.000 seconds)
;  |
;  |
;  * PASS (0.003 seconds)
;
;
; Ran 1 tests containing 4 steps with 1 assertions:
; * 1 pass
;
; Stopping test component
=> true

Step Cleanup

Often, steps will create some resource that should be removed on test completion. The step/clean! multimethod along with step/register-cleanup! allows for test steps to register resource specific cleanups. These cleanups are performed in reverse order of registration.

(defmethod step/clean! :foo/bar
  [system resource-type k]
  (printf "Removing %s resource %s" resource-type k)
  ;; Actual cleanup using system component
  )

(defstep step-with-cleanup
  "A step demonstrating resource cleanup"
  :title "Step with cleanup"
  :test (fn [_]
          (step/register-cleanup! :foo/bar :my-key)))

(deftest test-with-cleanup
  "A test demonstrating resource cleanup"
  (step-with-cleanup))

(runner/run-tests!
  (constantly {})
  [(test-with-cleanup)]
  {})

; Starting test system...
; Running 1 tests...
;
;  * Testing test-with-cleanup
;  | user:13
;  | A test demonstrating resource cleanup
;  |
;  +->> Step with cleanup
;  | 0 assertions ()
;  | [PASS] (0.000 seconds)
;  |
;  +->> Cleaning :foo/bar resource :my-key
; Removing :foo/bar resource :my-key |
;  * PASS (0.000 seconds)
;
; Ran 1 tests containing 1 steps with 0 assertions:
; * 1 pass
=> true

Test Discovery

Tests can be specified explictly when running tests, or by utilizing runner/find-tests, which retrieves tests based on a matcher. The matcher can be either a keyword to match against test metadata or a regular expression to match test name.

(deftest ^:quick test-1
  ",,,"
  ,,,)

(deftest test-2
  ",,,"
  ,,,)

(runner/find-tests :quick)
=> (#:greenlight.test{:description ",,,", :title "test-1", :ns user, :line 1, :steps []})

(runner/find-tests #"test-2")
=> (#:greenlight.test{:description ",,,", :title "test-2", :ns user, :line 5, :steps []})

Parallel Test Execution

Tests can be executed in parallel by providing a --parallel option with a number of threads. Tests can be further grouped with ::test/group metadata to indicate that tests within the same group should run serially.

If a ::test/group is not provided, a test is placed in its own group. Groups of tests are run in parallel.

(deftest simple1
  (math-test))

(deftest simple2
  {::test/group :sync}
  (math-test))

(deftest simple3
  {::test/group :sync}
  (math-test))

;; Will run `simple1` and `simple2` concurrently, then
;; `simple3` on completion of `simple2`.
(runner/run-tests!
  (constantly {})
  [(simple1)
   (simple2)
   (simple3)]
  {:parallel 2})

Retrying steps

Integration tests can sometimes be slow, or reliant on less stable systems. When running such tests manually, it can be helpful to not have to rerun entire tests from the beginning. You can do this by passing {:on-fail :prompt} to the test runner:

(runner/run-tests!
  (constantly {})
  [(simple1)
   (simple2)
   (simple3)]
  {:on-fail :prompt})

When a step fails, it will now ask if you want to retry the step.

License

Licensed under the Apache License, Version 2.0. See the LICENSE file for rights and restrictions.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].