21
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Frisby.js v2.xを使用したWeb APIテスト

Last updated at Posted at 2017-08-25

v2.xでの変更点

Upgrading From Frisby 0.x to 2.x

  • Frisby.jsでJasmine Test Frameworkを隠蔽しない
  • Jasmine Testの実行環境をjasmine-nodeからJestに変更
  • JSON Validatorにjoiを採用
  • JSON Schemaは未サポート(使用したい場合はこちらを参照)

細かいところでは…

環境

パッケージ

Frisby.js
NPM
https://www.npmjs.com/package/frisby
https://docs.frisbyjs.com/

Jest
NPM
https://www.npmjs.com/package/jest
http://facebook.github.io/jest/

環境構築

$ npm install frisby
$ npm install jest

テスト作成

記述

__tests__/*.js
beforeAll(() => {
  frisby.globalSetup({
    // 共通設定
  });
});

describe('Describe', () => {
  it('Test', () => {
    // FrisbySpecでテストを実施する
    return frisby.get('/users/1')
      // FrisbySpecでチェーンして色々準備する…
  });
});

Frisby

メソッド
formData() FormDataを生成する(Multipart用)
create(name) Deplicated
プロパティ
version
utils
Joi joiを参照する

Global setup

メソッド
globalSetup(opts) 上書き
nullを指定すると既定値に戻る
baseUrl(url) globalSetup()後に設定する

opts

{
  request: {
    credentials: 'include',

    headers: {
      // 既定値
      'Content-Type': 'application/json',
      'User-Agent': 'frisby/2.1.2 (+https://github.com/vlucas/frisby)',
    },

    // baseUrl(既定値:undefined)
    baseUrl: undefined,

    // rawBody(既定値:false)
    rawBody: false,

    // inspectOnFailure(既定値:true)
    inspectOnFailure: true,

    // timeout(既定値:5000[ms])
    timeout: 5000,

    // redirect('follow'または'manual' 既定値:'follow')
    redirect: 'follow',
  },
};

FrisbySpec

メソッド
get()
head()
options()
patch()
post()
put()
delete() = del()
fetch()
use()
setup()
timeout()
fromJSON()

FrisbySpecを返却する

Custom Expect Handlers

Define Custom Expect Handlers

メソッド
addExpectHandler(expectName, expectFn)
removeExpectHandler(expectName)
サンプル(HTTPステータスがどれかに一致するかどうか)
const frisby = require('frisby');

beforeAll(() => {
  frisby.addExpectHandler('statuses', (response, statuses) => {
    if (Array.isArray(statuses)) {
      expect(statuses).toContain(response.status);
    } else {
      expect(response.status).toBe(statuses);
    }
  });
});

afterAll(() => {
  // 必要であれば削除する
  frisby.removeExpectHandler('statuses');
});

describe('Custom Expect Handler', () => {
  it('Test', () => {
    return frisby.get('https://www.google.com')
      .expect('statuses', [200, 301])
      .expect('statuses', 200);
  });
});
サンプル(JSON Schemaによるvalidation)
const frisby = require('frisby');
const Validator = require('jsonschema').Validator; // install jsonschema.

beforeAll(() => {
  frisby.addExpectHandler('jsonschema', (response, _path, _schema) => {
    let schema = _schema === undefined ? _path : _schema;
    let path = _schema === undefined ? false : _path;

    frisby.utils.withPath(path, response.json, jsonChunk => {
      let validator = new Validator();
      validator.validate(jsonChunk, schema, { throwError: true });
    });
  });
});

afterAll(() => {
  // 必要であれば削除する
  frisby.removeExpectHandler('jsonschema');
});

describe('Custom Expect Handler', () => {
  it('Test1', () => {
    return frisby.fromJSON(4)
      .expect('jsonschema', { 'type': 'number'});
  });
  it('Test2', () => {
    return frisby.fromJSON([0, 1, 2, 3, 4])
      .expect('jsonschema', '*', { 'type': 'number'});
  });
});

FrisbySpec

  • メソッドはFrisbySpecを返却する(チェーンが可能)
  • getterはuse()を使用する
メソッド
use(fn) チェーン用
setup(opts, replace) 通常はglobalSetup()で設定する
replaceがtrueの場合は上書き
timeout(timeout) getter/setter
getBaseUrl()

HTTP Requests

メソッド
get(url, params)
head(url, params)
options(url, params)
patch(url, params)
post(url, params)
put(url, params)
delete(url, params) = del(url, params)
fetch(url, params = {}, options = {}) node-fetch API用
fromJSON(json) ローカル用(jsonで指定した値がResponseとなる)
HTTP HEADで要求する場合
const frisby = require('frisby');

describe('HTTP method', () => {
  it('HEAD', () => {
    return frisby.fetch('http://qiita.com/', {
      method: 'HEAD',
      compress: false,
    })
      .expect('status', 200);
  });
});
HTTP GET(URLパラメーターあり)で要求する場合
const frisby = require('frisby');

describe('HTTP method', () => {
  it('GET', () => {
    const url = new URL('https://www.google.com/search');
    url.searchParams.append('q', 'http://qiita.com/');
    return frisby.fetch(url.href, {
      method: 'GET',
    }, {
      urlEncode: false,
    })
      .expect('status', 200);
  });
});

Expectations

メソッド
expect(expectName)
expectNot(expectName)

Built-In Expect Handlers

expectName 引数
status statusCode
header header, [headerValue]
json [path], json 指定した値のみを検証
jsonStrict [path], json すべての値を検証
jsonTypes [path], json 指定した型のみを検証
jsonTypesStrict [path], json すべての型を検証
bodyContains value Bodyがテキストの場合

path

  • Arrayの要素は数字または角括弧で指定
  • pathの値が存在しない場合はError
特殊文字
* Array, Object すべての要素が対象 = &
? Array, Object どれかひとつの要素が対象

Inspectors

メソッド
inspectRequest()
inspectRequestHeaders()
inspectResponse()
inspectHeaders()
inspectStatus()
inspectBody()
inspectJSON()
inspectLog() 即時出力

Promise

メソッド
then(fn)
catch(fn)
finally(fn)
done(doneFn) ※最近のトレンドはFrisbySpec(= Promise)を返却する形式
promise() node-fetchのPromiseが返却される

Tips

Form形式の場合

v2.xはJSON形式が前提(jsonパラメーターがなくなった)だが、以下のようにすることでv.0.8.5から多少の変更で送信が可能

application/x-www-form-urlencoded
const frisby = require('frisby');
const qs = require('qs'); // Jestで入る

describe('application/x-www-form-urlencoded', () => {
  it('Test', () => {
    return frisby.post('/login', {
      body: qs.stringify({
        id: 'testy.mctesterpants@example.com',
        password: 'frisbbbbbbbbbby',
      }),
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      }
    })
      .expect('status', 200);
  });
});
multipart/form-data
const frisby = require('frisby');

describe('multipart/form-data', () => {
  it('Test', () => {
    let form = frisby.formData();
    form.append('id', 'testy.mctesterpants@example.com');
    form.append('password', 'frisbbbbbbbbbby');

    return frisby.post('/login', {
      body: form
    })
      .expect('status', 200);
  });
});

Basic認証

user: user, password: passwd
const frisby = require('frisby');
const url = require('url');

it('auth test(2)', () => {
  // use Legacy URL API.
  let myURL = url.parse('https://httpbin.org/basic-auth/user/passwd');
  myURL.auth = 'user:passwd';
  return frisby.get(url.format(myURL))
    .expect('status', 200);
});

バイナリのテスト

PNG解析
const frisby = require('frisby');

it('PNG test', () => {
  return frisby.setup({ request: { rawBody: true } })
    .get('https://httpbin.org/image/png')
    .expect('status', 200)
    .expect('header', 'Content-Type', 'image/png')
    .then(res => {
      let body = res.body;
      expect(body.byteLength).toBeGreaterThan(8);
      expect(String.fromCharCode.apply(null, new Uint8Array(body.slice(1, 4)))).toEqual('PNG');
    });
});

テスト実施

準備

package.json
{
  "scripts": {
    "test": "jest"
  }
}

オプション
Configuring Jest

実行

  1. すべて実行する場合

    $ npm test
  2. ファイルを指定して実行する場合

    $ npm test -- __tests__/xxx.spec.js

※Windows(Git Bash)でファイル名を指定して実行する場合のメモ
$ npm test -- __tests__\\\\xxx.spec.js

21
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?