びぼーろくっ!

誰かに見せるわけでもないけど、備忘録として。。

はじめてつかったGitのリベースとスカッシュ

社内で新しいGitの運用方法が採用されたので
忘れないようにやり方をメモリます。

今回はGitExtensionを使ったやり方を記述しています。

リベースとスカッシュの手順

経緯:masterブランチやdevelopブランチからトピックブランチを作成して
developやmasterにコミットしていく方法で運用していたのですが
複数回コミットを行った際に履歴が訳分からなくなるというのが今回の発端でした。


まず、こんな感じのブランチがあって、ここでトピックブランチを作成します。

f:id:kinachan0725:20171114175318p:plain

適当に2回コミットしました。

f:id:kinachan0725:20171114175337p:plain

これらのCSSファイル、JSファイルの追加を1コミットにまとめたい。。
コマンドラインで下記のコマンドを実施します。

git rebase -i HEAD~x

※xはまとめるコミットの数だけ指定します。
今回は2コミット分をまとめるので、git rebase HEAD~2とします。

なんかファイルが出てきました。

f:id:kinachan0725:20171114175445p:plain

これを編集していきます。

内容を編集するためのテキストが開くので、まとめたいコミットがまとまるように書き換えます。
やり方の例としては最初のコミットをpickのままにして、
他のコミットをpick→squash(s)にすることでコミットが一つにまとまります。

そのため、編集箇所は2行目の「pick」を「s」に変更したらOKですね。
保存して閉じます。

閉じたらまたファイルが出てきました。

f:id:kinachan0725:20171114175457p:plain

これはコミットの際に表示するメッセージですね。
今回はCSS&JSファイルを追加と変更します。

f:id:kinachan0725:20171114175507p:plain

保存して閉じてF5を押すととメッセージが変更されて、コミット内容が1つになっているのが分かります。

これが「スカッシュ」の手順となります。



今度はリベースのテストです。
マスターブランチから新しくmenu.jsを追加したとします。
そのため、TOPIC_1のブランチはmenu.jsを取り込んでいない状態となります。
GitExtensionではこのような画面になります。

f:id:kinachan0725:20171114175514p:plain

これを最新のブランチが分岐元になるように最新を取得します。
TOPIC_1のブランチで下記のコマンドを実行します。

git rebase '元となるブランチ'

今回はマスターブランチの最新から分岐させたいので、git rebase masterと打ちます。

f:id:kinachan0725:20171114175542p:plain

うまくいったようです。
F5を押して更新を掛けてみます。

f:id:kinachan0725:20171114175553p:plain

成功ですね!やった!
最新のメニューが追加された状態でマージを下ということになりますね。
もし、この際に競合が発生していたら解決をするという流れです。

そして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' }

mochaの例外テストを組みました。

mocha + power-assertのExceptionテストを実装しました。
前回の記事などでmochaやpower-assertは説明しているので省略します。

//test.js


describe('1件成功+1件例外のテスト', () => {
  before('setup',(done) => {
    target= new Target(); //Test対象メソッドのコンストラクタ作成
    done();
  });
  it('success',(done) => {
    const val = 10;
    //valが数値の為、正常に終了
    const result = target.ValueTwoTimes(val);
    assert(val*2 === result);
    done();
  });

  // 共通クラスの中にAssertExceptionを作成しました。
  // 第一引数にテストメソッド(bindで紐付ける)、
  //第二引数に期待値(エラーオブジェクト)を渡してテストを実行します。
  it('exception', (done) => {
    const val = 'あ';
    let error = new Error('valueは数値でなければなりません!');
    testCommon.AssertException(async.ValueTwoTimes.bind(this,val),error);
    done();
  });
});
import assert from 'power-assert';

export class TestCommon {
 //例外テストを実行し、検証をします。
  AssertException = (callBackFunc,expectedError) => {
    const result = this.exceptionMethodRun(callBackFunc);
    if (result === undefined) assert(result === undefined,'例外処理未発生の為エラー');

    assert(expectedError.message === result.message,'例外処理メッセージ不一致');
  }

 //例外テストを実行しExceptionを返します。
  exceptionMethodRun = (callBackFunc) => {
    let error = undefined;
    try {
      callBackFunc();
    } catch (exception) {
      error = exception;
    }
    return error;
  }
}

const testCommon = new TestCommon();
module.exports = testCommon;
//テスト対象メソッド
export default class Target {
  ValueTwoTimes = (value) => {
    if (typeof value !== 'number') {
      throw new Error('valueは数値でなければなりません!');
    }
    const number = parseInt(value, 10);
    return number * 2;
  }
}
module.exports = Target;

原理は簡単でcallBackで渡されたテスト実行メソッドをtry~catchの中で実行しているだけです。
Exceptionのテストでいちいち記述量が増えてしまうと大変なので作りました。

テストカバレッジを入れました。

テストカバレッジを入れました。

テストカバレッジのnycをmochaに入れました。
mochaでテストの網羅率を調べたいという際に便利です。
使い方やインストール方法はこちら↓

Using Istanbul With Mocha

//テスト対象メソッド
const red = 1;
const blue = 2;
const green = 3;
const black = 4;

exports.isNull = (val) => {
  return val === null;
}

exports.colorString = (color) => {
  const result = {};
  if (color === red) {
    result.color = 'red';
  }

  if (color === blue) {
    result.color = 'blue';
  }

  if (color === green) {
     result.color = 'green';
  }

  if (color === black) {
     result.color = 'black';
  }
  return result;
};
//テストコード
import mocha from 'mocha';
import assert from 'power-assert';

function requireColor(colorCode) {
  const color = require('../testTarget/color');
  const colorStr = color.colorString(colorCode);
  return colorStr;
}


