仕様変更で没になったので・・・
仕様変更で没になったので・・・
せっかく書いたのに悔しいので載せます。
ソースを見れば一目ですが、HTTPでリクエストを飛ばす処理になってます。
時間が無くて動作確認してないです。多分POSTでつまづくかも。。
簡単な解説
DELETEやPUT、PATCHは未記入ですが同じような要領でいけます。
戻りをTモデルにしてあるのでどのクラスでもマッピングしてくれるように書きました。
リクエスト先の戻りはJSONを想定して書いてます。
using System; using System.Collections.Generic; using System.IO; using System.Net.Http; using System.Linq; using System.Runtime.Serialization.Json; using System.Text; using System.Threading.Tasks; using System.Collections; namespace MyNameSpace.Request { public class RequestService { /// <summary> /// GETメソッド /// </summary> /// <param name="data"></param> /// <returns></returns> public async Task<T> Get<T>(string url, Dictionary<string, dynamic> parameters = null) { var queryParams = createQueryParameter(parameters); var http = new HttpClient(); http.DefaultRequestHeaders.Add("contentType", "application/json"); var res = await http.GetAsync(url + queryParams); var body = await res.Content.ReadAsStringAsync(); return serializeBody<T>(body); } /// <summary> /// POST /// </summary> /// <param name="data"></param> /// <returns></returns> public async Task<T> Post<T>(string url, object data) { var http = new HttpClient(); http.DefaultRequestHeaders.Add("contentType", "application/json"); var param = createNameValueCollection(data); var content = new FormUrlEncodedContent(param.ToArray()); var body = ""; try { var res = await http.PostAsync(url, content); body = await res.Content.ReadAsStringAsync(); } catch (Exception e) { var msg = e.Message; } return serializeBody<T>(body); } /// <summary> /// 受け取ったJSONをTモデルにシリアライズする /// </summary> /// <param name="data"></param> /// <returns></returns> private T serializeBody<T>(string body) { using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(body))) { var serializer = new DataContractJsonSerializer(typeof(T)); return (T)serializer.ReadObject(stream); } } /// <summary> /// 送信用のデータをキャストする /// </summary> /// <param name="data"></param> /// <returns></returns> private List<KeyValuePair<string, string>> createNameValueCollection(object data) { var bodies = new List<KeyValuePair<string, string>>(); var type = data.GetType(); foreach (var prop in type.GetProperties()) { var name = prop.Name; if (prop.PropertyType.IsArray) { var values = (IEnumerable)prop.GetValue(data, null); foreach (var arrProp in values) { var arrValue = arrProp.ToString(); bodies.Add(new KeyValuePair<string, string>(name, arrValue)); } } else if (prop.PropertyType.Name == "DateTime") { var value = ((DateTime)prop.GetValue(data, null)).ToShortDateString(); bodies.Add(new KeyValuePair<string, string>(name, value)); } else { var value = prop.GetValue(data, null).ToString(); bodies.Add(new KeyValuePair<string, string>(name, value)); } } return bodies; } /// <summary> /// GET専用・クエリパラメータを作成 /// </summary> /// <param name="data"></param> /// <returns></returns> private string createQueryParameter(Dictionary<string, dynamic> parameters) { if (parameters == null || parameters.Count == 0) return ""; var list = new List<string>(); foreach(var param in parameters) { var builder = new StringBuilder(); builder.Append(param.Key).Append("=").Append(param.Value); list.Add(builder.ToString()); } var arr = list.ToArray(); return "?" + string.Join("&", arr); } } }
javascriptの配列の関数を並べてみました。
殆どMDNからのパクリです。
なんとなくforEachで片付けてしまうところとかもあるし、必要に応じて使い分ければ冗長なコードを書かなくて済むし
なんとなく使いこなせたらかっこいい気がするw
ので、並べてみました。分からなかったらMDNや他のサイトに飛んでください。
足りないものがあれば追記していく予定です。
concat
配列と配列を結合する。
戻り:配列インスタンス
配列以外の値を引数に指定した場合、末端にappendされる。
引数に何も指定してなければ、新しい配列がコピーされる。
const arr = ['a', 'b', 'c', 'ab', 'bc']; const concat = arr.concat(['d', 'e']); // concat: ['a', 'b', 'c', 'ab', 'bc', 'd', 'e'] const concat2 = arr.concat('d'); // concat2: ['a', 'b', 'c', 'ab', 'bc', 'd'] const concat3 = arr.concat(); // concat3: ['a', 'b', 'c', 'ab', 'bc']
every
引数に渡されたcallback関数を実行。
callback内でfalseがl返されたらその時点でループは終了。戻りはfalseとなる。
ループ終了後、falseがなければ戻りはtrueとなる。
const arr = ['a', 'b', 'c', 'ab', 'bc']; const every = arr.every((val) => { return typeof val === 'string'; }); // every: true; const every2 = ['a', 'b', 'c', 'ab', 1, 'bc'].every((val) => { return typeof val === 'string'; }); // every2: false;
filter
引数に渡されたcallback関数を実行。
戻り:条件に一致する配列の要素を返却し、新しい配列を作成する。
const arr = ['a', 'b', 'c', 'ab', 'bc']; const filter = arr.filter((val) => { return val.indexOf('a') !== -1; }); // filter: ['a', 'ab'];
forEach
callBackの関数を順番に実行する。
戻りはvoid;
const arr = ['a', 'b', 'c', 'ab', 'bc']; let forEach = ''; arr.forEach((val, i) => { forEach += val; forEach += (i !== arr.length - 1) ? 'と' : ''; }); // forEach: aとbとcとabとbc
indexOf
配列の要素の中から完全一致するものを探す。
戻りは要素の最初の添字(位置)存在しない場合は-1
const arr = ['a', 'b', 'c', 'ab', 'bc']; const indexOf = arr.indexOf('ab'); // indexOf : 3
join
配列と配列を結合して文字列に返却。
対象の配列が空の場合は空文字を返却
const arr = ['a', 'b', 'c', 'ab', 'bc']; const join = [].join(); // join : '' //空文字 const join2 = [].join(','); // join2 : '' //空文字 const join3 = arr.join(); // join3: a,b,c,ab,bc const join4 = arr.join('と'); // join4: aとbとcとabとbc
lastIndexOf
配列の要素の中から完全一致するものを探す。
戻りは要素の添字(位置)存在しない場合は-1
indexOfとの違いは見つかった最後の添字という点。
const arr = ['a', 'b', 'b', 'c']; const lastIndexOf = arr.lastIndexOf('b'); // lastIndexOf : 2 // ちなみにindexOfでは const indexOf = arr.indexOf('b'); // indexOf : 1;
map
全ての配列要素に対し処理をし、その結果新しい配列の結果を返却する。
forEachだと冗長な処理になりがちだが、これを使用するとスッキリかけることがある。
const numArr = [2, 3, 4, 5]; const map = numArr.map((val) => { return val * 2; }); // map : [4, 6, 8, 10];
pop
配列から最後の要素を取り出し、取り出した要素を戻りとして返却する。
もし配列が空だった場合、戻りはundefinedになる。
const arr = ['japan', 'america', 'rusia']; const pop = arr.pop(); // arr: ['japan', 'america'] // 末端の要素が取り出されて配列数が1減る。 // pop: 'rusia'
push
配列に要素を追加する。戻りは追加後の配列数。
const arr = ['japan', 'america', 'rusia']; const push = arr.push('germany'); // push : 4 // arr: ['japan', 'america', 'rusia', 'germany']
reduce
アキュムレータと配列の各要素に対して(左から右へ)関数を適用し、単一の値にする。
ちょっと理解ができないので、実際に書いてみる。
const numArr = [2, 3, 4, 5]; const reduce = numArr.reduce((preVal, crrVal, index) => { console.log(`${index}: preval:${preVal} crrVal:${crrVal}`); return preVal + crrVal; }); // 1: preval:2 crrVal:3 // 2: preval:5 crrVal:4 // 3: preval:9 crrVal:5 // reduce: 14
まずpreviousValueとcurrentValueを使用して処理を実施して、
その結果がpreValに代入されている。
crrValは前回のcrrValの次の要素・・・。ということでいいのか?
なんとなくわかったような。そうではないような。。。
と思ったけど、こんな使い方なら良さそう。
// 二次元配列を一次元配列にする const associativeArray = [['a', 'b'], ['c', 'd'], ['e', 'f']]; const reduce2 = associativeArray.reduce((preArr, crrArr) => { return preArr.concat(crrArr); }); // reduce2: ['a', 'b', 'c', 'd', 'e', 'f']
reduceRight
reduceとは逆で右から左へと処理の順番が異なる。それ以外は同じ。
const associativeArray = [['a', 'b'], ['c', 'd'], ['e', 'f']]; const reduceRight = associativeArray.reduceRight((preArr, crrArr) => { return preArr.concat(crrArr); }); // reduceRight: ['e', 'f', 'c', 'd', 'a', 'b']
reverse
配列を逆さまにする。最初の配列要素が最後、最後の要素が最初。という感じ。
また、新しい配列インスタンスは作らないので、元の配列の順序も変わります。
const arr = ['a', 'b', 'c', 'ab', 'bc']; const reverseArr = arr.reverse(); // arr: ['bc', 'ab', 'c', 'b', 'a']
shift
配列の最初の要素(0番目)を取り除き、取り除かれたあとの配列の要素の添字は1マイナスになる。
popの反対というイメージ。
const arr = ['japan', 'america', 'rusia']; const shift = arr.shift(); // arr: ['america', 'rusia'] // shift: 'japan'
slice
配列の一部を取り出して新しい配列を作成。
配列のシャローコピーも可能。
const arr = ['japan', 'america', 'rusia', 'germany', 'france']; // begin・・・取り出す位置 // end・・・どこまで取り出すかを決めた位置 // slice(1, 3) は1番目の要素から4番目の要素まで // (添字が 1, 2, の要素) を取り出します。 const slice = arr.slice(1, 3); // slice: ['america', 'rusia'] // arr: ['japan', 'america', 'rusia', 'germany', 'france']; // 元の配列に変更は加えないので、arrは変わることはない。
シャローコピーの実施と確認
const arr = ['japan', 'america', 'rusia', 'germany', 'france']; const slice = arr.slice(); // 配列の要素内の比較 const result = JSON.stringify(arr) === JSON.stringify(slice); console.log(result); // true // きちんと値渡しになっているかを確認 slice[0] = 'finland'; const result2 = JSON.stringify(arr) === JSON.stringify(slice); console.log(result2); // false
sort
配列を並び替えする。
const arr = ['japan', 'america', 'rusia']; arr.sort(); // sortArr: ['america', 'japan', 'rusia'] const numArr = [2, 10, 3, 100]; numArr.sort(); // numArr: [10, 100, 2, 3]
どうやら数字も文字列としてみているような挙動なので、
数字のソートはcallbackで対応する。
// callBackでaとbを比較してあげるだけで簡単にソートできる。 const numArr = [2, 10, 3, 100]; numArr.sort((a, b) => { return a - b; }); // numArr: [2, 3, 10, 100] // a - bをb - aにすると降順になる。
some
引数に渡されたcallback関数を実行。
callback内でtrueがl返されたらその時点でループは終了。戻りはtrueとなる。
ループ終了後、trueがなければ戻りはfalseとなる。
const arr = ['a', 'b', 'c', 'ab', 'bc']; const some = arr.some((val) => { return typeof val === 'number'; }); // some: false; const some2 = arr.some((val) => { return typeof val === 'string'; }); // some2: true;
splice
説明が厄介な関数。
古い要素を取り除きつつ新しい要素を追加することで、
配列の内容を変更します。
とMDNには記載があるが分かりづらいのでソースに落とし込みます。
spliceの引数は3つ
array.splice(index, howMany, [element1][, ..., elementN]);
index: 配列を変化させ始める要素の添字。arr.lengthより大きい場合、arr.lengthと同じ値
値が負数の場合、配列の終端からその値を引いた数が開始位置となる。
howMany: 配列から取り除く要素の数を示す。0の場合は取り除かれない。
引数無しの場合、index以降の全ての要素が取り除かれる。
element1, ...elementN
追加する要素。要素指定がなければ配列から要素を取り除く
戻りは取り除かれた配列。何も取り除かれなかった場合、空配列が返却される。
// 空の配列を返却。 const arr = ['japan', 'america', 'rusia']; const splice = arr.splice(); // splice: [] // arr: ['japan', 'america', 'rusia']
// 2番目以降の配列を全て取り除く const arr = ['japan', 'america', 'rusia']; const splice = arr.splice(1); // splice: ['america', 'rusia'] // arr: ['japan']
// 2番目(index)の位置に0個(howMany)取り除き、germany(elem)を追加する。 const arr = ['japan', 'america', 'rusia']; const splice = arr.splice(1, 0, 'germany'); // splice: [] // 何も取り除かれていない。 // arr: ['japan', 'germany', america', 'rusia']
// 2番目(index)の位置に1個(howMany)取り除く。 const arr = ['japan', 'america', 'rusia']; const splice = arr.splice(1, 1); // splice: ['america'] // arr: ['japan', 'rusia']
// 2番目(index)の位置に1個(howMany)取り除き、germany(elem)を追加する。 const arr = ['japan', 'america', 'rusia']; const splice = arr.splice(1, 1, 'germany'); // splice: ['america'] // arr: ['japan', 'germany', 'rusia']
// 1番目(index)の位置に2個(howMany)取り除き、germany(elem)を追加する。 const arr = ['japan', 'america', 'rusia']; const splice = arr.splice(0, 2, 'germany'); // splice: ['japan', 'america'] // arr: ['germany', 'rusia']
unshift
配列の最初に要素を追加し、新しい配列数を返却します。
const arr = ['japan', 'america', 'rusia']; const unshift = arr.unshift('germany'); // unshift: 4 // arr: ['germany', 'japan', 'america', 'rusia']
モジュール内で呼び出されてるメソッドをスタブ化できるproxyQuireが便利すぎた。
テストを書いて困ったことがあったので、備忘録して書いておきます。
実装側はこんな感じ。
//Goods.js テスト対象 import {readFileSync} from 'fs'; const goodThings = JSON.parse(readFileSync('/rankValues/good')); const sosoThings = JSON.parse(readFileSync('/rankValues/soso')); const badThings = JSON.parse(readFileSync('/rankValues/bad')); export default class Goods { get (status) { let goods; const goodsList = [goodThings, sosoThings, badThings]; goodsList.forEach((key) => { if (status === key) goods = goodsList[key]; }); return goods; } }
こんな感じのモジュールがあって、
無心でテストを書いてたらイケてないことに気付きました。
(途中で気付いたのですが、最後まで無心ならこうなってただろうという想定で書きました)
//BadTest.js イケてなかったテスト import 'mocha'; import assert from 'power-assert'; import {readFileSync} from 'fs'; import Goods from '../../src/Goods'; const goodThings = JSON.parse(readFileSync('/rankValues/good')); const sosoThings = JSON.parse(readFileSync('/rankValues/soso')); const badThings = JSON.parse(readFileSync('/rankValues/bad')); describe('テスト.だめな例', () => { let goods = new Goods(); }); it('Good!', () => { const result = goods.get('good'); assert.deepEqual(result, goodThings); }); it('Soso!', () => { const result = goods.get('soso'); assert.deepEqual(result, sosoThings); }); it('Bad....', () => { const result = goods.get('bad'); assert.deepEqual(result, badThings); }); });
このテストケースの問題点としては、実装されたデータを使用しているという点ですね。
テストケースのデータは定数(enumとか?)以外は
テスト用のデータを使うのがいいですよね。。
例えば
あくまでテストのデータの中身を検証するテストではなくて、get()の実装を見るテストなので・・・
ただし変更が困難なのがconstで定義されたgoodsがclassの外側に出ていること。
これはimportで読み込まれた時点で処理がスタート(この場合はfs.readFileSync()とJSON.parse())してるため
うかつに変えることができません・・・
また、今回のソースとは異なりますが
//hoge.jsのimport文 import Foo from 'foo'; // foo.jsのimport文 import Bar from 'bar';
のように複雑にimportされているテストだと、
hoge.jsを成功させるにはFooとBarの処理を想定してテストを書かなきゃいけません。
これじゃ単体テストの意味がありません・・・。
そこで必要になるのがproxyQuireです。
簡単にいえばimportなどで呼ばれたモジュールやexport以外のモジュールや
関数などをスタブ化出来るというものです。
sinon.jsと組み合わせで実装していきます。
//Test.js import 'mocha'; import assert from 'power-assert'; import proxyQuire from 'proxyquire'; import sinon from 'sinon'; import Goods from '../../src/Goods';; // この3行はテストデータです。実装側のファイルは使用しません(読まない)。 const goodThings = {good: {goods: ['tv','tablet','guitar'],}}; const sosoThings = {soso: {goods: ['fruit','meet','juice'],}}; const badThings = {bad: {goods: ['shit','paper','trash'],}}; function createProxyQuire() { // スタブ化していきます。今回スタブ化するのはfs.readFileSyncのみなので // 3種類の引数が渡されたらテストデータを返すようスタブを作成します。 const stubReadFileSync = sinon.stub(); stubReadFileSync.withArgs('/rankValues/good') .returns(JSON.stringify(goodThings)); stubReadFileSync.withArgs('/rankValues/soso') .returns(JSON.stringify(sosoThings)); stubReadFileSync.withArgs('/rankValues/bad') .returns(JSON.stringify(badThings)); // ProxyQuireを使用します。 // 第一引数にテスト対象のモジュール(相対パス), // 第二引数にスタブ化したいものをkey-value形式でかいていきます。 const proxyQuireAuthService = proxyQuire('../../src/Goods',{ fs:{ readFileSync: stubReadFileSync } }); return proxyQuireAuthService; } describe('proxyQuireを使ったテスト', () => { const Goods = createProxyQuire(); }); it('Good!', () => { const result = Goods.default.prototype.get('good'); assert.deepEqual(result, goodThings); }); it('Soso!', () => { const result = Goods.default.prototype.get('soso'); assert.deepEqual(result, sosoThings); }); it('Bad....', () => { const result = Goods.default.prototype.get('bad'); assert.deepEqual(result, badThings); }); });
本日はこんな感じです。
はじめてつかったGitのリベースとスカッシュ
社内で新しいGitの運用方法が採用されたので
忘れないようにやり方をメモリます。
今回はGitExtensionを使ったやり方を記述しています。
リベースとスカッシュの手順
経緯:masterブランチやdevelopブランチからトピックブランチを作成して
developやmasterにコミットしていく方法で運用していたのですが
複数回コミットを行った際に履歴が訳分からなくなるというのが今回の発端でした。
まず、こんな感じのブランチがあって、ここでトピックブランチを作成します。
適当に2回コミットしました。
これらのCSSファイル、JSファイルの追加を1コミットにまとめたい。。
コマンドラインで下記のコマンドを実施します。
git rebase -i HEAD~x
※xはまとめるコミットの数だけ指定します。
今回は2コミット分をまとめるので、git rebase HEAD~2とします。
なんかファイルが出てきました。
これを編集していきます。
内容を編集するためのテキストが開くので、まとめたいコミットがまとまるように書き換えます。
やり方の例としては最初のコミットをpickのままにして、
他のコミットをpick→squash(s)にすることでコミットが一つにまとまります。
そのため、編集箇所は2行目の「pick」を「s」に変更したらOKですね。
保存して閉じます。
閉じたらまたファイルが出てきました。
これはコミットの際に表示するメッセージですね。
今回はCSS&JSファイルを追加と変更します。
保存して閉じてF5を押すととメッセージが変更されて、コミット内容が1つになっているのが分かります。
これが「スカッシュ」の手順となります。
今度はリベースのテストです。
マスターブランチから新しくmenu.jsを追加したとします。
そのため、TOPIC_1のブランチはmenu.jsを取り込んでいない状態となります。
GitExtensionではこのような画面になります。
これを最新のブランチが分岐元になるように最新を取得します。
TOPIC_1のブランチで下記のコマンドを実行します。
git rebase '元となるブランチ'
今回はマスターブランチの最新から分岐させたいので、git rebase masterと打ちます。
うまくいったようです。
F5を押して更新を掛けてみます。
成功ですね!やった!
最新のメニューが追加された状態でマージを下ということになりますね。
もし、この際に競合が発生していたら解決をするという流れです。
そしてTOPIC_1のブランチをpushすればremote側も反映されるという仕組みです。
JavaScriptのES6で登場した{並括弧}をつかって代入(const {foo} = this)
なんか当たり前に覚えていたけど、それといった文献を見当たらなかったし
そもそも記号の検索って難しいので書きました。
日本語ですら記号の読み方が曖昧だったりするのに更に日本語のサイトがないとね・・・
初心者には厳しい時代になってきました。。
さて、掲題です。
ES6だとこのような記述が多く見られます。
import express from 'express'; import {readFileSync} from 'fs';
これ!なんで命名された変数を囲ってるの!
オブジェクト?オブジェクトなのかっ!?
って最初思って意味不明でした。。
なんとなく直感的に分かったのですが、慣れるまで時間が掛かりました。
以下のような感じがわかりやすいかと思います。
const test = {foo:'foo',bar: 'bar'}; console.log(test); //そのままobjectが出力されます。 //今までのやり方 const foo = test.foo; console.log(foo); const {bar} = test; //testObjectのbarのみを取り出す console.log(bar);
//出力結果 { foo: 'foo', bar: 'bar' } foo bar
こうやって簡単にすると分かり易いですね。
ちなみにこれ、import文でも可能ですし、関数でも可能です。
import {readFileSync} from 'fs'; const data = readFileSync('path');
上記のこの1行はreadFileSyncのみ使用可能にするということですね。
追伸:いい加減Expressのドキュメント覚えないとダメな気がしてきた。。。
何でも出来ちゃう反面、ちゃんと覚えないと効率が悪い。。。
React.jsのコンポーネントのテスト!
今回テストをするにあたってenzymeを導入しました。
(エンザイムってカタカナで書くと健康食品みたい。。)
github.com
実際の実装の仕方は下記の記事が参考になりました。
labotech.dmm.com
(DMMさん!こんなところでもお世話になるなんて!)
今回の記述したコードはこちらです。
//react.component.test.js import React from 'react'; import testCommon from './testCommon'; import mocha from 'mocha'; import sinon from 'sinon'; import assert from 'power-assert'; import mochaSinon from 'mocha-sinon'; import { shallow, mount } from 'enzyme'; import EmailInput from '../testTarget/EmailInput'; describe('EmailInputのテスト', () => { it('propが渡されたときにvalueにセットされること', () => { const wrapper = shallow(<EmailInput />); // ここでpropsをセットする wrapper.setProps({ 'value': 'kinachan0725@example.com' }); const input = wrapper.find('input'); assert.deepEqual(input.node.props.value,'kinachan0725@example.com'); }); it('メールアドレスが入力されたときにonChangeイベントが発火すること', () => { // onChangeメソッドが呼ばれたときの入出力(引数の値や戻り値、呼ばれた回数など)を監視する const onChange = sinon.spy(); // テスト対象のコンポーネントのみをレンダリング const wrapper = shallow(<EmailInput onChange={onChange} />); // 入力イベントを擬似的に再現 wrapper.find('input').simulate( 'change', { target: {value: 'x'} } ); // onChangeメソッドが1回呼ばれているかを確認 assert(onChange.calledOnce === true); }); });
//テスト対象ファイル EmailInput.js import React, { Component } from 'react'; import PropTypes from 'prop-types'; class EmailInput extends Component { render() { return ( <div> <input type="text" name="email" ref={(email) => { this.textInput = email; }} placeholder="メールアドレス" value={this.props.value} onChange={this.props.onChange} /> </div> ); } } //propTypesで型と初期値を宣言 EmailInput.propTypes = { value: PropTypes.string, onChange: PropTypes.func, }; EmailInput.defaultProps = { value: undefined, onChange: undefined, }; export default EmailInput;
とまあ、ほぼコピペです。
ただ、実際に変更したのはこちら。
今のプロジェクトではeslintを導入して、ルールにprop-typesが指定されていない場合エラーになるので
ESLint 最初の一歩 - Qiita
ESLintの導入と警告対応のメモ - Qiita
(公式よりこちらのが分かり易いのでQittaの記事を)
EmailInputにthis.props.valueと指定したら怒られます。
(eslintのルールを変更すればいいんですけど、それは入れた意味がないw)
これはテスト対象の2行目にimportしている
PropTypesで解決します。
要はpropsの中に好き勝手にプロパティを使うのではなく、ちゃんと宣言しろってことですね。
また、テストコードですが、参考URLとは異なりassertにpower-assertを使用しているので組み替えました。
propsの値はinput.node.props.valueで取れるみたいですね。
ちなみにテスト実行時コンソール上で
Warning: Shallow renderer has been moved to react-test-renderer/shallow. Update references to remove this warning.
が表示されましたが、これで解決できそうです。
Support react-dom/test-utils · Issue #875 · airbnb/enzyme · GitHub
とりあえずテストは通るので、来週辺りにこれ解決しようかな。。。
Mocha + Sinon + Nockを使ったテスト
原因が分かるまで大量に時間を要したので忘れないようにメモっておこう。。
Nockはこの記事を参考にしました。
MochaとNockでモックサーバーを作ってレスポンスのテスト | MMMブログ
ポイントはnockのgetを空文字にしたところ
問題なく通信が行われたこと。
これを指定せずにallusersなどに指定したら
実際に通信が行われてnock化されていない事が判明。。
詳しい原因は調べてみないと分からないけど、とにかく動いた。。
//testファイル describe('Nockを使ったテスト', () => { const exsample = 'http://exsample.com/'; let stub; let target; beforeEach('setup', (done) => { target = new Target(); done(); }); afterEach('teardown', (done) => { stub.restore(); target = null; done(); }); //Nockを使ったテスト it('use nock', (done) => { //メソッドをスタブ化 stub = sinon.stub(target, 'getBaseUrlByRequest'); stub.returns(exsample); //Nockを作成 var nockRequest= nock(`${exsample}allusers`, { reqheaders: { 'content-type': 'application/json' } }).get('').reply(200, { _id: 'kinachan0725', _rev: '946B7D1C', username: 'きなちゃん', email: 'kinachan0725@exsample.com' }); let result = target.readAllUsers('allusers', null, 'req') .then((data) => { console.log('data', data); assert(data !== undefined); done(); }, (err) => { console.log('err', err); done(); }); }); });
// テスト対象のファイル export default class Target { readAllUsers= async(url, base = null, req = null) => { let baseUrl; if (base != null) { baseUrl = base; } else if (req != null) { //スタブ化されているので、無条件で全てexsample.comが返却される baseUrl = this.getBaseUrlByRequest(req); } else { baseUrl = typeof window === 'undefined' ? this.BaseUrl : window.location.origin; } //nockで設定したものが返却される const res = await fetch(`${baseUrl}${url}`, { method: 'GET', credentials: 'same-origin', headers: { 'content-type': 'application/json' }, }); const json = await res.json(); return json; } }
//コンソール上の出力結果 data { _id: 'kinachan0725', _rev: '946B7D1C', username: 'きなちゃん', email: 'kinachan0725@exsample.com' }