📜 ⬆️ ⬇️

Testing the Documented API with the Apiary Dredd Utility

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.


image


In this tutorial, let's talk about the Dredd utility using the example of the API from GitHub.


image


Bootstrap


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.


image


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.


image


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.


image


Create a working directory.


mkdir ~/dredd_test && cd ~/dredd_test && git init


Virtual environment


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 / vagrantfile
 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:


vagrant / provision.sh
 #!/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.


Blueprint for GitHub API


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.


github-api.md
 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:


image


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:



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.


Preparing the composer project


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:


composer.json
 { "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:



Configuring dredd


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 :


dredd.yml
 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-local.yml
 #...   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.


Writing Dredd hooks


Open the file hooks/github-api.php and add the following code there:


hooks / github-api.php
 <?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.


Preparing a prototype API using Express JS


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.


app.js
 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


:


package.js
 { "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, .)


Running tests


 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
image


?

- , 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