Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/FuzzyMonkeyCo/monkey
@FuzzyMonkeyCo's minion
https://github.com/FuzzyMonkeyCo/monkey
api cli-application exploratory-test-monkey generative-testing hacktoberfest http integration-testing model-based-test openapi openapi-validation property-based-testing property-testing swagger test-automation test-runners testing tests unit-testing validations
Last synced: about 2 hours ago
JSON representation
@FuzzyMonkeyCo's minion
- Host: GitHub
- URL: https://github.com/FuzzyMonkeyCo/monkey
- Owner: FuzzyMonkeyCo
- License: agpl-3.0
- Created: 2017-10-30T14:07:47.000Z (about 7 years ago)
- Default Branch: master
- Last Pushed: 2024-11-09T01:09:45.000Z (8 days ago)
- Last Synced: 2024-11-09T01:31:17.127Z (8 days ago)
- Topics: api, cli-application, exploratory-test-monkey, generative-testing, hacktoberfest, http, integration-testing, model-based-test, openapi, openapi-validation, property-based-testing, property-testing, swagger, test-automation, test-runners, testing, tests, unit-testing, validations
- Language: Go
- Homepage: https://fuzzymonkey.co
- Size: 1.24 MB
- Stars: 20
- Watchers: 2
- Forks: 1
- Open Issues: 25
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-technostructure - FuzzyMonkeyCo/monkey
- awesome-technostructure - FuzzyMonkeyCo/monkey
README
# [monkey](https://github.com/FuzzyMonkeyCo/monkey) ~ FuzzyMonkeyCo's minion [![Goreport card](https://goreportcard.com/badge/github.com/FuzzyMonkeyCo/monkey)](https://goreportcard.com/report/github.com/FuzzyMonkeyCo/monkey)
[FuzzyMonkey](https://fuzzymonkey.co) is an automated API testing service that behaves as your users would and minimizes sequences of calls that lead to a violation of your software's properties.
[monkey](https://github.com/FuzzyMonkeyCo/monkey) is the official open source client that executes the tests FuzzyMonkey generates.
[![asciicast](https://asciinema.org/a/171571.png)](https://asciinema.org/a/171571?autoplay=1)
```
monkey M.m.p go1.23.2 linux amd64Usage:
monkey [-vvv] env [VAR ...]
monkey [-vvv] [-f STAR] fmt [-w]
monkey [-vvv] [-f STAR] lint [--show-spec]
monkey [-vvv] [-f STAR] exec (repl | start | reset | stop)
monkey [-vvv] [-f STAR] schema [--validate-against=REF]
monkey [-vvv] [-f STAR] fuzz [--intensity=N] [--seed=SEED]
[--label=KV]...
[--tags=TAGS | --exclude-tags=TAGS]
[--no-shrinking]
[--progress=PROGRESS]
[--time-budget-overall=DURATION]
[--only=REGEX]... [--except=REGEX]...
[--calls-with-input=SCHEMA]... [--calls-without-input=SCHEMA]...
[--calls-with-output=SCHEMA]... [--calls-without-output=SCHEMA]...
monkey [-f STAR] pastseed
monkey [-f STAR] logs [--previous=N]
monkey [-vvv] update
monkey version | --version
monkey help | --help | -hOptions:
-v, -vv, -vvv Debug verbosity level
-f STAR, --file=STAR Name of the fuzzymonkey.star file
version Show the version string
update Ensures monkey is the latest version
--intensity=N The higher the more complex the tests [default: 10]
--time-budget-overall=DURATION Stop testing after DURATION (e.g. '30s' or '5h')
--seed=SEED Use specific parameters for the Random Number Generator
--label=KV Labels that can help classification (format: key=value)
--tags=TAGS Only run checks whose tags match at least one of these (comma separated)
--exclude-tags=TAGS Skip running checks whose tags match at least one of these (comma separated)
--progress=PROGRESS dots, bar, ci (defaults: dots)
--only=REGEX Only test matching calls
--except=REGEX Do not test these calls
--calls-with-input=SCHEMA Test calls which can take schema PTR as input
--calls-without-output=SCHEMA Test calls which never output schema PTR
--validate-against=REF Validate STDIN payload against given schema $ref
--previous=N Select logs from Nth previous run [default: 1]Try:
export FUZZYMONKEY_API_KEY=fm_42
export FUZZYMONKEY_SSL_NO_VERIFY=1
monkey update
monkey -f fm.star exec reset
monkey fuzz --only /pets --calls-without-input=NewPet --seed=$(monkey pastseed)
echo '"kitty"' | monkey schema --validate-against=#/components/schemas/PetKind
```### Getting started
**Recommended way:** using [the GitHub Action](https://github.com/FuzzyMonkeyCo/setup-monkey).
Quick install:
```shell
curl -#fL https://git.io/FuzzyMonkey | BINDIR=/usr/local/bin sh# or the equivalent:
curl -#fL https://raw.githubusercontent.com/FuzzyMonkeyCo/monkey/master/.godownloader.sh | BINDIR=/usr/local/bin sh
```With Docker:
```shell
DOCKER_BUILDKIT=1 docker build -o=/usr/local/bin --platform=local https://github.com/FuzzyMonkeyCo/monkey.git# or the faster:
DOCKER_BUILDKIT=1 docker build -o=/usr/local/bin --platform=local --build-arg PREBUILT=1 https://github.com/FuzzyMonkeyCo/monkey.git
```Or simply install the [latest release](https://github.com/FuzzyMonkeyCo/monkey/releases/latest).
### Configuration
`monkey` uses [Starlark](https://github.com/bazelbuild/starlark) as its configuration language: a simple Python-like deterministic language.
#### Minimal example `fuzzymonkey.star` file
```python
OpenAPIv3(
name = "dev_spec",
file = "openapi/openapi.yaml",
host = "http://localhost:3000",ExecReset = "curl -fsSL -X DELETE http://localhost:3000/api/1/items",
)
```#### Demos
* [demo_erlang_cowboy_simpleREST](https://github.com/FuzzyMonkeyCo/demo_erlang_cowboy_simpleREST)
#### A more involved [`fuzzymonkey.star`](./fuzzymonkey.star)
```python
# Invariants of our APIs expressed in a Python-like languageassert that(monkey.env("TESTING_WHAT", "demo")).is_equal_to("demo")
SPEC = "pkg/runtime/testdata/jsonplaceholder.typicode.comv1.0.0_openapiv3.0.1_spec.yml"
print("Using {}.".format(SPEC))monkey.openapi3(
name = "my_spec",
# Note: references to schemas in `file` are resolved relative to file's location.
file = SPEC,
host = "https://jsonplaceholder.typicode.com",
)# Note: exec commands are executed in shells sharing the same environment variables,
# with `set -e` and `set -o pipefail` flags on.# List here the commands to run so that the service providing "my_spec"
# can be restored to its initial state.
monkey.shell(
name = "example_resetter",# Link to above defined spec.
provides = ["my_spec"],# The following gets executed once per test
# so have these commands complete as fast as possible.
# For best results, tests should start with a clean slate
# so limit filesystem access, usage of $RANDOM and non-reproducibility.
reset = """
echo ${BLA:-42}
BLA=$(( ${BLA:-42} + 1 ))
echo Resetting System Under Test...
""",
)## Add headers to some of the requests
MY_HEADER = "X-Special"
def add_special_headers(ctx):
"""Shows how to modify an HTTP request before it is sent"""req = ctx.request
if type(req) != "http_request":
print("`ctx.request` isn't an HTTP request! It's a {}", type(req))
returnassert that(MY_HEADER.title()).is_equal_to(MY_HEADER)
assert that(dict(req.headers)).does_not_contain_key(MY_HEADER)
req.headers.add(MY_HEADER, "value!")
print("Added an extra header:", MY_HEADER)# Let's also set a bearer token:
token = monkey.env("DEV_API_TOKEN", "dev token is unset!")
req.headers.set("authorization".title(), "Bearer " + token)# Let's edit (a possibly-empty) body
if req.body == None:
req.body = {}
req.body["key"] = 42monkey.check(
name = "adds_special_headers",
before_request = add_special_headers,
tags = ["special_headers"],
)monkey.check(
name = "checks_special_headers",
after_response = lambda ctx: assert that(dict(ctx.request.headers)).contains_key(MY_HEADER),
tags = ["special_headers"],
)## Ensure some general property
def ensure_lowish_response_time(ms):
def responds_in_a_timely_manner(ctx):
assert that(ctx.response).is_of_type("http_response")
assert that(ctx.response.elapsed_ms).is_at_most(ms)return responds_in_a_timely_manner
monkey.check(
name = "responds_in_a_timely_manner",
after_response = ensure_lowish_response_time(1000),
tags = ["timings"],
)## Express stateful properties
def stateful_model_of_posts(ctx):
"""Properties on posts. State collects posts returned by API."""
if type(ctx.request) != "http_request":
return# NOTE: response has already been decoded & validated for us.
url = ctx.request.url
if all([
ctx.request.method == "GET",
"/posts/" in url and url[-1] in "1234567890", # /posts/{post_id}
ctx.response.status_code in range(200, 299),
]):
post_id = url.split("/")[-1]
post = ctx.response.body# Ensure post ID in response matches ID in URL (an API contract):
assert that(str(int(post["id"]))).is_equal_to(post_id)# Verify that retrieved post matches local model
if post_id in ctx.state:
assert that(post).is_equal_to(ctx.state[post_id])return
if all([
ctx.request.method == "GET",
url.endswith("/posts"),
ctx.response.status_code == 200,
]):
# Store posts in state
for post in ctx.response.body:
post_id = str(int(post["id"]))
ctx.state[post_id] = post
print("State contains {} posts".format(len(ctx.state)))monkey.check(
name = "some_props",
after_response = stateful_model_of_posts,
)## Encapsulation: ensure each monkey.check owns its own ctx.state.
def encapsulation_1_of_2(ctx):
"""Show that state is not shared with encapsulation_2_of_2"""
assert that(ctx.state).is_empty()monkey.check(
name = "encapsulation_1_of_2",
after_response = encapsulation_1_of_2,
tags = ["encapsulation"],
)monkey.check(
name = "encapsulation_2_of_2",
after_response = lambda ctx: None,
state = {"data": 42},
tags = ["encapsulation"],
)## A test that always fails
def this_always_fails(ctx):
assert that(ctx).is_none()monkey.check(
name = "always_fails",
after_response = this_always_fails,
tags = ["failing"],
)
```### Issues?
Report bugs [on the project page](https://github.com/FuzzyMonkeyCo/monkey/issues) or [contact us](mailto:[email protected]).
## License
See [LICENSE](./LICENSE)