We all love nothing to do work with a well-documented API. Using the Blueprint or Swagger API standards, you can obtain machine-and-human-readable documentation, and therefore API verification tools based on this documentation.
Apiary offers interactive tools to validate the API manually, substituting the necessary parameters into forms generated from the documentation. But much more benefit can be gained if the API is checked automatically. This eliminates the need to write separate tests for each interface, but imposes certain restrictions on the structure and quality of the documentation itself.
In this tutorial, let's talk about the Dredd utility using the example of the API from GitHub.
I assume that you already have Vagrant and Git locally installed. I work on MacOS, all the commands below were executed in it.
Prepare the necessary accounts and permissions.
Register and create a new project in the service Apiary.
In the created project, click the Tests> Tutorial tab. Here you need to write the values ​​of the variables apiaryApiName
and apiaryApiKey
, which will be useful later.
In the GitHub settings, create a token to access the API with the following permissions (scopes): gist, user: email . You can read more about available permissions in the GitHub API documentation .
Do not forget to copy the token . It is visible only once.
Create a working directory.
mkdir ~/dredd_test && cd ~/dredd_test && git init
Prepare a virtual environment in order not to litter your OS, as well as to document every step taken.
mkdir vagrant && touch vagrant/Vagrantfile && git add vagrant/
In the root directory, create a .gitignore
file and add exceptions to it:
echo 'vagrant/.vagrant' >> .gitignore && git add .gitignore
Edit the vagrant/Vagrantfile
, add the following configuration there:
Vagrant.require_version ">= 1.5" Vagrant.configure("2") do |config| config.vm.hostname = "dredd.dev" config.vm.provider :virtualbox do |v| v.name = "dredd_test" v.customize [ "modifyvm", :id, "--name", "dredd_test", "--memory", 2048, "--natdnshostresolver1", "on", "--natdnsproxy1", "on", "--cpus", 2, ] end # , # .. Ubuntu/Debian npm config.vm.box = "centos/7" # IP- config.vm.network :private_network, ip: "192.168.99.105" config.ssh.forward_agent = true # , # if ENV['DREDD_GITHUB_TEST_PATH'] config.vm.synced_folder "#{ENV['DREDD_GITHUB_TEST_PATH']}", "/var/dredd_test", :owner=> 'vagrant', :group=> 'vagrant' else # - config.vm.synced_folder "~/dredd_test", "/var/dredd_test", :owner=> 'vagrant', :group=> 'vagrant' end # config.vm.provision :shell, path: "provision.sh" # , # GitHub API , Apiary config.vm.provision "shell" do |s| s.binary = true # Replace Windows line endings with Unix line endings. s.inline = %Q(sudo echo \ "GITHUB_API_TOKEN=#{ENV['GITHUB_API_TOKEN']}\nAPIARY_API_KEY=#{ENV['APIARY_API_KEY']}\nAPIARY_API_NAME=#{ENV['APIARY_API_NAME']}" \ > /etc/environment ) end # , VM config.vm.provision "shell" do |s| s.binary = true # Replace Windows line endings with Unix line endings. s.inline = %Q(cd /var/dredd_test && composer install && composer check) end end
Now you need to prepare a file with a set of instructions for preparing a virtual machine.
touch vagrant/provision.sh && git add vagrant/provision.sh
Put the text in it:
#!/usr/bin/env bash yum -y clean all # EPEL ( nodejs etc) webtatic ( php) sudo rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm sudo rpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm # npm sudo curl --silent --location https://rpm.nodesource.com/setup_6.x | bash - yum -y update # , # PHP composer, # API Dredd # - Express JS API sudo yum -y install curl git nodejs npm php70w-cli --skip-broken \ && curl -sS https://getcomposer.org/installer | sudo php -- --install-dir=/usr/bin --filename=composer \ && yum -y install gcc-c++ make \ && sudo npm cache clean \ && sudo npm install dredd -g \ && sudo npm install express -g \ && echo "Done"
As you can see, in addition to the desired Dredd, the PHP Composer dependency manager will also be installed. PHP will be useful as an example for writing hooks when testing APIs. You can use other languages for this, for example, the same NodeJs, so as not to breed the "zoo" of technologies. As my main language is PHP, I will show below how to integrate dredd into Composer so as not to memorize call commands.
So, we have a virtual environment ready.
Now it’s time to prepare the API documentation. The following interfaces were used as the experimental animal:
Dredd supports two types of documentation : API Blueprint and Swagger. We’ll use the first one, since this standard is based on Markdown markup and you can read the API documentation in your favorite git-service: Gitlab, Github, Bitbucket etc. In addition, Swagger support is announced for now in beta mode.
In the project root, create the touch github-api.md && git add github-api.md
file touch github-api.md && git add github-api.md
and put the documentation in it.
I will not describe each line in detail, but I will make some explanations about the structure of the listing below.
FORMAT: 1A HOST: https://api.github.com/ # Github API test Just a simple test of GitHub API ## Users Collection [/users] ### OAuth Non-Web authentication flow: load user data [GET /users/technoweenie] + Request JSON (application/json; charset=utf-8) + Headers Authorization: token 12345 + Response 200 (application/json; charset=utf-8) + Headers Server: GitHub.com Date: Fri, 28 Jan 2017 02:30:40 GMT Content-Length: 1248 Status: 200 OK X-RateLimit-Limit: 5000 X-RateLimit-Remaining: 4992 X-RateLimit-Reset: 1485553884 Cache-Control: private, max-age=60, s-maxage=60 Vary: Accept, Authorization, Cookie, X-GitHub-OTP, Accept-Encoding ETag: "123456701aaf50ed0c83ad4123456789" Last-Modified: Fri, 02 Dec 2016 07:32:00 GMT X-OAuth-Scopes: gist, user:email X-GitHub-Media-Type: github.v3; format=json Access-Control-Expose-Headers: ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval Access-Control-Allow-Origin: * Content-Security-Policy: default-src 'none' Strict-Transport-Security: max-age=31536000; includeSubdomains; preload X-Content-Type-Options: nosniff X-Frame-Options: deny X-XSS-Protection: 1; mode=block X-Served-By: q1234567cned3f5c4u15a34csddc1234 X-GitHub-Request-Id: ABCD:EFGH:A5DA75D:2EF637A:12345678 Connection: close + Body { "login": "technoweenie", "id": 21, "avatar_url": "https://avatars.githubusercontent.com/u/21?v=3", "gravatar_id": "", "url": "https://api.github.com/users/technoweenie", "html_url": "https://github.com/technoweenie", "followers_url": "https://api.github.com/users/technoweenie/followers", "following_url": "https://api.github.com/users/technoweenie/following{/other_user}", "gists_url": "https://api.github.com/users/technoweenie/gists{/gist_id}", "starred_url": "https://api.github.com/users/technoweenie/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/technoweenie/subscriptions", "organizations_url": "https://api.github.com/users/technoweenie/orgs", "repos_url": "https://api.github.com/users/technoweenie/repos", "events_url": "https://api.github.com/users/technoweenie/events{/privacy}", "received_events_url": "https://api.github.com/users/technoweenie/received_events", "type": "User", "site_admin": true, "name": "risk danger olson", "company": "GitHub", "blog": "http://techno-weenie.net", "location": "Louisville, CO", "email": "technoweenie@gmail.com", "hireable": null, "bio": ":metal:", "public_repos": 164, "public_gists": 105, "followers": 2328, "following": 17, "created_at": "2008-01-14T04:33:35Z", "updated_at": "2017-01-24T10:45:13Z" } + Schema { "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "properties": { "login": { "type": "string" }, "id": { "type": "number" }, "avatar_url": { "type": ["string", "null"] }, "gravatar_id": { "type": ["string", "null"] }, "url": { "type": ["string", "null"] }, "html_url": { "type": ["string", "null"] }, "followers_url": { "type": ["string", "null"] }, "following_url": { "type": ["string", "null"] }, "gists_url": { "type": ["string", "null"] }, "starred_url": { "type": ["string", "null"] }, "subscriptions_url": { "type": ["string", "null"] }, "organizations_url": { "type": ["string", "null"] }, "repos_url": { "type": ["string", "null"] }, "events_url": { "type": ["string", "null"] }, "received_events_url": { "type": ["string", "null"] }, "type": { "type": ["string", "null"] }, "site_admin": { "type": "boolean" }, "name": { "type": ["string", "null"] }, "company": { "type": ["string", "null"] }, "blog": { "type": ["string", "null"] }, "location": { "type": ["string", "null"] }, "email": { "type": ["string", "null"] }, "hireable": { "type": ["boolean", "null"] }, "bio": { "type": ["string", "null"] }, "public_repos": { "type": ["number", "null"] }, "public_gists": { "type": ["number", "null"] }, "followers": { "type": ["number", "null"] }, "following": { "type": ["number", "null"] }, "created_at": { "type": ["string", "null"] }, "updated_at": { "type": ["string", "null"] } } } ## Gists Collection [/gists] ### Load all Gists [GET /gists] + Request JSON (application/json; charset=utf-8) + Response 200 (application/json; charset=utf-8) + Attributes (array[Gist], optional) ### Creating new Gist [POST] + Request JSON (application/json; charset=utf-8) + Headers Accept: application/json Authorization: token 12345 + Body { "description": "This is a simple test file for gist API check", "public": false, "files": { "file1.md": { "content": "# Hello, world\n- This is just a GitHub API testing result" } } } + Schema { "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "properties": { "files": { "type": "object" }, "description": { "type": ["string"] }, "public": { "type": ["boolean"] } }, "required": [ "files" ] } + Response 201 (application/json; charset=utf-8) + Attributes (Gist) ### Load a Gist [GET /gists/{id}] + Request JSON (application/json; charset=utf-8) + Parameters + id: a123456bcd (string) - The ID of the Gist. + Response 200 (application/json; charset=utf-8) + Attributes (Gist) ### Delete a Gist [DELETE /gists/{id}] + Parameters + id: a123456bcd (string) - The ID of the Gist. + Response 204 # Data Structures ## Gist This is a demo documentation example. Not all fields was included. + id: a123456bcd (string, required) + url: https://api.github.com/gists/{id} (string) + forks_url: https://api.github.com/gists/{id}/forks (string) + commits_url: https://api.github.com/gists/{id}/commits (string) + git_pull_url: https://gist.github.com/{id}.git (string) + public: false (boolean)
The first two lines are needed for parsers, which will understand the standard version and which default host to access, so that you can check the API in an interactive form.
Headings of the 1st, 2nd, ... levels can be used variably. For analyzers it is important that you specify in square brackets, for example [GET /users/technoweenie]
. This text will be used to compose interactive forms, for example in Apiary:
After this construction, the markup analyzer will wait for the details of the expected request and response on this interface. To do this, use the constructs + Request ( )
and + Response http-status-code ( )
The following are nested levels:
Headers
- Examples of headers and their values ​​that can be received or returned.Body
- An example of a request or response body, primarily for people who will read the documentation. Here you can bring a complete copy-n-paste from the real API, removing only sensitive data. For automated testing, it is better to use Schema or Attributes.Schema
- This section is already important for automated analysis and autotesting, but can also be useful for developers. Both Blueprint and Swagger use the json-schema.org standard to describe data types.Parameters
- This is an alternative, simplified way to describe the fields if you just want to quickly jot down simple examples.Attributes
- At this level, Blueprint allows you to refer to a previously created overall structure. In our example, this is a Gist
structure and it is described at the end of the document as a nested level to the Data Structures
header.Thus, in the given example, two different ways of describing the data structures in the body of requests and responses are shown: the “tricked” Schema and the simplified Parameters and Attributes.
After we have saved the document, it can be checked in Apiary and Github for readability and adequacy of use in manual mode.
This step is optional, but here it is present for a more comprehensive approach, when testing is an integral part of the project itself.
Add a new line to the git exception file: echo 'vendor/' >> .gitignore
Create a file composer.json
, as well as a folder for Dredd hooks with a file of future hooks inside.
mkdir hooks && touch composer.json composer.lock hooks/github-api.php && echo '{}' > composer.lock && git add composer.* hooks/github-api.php
In the composer file. json put the project description:
{ "name": "kivagant/github-api-test", "type": "project", "license": "MIT", "description": "Github API test example with dredd and Apiary", "require": { }, "require-dev": { "ddelnano/dredd-hooks-php": "^1.1" }, "scripts": { "check": [ "@test-mock", "@test" ], "test": "dredd --header=\"Authorization: token ${GITHUB_API_TOKEN}\" --config=./dredd-local.yml", "test-mock": "dredd --header=\"Authorization: token ${GITHUB_API_TOKEN}\" --config=./dredd.yml" } }
Here you should pay attention to the following points:
ddelnano/dredd-hooks-php
package has been added. This part of the Dredd project will allow us to intercept various stages of test execution and manipulate data in order, for example, to transfer some execution context between different tests (data obtained in one test pass to another test).test
and test-mock
, both of which will be executed when invoking the composer check
commandcomposer check
, in which we can also call unit tests, code quality checks, and so on.Now we will create two configuration files - for production and for the API layout.
touch dredd.yml dredd-local.yml && git add dredd.yml dredd-local.yml
In the first dredd.yml
file dredd.yml
we write the following parameters, the purpose of which can be read in the documentation :
reporter: apiary #custom: # apiaryApiKey: 12345 # better to use console argument # apiaryApiName: abcdefg #header: ["Authorization: token 12345"] # better to use console argument dry-run: null hookfiles: hooks/*.php # hooks language: vendor/bin/dredd-hooks-php # composer- sandbox: false server-wait: 3 init: false names: false only: [] output: [] sorted: false user: null inline-errors: false details: false method: [] color: true level: info timestamp: false silent: false path: [] hooks-worker-timeout: 5000 hooks-worker-connect-timeout: 1500 hooks-worker-connect-retry: 500 hooks-worker-after-connect-wait: 100 hooks-worker-term-timeout: 5000 hooks-worker-term-retry: 500 hooks-worker-handler-host: localhost hooks-worker-handler-port: 61321 blueprint: github-api.md # API Blueprint endpoint: 'https://api.github.com' #
The same text should be placed in the dredd-local.yml
file, but at the end replace the endpoint
parameter and add the additional server
parameter, like so:
#... dredd.yml, endpoint endpoint: 'http://localhost:3000' # server: node app.js # , "" API
In this version, Dredd will examine the contents of the github-api.md
file and launch all interfaces one by one. But this is not enough. In the documentation, we perform the following operations:
For the first two cases, everything is fine out of the box, but to get and delete a specific Gist, you must pass the identifier of the created resource to the following requests. For this we need the Dredd hooks.
Open the file hooks/github-api.php
and add the following code there:
<?php // use Dredd\Hooks; // github-api.md // , // const DEFAULT_GIST_ID = "a123456bcd"; // , // $scope = [ 'lastGistId' => '' ]; // Gist Hooks::after("Gists Collection > Creating new Gist", function (&$transaction) use (&$scope) { $scope['lastGistId'] = ''; // , if (!isset($transaction->real->body)) { // , $transaction->fail = true; return; } $body = json_decode($transaction->real->body, true); // JSON if (!isset($body['id'])) { // Gist, $transaction->fail = true; return; } $scope['lastGistId'] = $body['id']; // }); // , API , // Gist Hooks::before("Gists Collection > Load a Gist", function (&$transaction) use (&$scope) { $transaction->expected->body = str_replace(DEFAULT_GIST_ID, $scope['lastGistId'], $transaction->expected->body); $transaction->id = str_replace(DEFAULT_GIST_ID, $scope['lastGistId'], $transaction->id); $transaction->request->uri = str_replace(DEFAULT_GIST_ID, $scope['lastGistId'], $transaction->request->uri); $transaction->fullPath = str_replace(DEFAULT_GIST_ID, $scope['lastGistId'], $transaction->fullPath); }); // , API , // Gist Hooks::before("Gists Collection > Delete a Gist", function (&$transaction) use (&$scope) { $transaction->expected->body = str_replace(DEFAULT_GIST_ID, $scope['lastGistId'], $transaction->expected->body); $transaction->id = str_replace(DEFAULT_GIST_ID, $scope['lastGistId'], $transaction->id); $transaction->request->uri = str_replace(DEFAULT_GIST_ID, $scope['lastGistId'], $transaction->request->uri); $transaction->fullPath = str_replace(DEFAULT_GIST_ID, $scope['lastGistId'], $transaction->fullPath); });
Now we have almost everything ready, but for completeness we will add one more step.
Add a new line to the git exception file: echo 'node_modules/' >> .gitignore
Create a future API server file: touch app.js && git add app.js
In this file we add the following content, which will simply simulate the behavior of github. I will not dwell on the details.
var app = require('express')(); app.get('/', function(req, res) { res.json({message: 'Hello World!'}); }); app.get('/users/technoweenie', function(req, res) { res.set( { "Server": "GitHub-Mock.local", "Date": "Fri, 28 Jan 2017 02:31:55 GMT", "Content-Type": "application/json; charset=utf-8", "Content-Length": 1248, "Status": "200 OK", "X-RateLimit-Limit": 5000, "X-RateLimit-Remaining": 4992, "X-RateLimit-Reset": 1485553884, "Cache-Control": "private, max-age=60, s-maxage=60", "Vary": "Accept, Authorization, Cookie, X-GitHub-OTP, Accept-Encoding", "ETag": "\"123456701aaf50ed0c83ad4123456789\"", "Last-Modified": "Fri, 02 Dec 2016 07:32:00 GMT", "X-OAuth-Scopes": "gist, user:email", "X-Accepted-OAuth-Scopes": "", "X-GitHub-Media-Type": "github.v3; format=json", "Access-Control-Expose-Headers": "ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval", "Access-Control-Allow-Origin": "*", "Content-Security-Policy": "default-src 'none'", "Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload", "X-Content-Type-Options": "nosniff", "X-Frame-Options": "deny", "X-XSS-Protection": "1; mode=block", "X-Served-By": "q1234567cned3f5c4u15a34csddc1234", "X-GitHub-Request-Id": "ABCD:EFGH:25DA75D:2EF637A:12345679" } ).json( { "login": "technoweenie", "id": 21, "avatar_url": "https://avatars.githubusercontent.com/u/21?v=3", "gravatar_id": "", "url": "https://api.github.com/users/technoweenie", "html_url": "https://github.com/technoweenie", "followers_url": "https://api.github.com/users/technoweenie/followers", "following_url": "https://api.github.com/users/technoweenie/following{/other_user}", "gists_url": "https://api.github.com/users/technoweenie/gists{/gist_id}", "starred_url": "https://api.github.com/users/technoweenie/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/technoweenie/subscriptions", "organizations_url": "https://api.github.com/users/technoweenie/orgs", "repos_url": "https://api.github.com/users/technoweenie/repos", "events_url": "https://api.github.com/users/technoweenie/events{/privacy}", "received_events_url": "https://api.github.com/users/technoweenie/received_events", "type": "User", "site_admin": true, "name": "risk danger olson", "company": "GitHub", "blog": "http://techno-weenie.net", "location": "Louisville, CO", "email": "technoweenie@gmail.com", "hireable": null, "bio": ":metal:", "public_repos": 164, "public_gists": 105, "followers": 2328, "following": 17, "created_at": "2008-01-14T04:33:35Z", "updated_at": "2017-01-24T10:45:13Z" } ); }); app.get('/gists', function(req, res) { res.set( { "Server": "GitHub-Mock.local", "Date": "Fri, 28 Jan 2017 02:31:55 GMT", "Content-Type": "application/json; charset=utf-8" } ).json( [ { "url": "https://api.github.com/gists/12345678c62ca49gf313ahd156781234", "forks_url": "https://api.github.com/gists/12345678c62ca49gf313ahd156781234/forks", "commits_url": "https://api.github.com/gists/12345678c62ca49gf313ahd156781234/commits", "id": "12345678c62ca49gf313ahd156781234", "git_pull_url": "https://gist.github.com/12345678c62ca49gf313ahd156781234.git", "git_push_url": "https://gist.github.com/12345678c62ca49gf313ahd156781234.git", "html_url": "https://gist.github.com/12345678c62ca49gf313ahd156781234", "files": { "file.html": { "filename": "file.html", "type": "text/html", "language": "HTML", "raw_url": "https://gist.githubusercontent.com/anonymous/12345678c62ca49gf313ahd156781234/raw/1234ac2e64b06898787920a14f85511be/file.html", "size": 45934 } }, "public": true, "created_at": "2017-01-28T01:29:29Z", "updated_at": "2017-01-28T01:29:29Z", "description": "just a test", "comments": 0, "user": null, "comments_url": "https://api.github.com/gists/12345678c62ca49gf313ahd156781234/comments", "truncated": false } ] ); }); app.get('/gists/:id', function(req, res) { res.set( { "Server": "GitHub-Mock.local", "Date": "Fri, 28 Jan 2017 02:31:55 GMT", "Content-Type": "application/json; charset=utf-8" } ).json( { "url": "https://api.github.com/gists/" + req.params.id, "forks_url": "https://api.github.com/gists/" + req.params.id + "/forks", "commits_url": "https://api.github.com/gists/" + req.params.id + "/commits", "id": req.params.id, "git_pull_url": "https://gist.github.com/" + req.params.id + ".git", "git_push_url": "https://gist.github.com/" + req.params.id + ".git", "html_url": "https://gist.github.com/" + req.params.id, "files": { "file.html": { "filename": "file.html", "type": "text/html", "language": "HTML", "raw_url": "https://gist.githubusercontent.com/anonymous/" + req.params.id + "/raw/12345678910abc/file.html", "size": 45934 } }, "public": true, "created_at": "2017-01-28T01:29:29Z", "updated_at": "2017-01-28T01:29:29Z", "description": "just a test", "comments": 0, "user": null, "comments_url": "https://api.github.com/gists/" + req.params.id + "/comments", "truncated": false } ); }); app.post('/gists', function(req, res) { res.status(201).set( { "Server": "GitHub-Mock.local", "Date": "Fri, 28 Jan 2017 02:31:55 GMT", "Content-Type": "application/json; charset=utf-8" } ).json( { "url": "https://api.github.com/gists/d7b4325ac95ca49ef310aed14dfa8b15", "forks_url": "https://api.github.com/gists/d7b4325ac95ca49ef310aed14dfa8b15/forks", "commits_url": "https://api.github.com/gists/d7b4325ac95ca49ef310aed14dfa8b15/commits", "id": "d7b4325ac95ca49ef310aed14dfa8b15", "git_pull_url": "https://gist.github.com/d7b4325ac95ca49ef310aed14dfa8b15.git", "git_push_url": "https://gist.github.com/d7b4325ac95ca49ef310aed14dfa8b15.git", "html_url": "https://gist.github.com/d7b4325ac95ca49ef310aed14dfa8b15", "files": {}, "public": false } ); }); app.delete('/gists/:id', function(req, res) { res.status(204).set( { "Server": "GitHub-Mock.local", "Date": "Fri, 28 Jan 2017 02:31:55 GMT" } ).end(); }); app.listen(3000);
nodejs, :
touch package.json && git add package.json
:
{ "name": "dredd_test", "version": "1.0.0", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "MIT", "private": true, "description": "", "dependencies": { "dredd ": "^2.2.5", "express": "^4.14.0" } }
(, , "private": true, npm package.js npm ERR! code 1. , WARN, .)
cd vagrant/ vagrant box update DREDD_GITHUB_TEST_PATH=~/dredd_test GITHUB_API_TOKEN=<your-token> APIARY_API_KEY=<your-key> APIARY_API_NAME=<your-project-name> vagrant up
«»:
info: Configuration './dredd.yml' found, ignoring other arguments. info: Beginning Dredd testing... info: Found Hookfiles: 0=hooks/github-api.php info: Spawning `vendor/bin/dredd-hooks-php` hooks handler process. info: Hooks handler stdout: Starting server info: Successfully connected to hooks handler. Waiting 0.1s to start testing. pass: GET /users/technoweenie duration: NaNms pass: GET /gists duration: NaNms pass: POST /gists duration: NaNms pass: GET /gists/1f892b1c9fef3f3fc09fc66c11ad2027 duration: NaNms pass: DELETE /gists/1f892b1c9fef3f3fc09fc66c11ad2027 duration: NaNms info: Sending SIGTERM to hooks handler process. complete: 5 passing, 0 failing, 0 errors, 0 skipped, 5 total complete: Tests took 7286ms complete: See results in Apiary at: https://app.apiary.io/githubtest5/tests/run/bcb8184a-2365-4459-b898-20e9bea8a389 > dredd --header="Authorization: token ${GITHUB_API_TOKEN}" --config=./dredd-local.yml info: Configuration './dredd-local.yml' found, ignoring other arguments. info: Starting backend server process with command: node app.js info: Waiting 3 seconds for backend server process to start. info: Beginning Dredd testing... info: Found Hookfiles: 0=hooks/github-api.php info: Spawning `vendor/bin/dredd-hooks-php` hooks handler process. info: Hooks handler stdout: Starting server info: Successfully connected to hooks handler. Waiting 0.1s to start testing. pass: GET /users/technoweenie duration: NaNms pass: GET /gists duration: NaNms pass: POST /gists duration: NaNms pass: GET /gists/d7b4325ac95ca49ef310aed14dfa8b15 duration: NaNms pass: DELETE /gists/d7b4325ac95ca49ef310aed14dfa8b15 duration: NaNms info: Sending SIGTERM to hooks handler process. complete: 5 passing, 0 failing, 0 errors, 0 skipped, 5 total complete: Tests took 2779ms complete: See results in Apiary at: https://app.apiary.io/githubtest5/tests/run/625c2477-7384-48fa-8bb9-25e27af2a908 info: Sending SIGTERM to backend server process. info: Backend server process was killed.
, : , production. gist.github.com , .
GUI.
https://app.apiary.io/githubtest5/tests/run/625c2477-7384-48fa-8bb9-25e27af2a908
- , provision.sh
- ( npm, ). vagrant ssh
, sudo , dredd express. — .
, provision- (provision up ):DREDD_GITHUB_TEST_PATH=~/dredd_test GITHUB_API_TOKEN=<your-token> APIARY_API_KEY=<your-key> APIARY_API_NAME=<your-project-name> vagrant provision
vagrant ssh
, cd /var/dredd_test && composer check
.
.
git add . && git commit -m 'Dredd testing project initial state'
Source: https://habr.com/ru/post/322130/
All Articles