describe('color.js', () => {
  it('red', () => { // テストケース
    const colorString = requireColor(1); // test対象メソッド
    assert('red' === colorString.color); // 検証
  });
  it('blue', () => {
    const colorString = requireColor(2);
    assert('blue' === colorString.color);
  });
  it('green', () => {
    const colorString = requireColor(3);
    assert('green' === colorString.color);
  });
});

こんなソースがあったとしましょう。
その場合、nycを実行した場合このような結果が表示されました。

f:id:kinachan0725:20170726172920p:plain
(必要のないテストはマスクかけました。)

各画面の見方です。

Stmts : プログラムの各ステートメントは実行されましたか?(調べたら命令網羅と同義でした)
Branch : ifやcaseなどの全ての分岐の処理が実行されたか否か?
Funcs:各関数が呼び出されたかの網羅率
Line : ソースファイルの各実行可能行が実行されたか
Uncovered Line: Lineの対象を示す行番号

stackoverflowの文言を自分なりに翻訳しました。
間違っていたら教えてください・・・

testing - How do I read an Istanbul Coverage Report? - Stack Overflow

まずは分かり易いところから
Funcsが50%になっているのはisNull未実行&colorString実行の為です。
各関数が全てテスト実行されれば100%になります。

Uncovered Line・・全テストの中で処理が走っていない部分を記述されます。
下記2行が走っていない為ですね。

  return val === null; //8行目
  result.color = 'black'; //23行目

Branch・・各ifやcaseなどの分岐の処理が走っているか

    if (color === back)

この処理が走っていない為でしょう。

Stmtsは置いといてisNullの関数を削除した場合、値はどう変化するでしょうか。
試してみます。

f:id:kinachan0725:20170726173405p:plain

Funcsが100%になり、Lineも88.24⇒93.33、Stmtsも88.24⇒93.33に変化しましたね。
isNullの処理を削除したのでresult.color = 'black'; が23行目から10行目に変化しています。

ただ・・Stmtsの値の変化に関しては理解出来ませんでした。
こんな感じのテストとメソッドを書いてみたのですが・・・

//先ほどのテスト対象のコードに追記しています。
exports.ifCheck = (isHappy, isRich) => {
  let youState = 'You are ';
  if (isHappy) {
    youState += 'Happy';
  }
  if (isRich) {
    if (isHappy) youState += '&';
    youState += 'Rich';
  }
  if (!isHappy && !isRich) {
    youState = 'oh my god...';
  }
  return youState;
};
//同じくテストコードに追記
describe('testCase2', () => {
  it('You are Happy&Rich', () => {
    const result = requireifCheck(true, true);
    assert('You are Happy&Rich' === result);
  });
  it('You are Happy', () => {
    const result = requireifCheck(true, false);
    assert('You are Happy' === result);
  });
  it('oh my god...', () => {
    const result = requireifCheck(false, false);
    assert('oh my god...' === result);
  });
});

f:id:kinachan0725:20170726173614p:plain

んんん~~・・・
なぜStmtsが100になるのだ・・・
(今回の場合だとfalse,trueのパターンが存在しないためBranchが94%になっている)
それぞれ渡された引数がtrue,falseで存在するからかな。
まだまだ検証が必要のようです。

追記:パラメーターは関係なくそのままの命令網羅のようです。

今日は直交表に関して勉強しました。

テストロジックに関して知識がなかったので(他の知識も皆無だがw)
今回テスト計画を立てる際に指標になればと思い勉強しました。
数学はてんで駄目なので、分かり易いサイトを探すのに苦労しました。

テストパターンを効率化!直交表でテスト工数を最低限に! | geechs magazine

このサイトを参考に自分で直交表を作ってみます。
元バンドマンっぽくチケット管理システムを考えてみましょう。

f:id:kinachan0725:20170725165824p:plain

ひどい対バンの組み合わせですね。
誰がこんなイベント行くのでしょうか・・・

何のためには置いといて、これらを入力するWEBアンケートがあったとします。
このWEBアンケートのテストを網羅的にやるとなると。。

男性-当日-j-pop
男性-当日ーメタル
...etc

f:id:kinachan0725:20170725170027p:plain

2×3×4=24通りのテストが必要になります。
こんなのやってられません。帰りたいです。

そのため、頭のいいお偉いさんが直交表ということを考え出しました。
これは統計学に基づいて
「不具合が起こる要因のほとんどは2つまでの要因の組み合わせによるものだ」
という結論になったといわれます。これで90%以上はカバーできるとの事。

ならばこれらを使ってテスト工数を削減して、
効率のいいテストを組み立てていきたいと思います。

それでは先ほどのチケット管理システムの例を使って直交表を組み立てていきます。

f:id:kinachan0725:20170725170052p:plain

まずはこんな感じで並べていきます。
これで男性&支払い方法 or 目当ての組み合わせは網羅されました。

f:id:kinachan0725:20170725170128p:plain

続いて女性を入力していきます。
女性&支払い方法 or 目当ての組み合わせも網羅されました。
あとは支払い方法&目当ての組み合わせで足りない所を埋めていくだけですね。

f:id:kinachan0725:20170725170151p:plain

この表だと偏りがあるか分かり辛いのでソートして、Noを振ります。

f:id:kinachan0725:20170725170215p:plain

全12パターンのうち
男性と女性が6パターンずつ
支払い方法が4パターンずつ
目当てが3パターンずつ
綺麗に網羅されていることが分かりますね。

当然、これらの目当てに
性別:中性とか目当てにブルース・R&Dなども増えていくと総当りだとパターン数が増えていきます。
(中性という性別は存在しませんが。。。)
ただ、これらの直交表を使えば複雑なテストほど工数が減っていくので効果的ですね。