@ledsun blog

無味の味は佳境に入らざればすなわち知れず

Mac で go

Mac OS Xでgo言語の開発環境を用意します。

goをインストール

brew install 

インストールが成功したか確認してみましょう。

go version

Hello World

hello.goファイルを作成します。

package main

import "fmt"

func main() {
    fmt.Printf("hello, world\n")
}

go runコマンドで実行します。

go run hello.go

go buildコマンドでコンパイルします。

go build hello.go
./hello

パッケージ管理

go getコマンドでパッケージをインストールします。

環境設定が必要です。

mkdir $HOME/go
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin

mercurialを使ってパッケージを取得するにはmercurialコマンドも必要です。

brew install mercurial

例えばgo言語の公式チュートリアルを取得するには

go get code.google.com/p/go-tour/gotour

実行します。

gotour

起動します。

エディタ

ATOMを使います。

go-plusプラグインを入れると何か色々入ります。 さらにscrptプラグインを入れるとcmd + iで実行できます。

goコマンドにPATHが通っている必要があります。ATOM

export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin

したターミナルから起動してください。

それから

A Tour of Goを地道に読めばgoマスター(たぶん)。

忍者式テストをやってみた

忍者式テストを二週間くらいやってみた感想です。

忍者式テストとは

那須のKent Beckと呼ばれる隠者が発明(発見?)したテスト手法。 特徴をあげると

  1. 毎日、手動でテストを行う
  2. テストをペア作業で行う
  3. 実施のたびにテスト項目を見直す
  4. テスト項目の粒度は受け入れテスト

初めてこの手法を知ったのは2005年だったと思います。 当時、自社開発ソフトウェアの結合試験の真っ最中でした。 「バグの発見漏れは減るだろうけど、どうやって導入すれば???」と、 使えそうなのに手が出せなく、もどかしい思いをしました。

導入方法の謎

何年経っても導入方法が分からなかったので、金にものを言わせて本人に聞いてみました*1

「最初からチームで始めた」は誤解でした。 最初は隠者が一人でやっていたそうです。

やってみた

一人プロジェクトで、テストの作戦が決まっていなかったので、やってみました。

前提条件

実施形態

  • 毎日、17時からテスト開始
  • テスト項目はWiki*2で管理
  • その日、追加した機能のテスト項目を追加
  • テストを行いながら
    • 分かりにくい表現を修正
    • 新しい項目を思いついたら追加
    • 一緒に行った方が良いテストをグループ化
    • バグを見つけたテストグループは一番上に移動
    • 面倒くさくてバグが見つからないテストは簡略化

感想

  • 忍者式テストは本当にあった
    • バグの見逃しは本当に減った
    • その日のうちにバグが見つかるのは安心
  • レアなバグが次の週に見つかったりもする
    • テスト実施回数が増えると、レアバグがゲットできるのはTDD(自動テスト?)っぽい
  • テスト項目を一回で完璧にしない、徐々に育てていく感覚が新しい
  • 向いてなさそう
    • 趣味のプログラミング
      • 一回の作業時間が20分ではテストに割く時間がない
      • 早い段階で自動化する方がいい
    • 三ヶ月で作って納品みたいなプロジェクト
      • テスト項目の育て甲斐がなさそう
  • よくわからない
    • 今後、機能が増えても回るのか不安
    • 他の人を巻き込んだときにどうなるのか不安
  • ほんとに千里だった

かあちゃんえらい(自画自賛))

ExtremeProgramingを越えたと言われているチームは、一体どれだけ先に進んでいるんだろうか・・・?

おわりに

隠者の知恵を金で買うチャンスは今もある。みんなもやるといいとおもうよ。

dRubyによる分散・Webプログラミング

*1:http://togetter.com/li/266752 これが2012年の話と言う事実に驚愕している・・・・

*2:https://github.com/pubannotation/textae/wiki/userAcceptanceTest

Gitlabの更新情報をChatworkに通知するアプリケーション、Syamoを作りました

Gitlabの更新情報をChatworkに通知するアプリケーション、Syamoを作りました。

Syamoとは?

Gitlabのプロジェクトが更新されたときにChartworkのチャットルームにメッセージを送ります。

Gitlabのプロジェクトの更新情報は次の三つです。*1

  1. gitブランチへのpush
  2. Issueの更新
  3. MergeReqestの更新

どうやって動かすの?

githubでソースを公開しています。 git cloneしてherokuに配置すれば動きます。

その際以下の三つの情報を設定する必要があります。

  1. Chartwork APIトークン
  2. Gitlab API URL
  3. Gitlab API トークン

これらの情報はherokuに環境変数として設定します。詳細な設定方法はgithubを見てください。

どうやって動いているの?

node.jsで実行するWebアプリケーションです。 Gitlabのwebhookに本アプリケーションのURL登録し、通知情報を取得します。 Gitlabのwebhookに含まれないプロジェクト情報(プロジェクト名とユーザ名)を取得するためにGitlab APIを使います。

命名理由

chabotを参考にしたので、メジャーな鶏の品種で軍鶏にしました。 強そう!

愚痴

  • Gitlab webhookが不便
    • 空ブランチの作成・削除とかcommit0件でもpushなのか・・・
    • issueのアサインが非同期更新で、セレクトボックスの値を変えるたびに通知が・・・
    • issueをopen/closeするたびに二回ずつ通知が・・・
    • MergeRequestは最初にマージ不可で通知して、チェック終了後にマージ可と通知するのではなく、 チェックが終わってから一回だけ通知してほしい・・・
  • chatworkの記法表現力なさ過ぎ
    • 文字色を指定したい。issueを赤とか色分けしたいです。
    • [info]とか[hr]とかブロック要素に展開される記法直後の改行は無視して欲しい。 [hr]ほげほげとテンプレートに書くのが嫌です。

悩み

frisby.jsでテスト書いたのはいいけど、テストに本物のChatworkとGilabを使っているおかげで自分の秘密情報が無いとテストが通らない。 モックを使うべきなのか、ユニットテストでカバーすべきなのか?

*1:タグプッシュには対応してません。テンプレート足せば動くと思うけど試していません。

Jenkinsでherokuに配置

目標

Jenkisを使ってherokuへの配置を自動化します。

前提

  1. JenkinsはCloudbeesを使う*1
  2. Jenkinsのコードの取得は既に出来ている*2

準備

herokuのアカウントページSSH KeysにCloudBees Public Keyを登録します。

配置

要はシェルでgit push herokuします。 ただしJenkinsはソースコードを特殊なブランチにcheckoutします。pushするブランチを明示的に作成します。

【Jenkins】JenkinsでHerokuにデプロイすると「Everything up-to-date」と表示される - 量子的ぷろぐらま を参考にし、次のスクリプトを設定します。

git checkout -b temp
git push git@heroku.com:myapp.git temp:master
git checkout master
git branch -D temp

git pushのリモートブランチにはgitリポジトリを直接指定出来ます。

*1:Cloudebeesの設定方法はCloudbeesを使ってJenkinsを用意するを見てください。

*2:ここまでの設定はGitlabの更新を契機にJenkinsでnode.jsアプリケーションをテストを見てください。

Gitlabの更新を契機にJenkinsでnode.jsアプリケーションをテスト

はじめに

前提条件

  1. ソース管理はgitlab
  2. JenkinsはCloudbeesを使う*1
  3. テストはfresby.jsで書いてる

目標

gitlabでトピックブランチをmasterにマージしたら自動的にテストする

gitlabの設定

秘密鍵

Jenkinsがgit cloneできるように秘密鍵を登録します。

  1. Jenkinsで新規プロジェクトを作成
  2. (Cloudbeesの場合)Jenkinsの秘密鍵が用意されている
  3. gitlabの自分のアカウントに追加

pullするだけなので、自分のアカウントをそのまま使います。 複数人で開発するプロジェクトでは、Jenkins用ユーザを用意した方がいいかもしれません。

webhook

  1. gitlabのプロジェクトのSettingsのHooksを設定
  2. Push eventsイベントを選択
  3. 次のフォーマットのURLを登録
http://< jenkins host >/git/notifyCommit?url=git@< gitlab host >:< repository owner >/< repository name >.git

例えば

http://ledsun.ci.cloudbees.com/git/notifyCommit?url=git@gitlab.com:ledsun/sampleproject.git

参考:YUKASHIKADO Inc. 開発ブログ • Jenkins と GitLab と rbenvでプライベートなRuby開発環境を構築する

Jenkinsの設定

plugin

gitlabと連携するためのプラグインをインストールします。

  1. Jenkins > Jenkinsの管理 > プラグインの管理
  2. Gitlab Hook Pluginをインストール

Jenkinsのプロジェクト設定

Jenkinsのダッシュボードからプロジェクトを作成し設定します。

ソースコード管理

  1. git を選ぶ
  2. git urlを指定。例えばgit@gitlab.com:ledsun/sampleproject.git
  3. masterブランチを設定

ブランチを指定すると、masterにマージしたときだけビルドを実行します。 指定しないと、トピックブランチをプッシュした時もビルドが実行します。

ビルド・トリガ

  1. SCMをポーリングにチェックを入れる

ビルド

シェルの実行に次のスクリプトを指定します。

curl -s -o use-node https://repository-cloudbees.forge.cloudbees.com/distributions/ci-addons/node/use-node
NODE_VERSION=0.10.26 . ./use-node
npm install
npm install jasmine-node@1.14.2
npm install forever
export PATH=$PATH:node_modules/jasmine-node/bin/:node_modules/forever/bin
forever start --no-colors app.js
sleep 2s
jasmine-node spec/dev/ --junitreport
forever stop --no-colors app.js

注意点

  1. Cloudbeesでnodeを使うにはおまじないが必要
  2. nodeコマンドはローカルにイスントールしてパスを通す
  3. jasmine-nodeでfresby.jsのテストを実行するときはバージョン1.14.2を指定する
  4. テスト終了後にプロセスをnodeアプリケーション終了するためにforeverを使う
  5. nodeアプリケーションの起動に時間がかかるのでsleepで待つ

nodeを動かす

CloudBees DEV@cloud (Jenkins as a Service) Documentation のサンプルを参考にします。 サンプルはnode.jsのバージョンが古いのでなるべく最新の10.26を指定します。

curl -s -o use-node https://repository-cloudbees.forge.cloudbees.com/distributions/ci-addons/node/use-node
NODE_VERSION=0.10.26 . ./use-node

これでnpmコマンドも入ります。

nodeコマンドをインストール

Cloudbeesでは、nodeコマンドはHow do I get a grunt task working with a Cloudbees Jenkins build - Stack Overflow のようにローカルにインストールしてPATHを設定して使います。

  • forever
  • jasmine-node

をインストールします。

npm install jasmine-node@1.14.2
npm install forever
export PATH=$PATH:node_modules/jasmine-node/bin/:node_modules/forever/bin

nodeアプリケーションの開始

終了しなくても一度の実行ではテストが成功します。 何度か実行すると失敗します。

Jenkinsでnodeのアドオンをビルドして、それを使ったhttpサーバアプリケーションを起動する - 猫ぱーんち!を参考にし foreverを使って明示的に起動・終了します。

Jenkinsのコンソールはカラー表示に対応していないので--no-colorオプションをつけます。

forever start --no-colors app.js

foreverを使うとアプリケーションの起動完了前に次のコマンドが実行されます。 起動完了を待つためにスリープを入れます。

sleep 2s

テストの実行

frisby.jsのテストはjasmine-nodeコマンドで実行します。 jasmine-nodeの最新版(1.14.3)は上手く動きません。1つ前のバージョン1.14.2を使います。

また、Jekinsでテスト結果を集計するために--junitreportオプションをつけます*2

jasmine-node spec/dev/ --junitreport

nodeアプリケーションの終了

forever stop --no-colors app.js

ビルド後の処理

Junitテスト結果の集計にreports/*.xmlを指定します。 Jenkinsのダッシュボードに成功・失敗したテスト件数が表示されるようになります。

確認

Jenkinsの設定が上手く言っているか確認するために、ビルドの実行をしてみましょう。

20140718追記

jasmine-nodeの1.14.5がリリースされ、バージョン指定が不要になりました。

*1:Cloudebeesの設定方法はCloudbeesを使ってJenkinsを用意するを見てください。

*2:2014年7月8日時点では、--junitreportオプションを付けるとテストが正しく実行されません。1.14.4で解消されるそうです

Cloudbeesを使ってJenkinsを用意する

Jenkinsを使うだけならサーバーもインストール不要なPaasを使うのが便利です。 今回はCloudBeesを使います。*1

Cloudbeesの用意

  1. CloudBeesへSign Up
  2. githubアカウントでログイン*2

Jenkinsの用意

  1. Buildsボタンを押す
  2. 10分待て言われるので待つ

しばらくしてBuildsボタンを押すとJenkinsのダッシュボードが表示されます。

*1:OpenShift Onlineを使うのもいいと思います。

*2:GoogleアカウントでもOKです。

component.js(1.0.0-rc5)を試す

component.js

component.jsはnpmやbowerを使った依存する外部ライブラリ(JavaScript,CSSともを含む)の解決と、 unglifyやcssminで行う自作ファイルを適切に組み合わせるビルドを1つのツールでやってしまおうとする 意欲的なツールです。

Getting Started

使用感はGetting Startedを眺めてもらうのが 分かりやすいです*1

流れ

component jsを試してみた。 - 日頃の行いと大体一緒です。 たまたまバグを踏んだので、その部分を補足します。

  1. npmでinstall
  2. githubの設定
  3. index.html,css,jsとcomponent.jsonの記述
  4. component buildの実行

install

npm install -g component@1.0.0-rc5

1.0.0はstableではありません。バージョンを指定しないと0.19.9が入ります。 結構大きいです。

github APIの設定

component1.0.0github APIを使って依存ライブラリを取得します。 github APIの設定が必要です。

https://github.com/settings/applications#personal-access-tokens にてアクセストークンを作成します。 権限はrepoとuserだけで動きました。もっと減らせるかもしれません。

アクセストークンの動作確認

curl -u <token>:x-oauth-basic https://api.github.com/user

.netrcにトークンを追記

machine api.github.com
  login <token>
  password x-oauth-basic

ファイルを準備

index.html

<!DOCTYPE html>
<html>
  <head>
    <title>Getting Started with Component</title>
    <link rel="stylesheet" href="build/build.css">
  </head>
  <body>
    <h1>Getting Started with Component</h1>
    <p class="blink">Woo!</p>
    <script src="build/build.js"></script>
  </body>
</html>

index.js

var blink = document.querySelector('.blink');

setInterval(function () {
  blink.style.visibility = getComputedStyle(blink).visibility === 'hidden'
    ? 'visible'
    : 'hidden';
}, 30

index.css

* {
  box-sizing: border-box;
}

component.json

{
  "name": "getting-started-with-component",
  "dependencies": {
    "necolas/normalize.css": "^3.0.0"
  },
  "scripts": ["index.js"],
  "styles": ["index.css"]
}

実行

component buildコマンドで依存ライブラリの取得とビルドが走るはずですが・・・

component build
   installed : necolas/normalize.css@3.0.1 in 1148ms
       build : resolved in 4903ms
       build : files in 7ms
       build : build/build.js in 9ms - 1kb

       error : Cannot call method 'process' of undefined

期待しないエラーが表示されます*2。 しかしエラーの詳細が分かりません。

こういう場合は、component guideにあたります。 Trouble shooting に従いDEBUG変数を指定して再度実行します。

DEBUG=component* component build
  component-resolver remote not set - defaulting to remotes's defaults +0ms
  component-resolver:locals resolving local at "/Users/ledsun/SandBox/JavaScript/component" +6ms
  component-resolver resolving "getting-started-with-component" +4ms
  component-resolver remaining dependencies: 1 +4ms
  component-resolver remaining semver: 0 +0ms
  component-resolver finished resolving locals +0ms
  component-resolver finished resolving dependencies (1) +0ms
  component-resolver:semver resolving semver necolas/normalize.css@^3.0.0 +1ms
  component-resolver:dependencies resolving dependency necolas/normalize.css@3.0.1 +2ms
  component-resolver:dependencies searching ["local","github","bitbucket"] for necolas/normalize.css@3.0.1 +1ms
  component-resolver:dependencies found necolas/normalize.css@3.0.1 from remote "local" +1ms
  component-resolver resolving "necolas/normalize.css" +0ms
  component-resolver remaining dependencies: 0 +0ms
  component-resolver remaining semver: 1 +1ms
  component-resolver:semver resolved semver necolas/normalize.css@^3.0.0 -> necolas/normalize.css@3.0.1 +0ms
  component-resolver finished resolving semver +0ms
  component-resolver finished resolving dependencies(2) +1ms
  component-downloader "/Users/shigerunakajima/SandBox/JavaScript/component/components/necolas/normalize.css/3.0.1" exists, skipping downloading. +0ms
  component-resolver finished installing dependencies +0ms
       build : resolved in 22ms
       build : files in 6ms
       build : build/build.js in 9ms - 1kb
  component-consoler TypeError: Cannot call method 'process' of undefined
    at /usr/local/lib/node_modules/component/node_modules/component-build/node_modules/builder-autoprefixer/index.js:21:49
    at Object.read (/usr/local/lib/node_modules/component/node_modules/component-build/node_modules/component-builder/node_modules/component-manifest/build/index.js:436:49)
    at Styles.autoprefixer (/usr/local/lib/node_modules/component/node_modules/component-build/node_modules/builder-autoprefixer/index.js:16:10)
    at next (/usr/local/lib/node_modules/component/node_modules/co/index.js:99:21)
    at /usr/local/lib/node_modules/component/node_modules/co/index.js:102:18
    at /usr/local/lib/node_modules/component/node_modules/component-build/node_modules/component-builder/build/plugins/url-rewriter.js:438:7
    at /usr/local/lib/node_modules/component/node_modules/component-build/node_modules/component-builder/node_modules/component-manifest/build/index.js:446:7
    at fs.js:266:14
    at /usr/local/lib/node_modules/component/node_modules/component-build/node_modules/component-builder/node_modules/graceful-fs/graceful-fs.js:104:5
    at /usr/local/lib/node_modules/component/node_modules/component-resolver/node_modules/graceful-fs/graceful-fs.js:104:5 +12ms

       error : Cannot call method 'process' of undefined

/usr/local/lib/node_modules/component/node_modules/component-build/node_modules/builder-autoprefixer/index.js:21:49 で、例外が起きているのが分かります。

このファイルを見るとauto変数が初期化されていないのが原因ぽい。 builder-autoprefixerコミットログを見るとそれっぽい修正が入っています。 直すと・・・

component build                                                                                              (git)-[master]
       build : resolved in 17ms
       build : files in 66ms
       build : build/build.js in 69ms - 1kb
       build : build/build.css in 69ms - 7kb

無事ビルドが通りました。パチパチ。

結果

build.js

/**
 * Require the module at `name`.
 *
 * @param {String} name
 * @return {Object} exports
 * @api public
 */

function require(name) {
  var module = require.modules[name];
  if (!module) throw new Error('failed to require "' + name + '"');

  if (!('exports' in module) && typeof module.definition === 'function') {
    module.client = module.component = true;
    module.definition.call(this, module.exports = {}, module);
    delete module.definition;
  }

  return module.exports;
}

/**
 * Registered modules.
 */

require.modules = {};

/**
 * Register module at `name` with callback `definition`.
 *
 * @param {String} name
 * @param {Function} definition
 * @api private
 */

require.register = function (name, definition) {
  require.modules[name] = {
    definition: definition
  };
};

/**
 * Define a module's exports immediately with `exports`.
 *
 * @param {String} name
 * @param {Generic} exports
 * @api private
 */

require.define = function (name, exports) {
  require.modules[name] = {
    exports: exports
  };
};
require.register("getting-started-with-component", function (exports, module) {
var blink = document.querySelector('.blink');

setInterval(function () {
  blink.style.visibility = getComputedStyle(blink).visibility === 'hidden'    ? 'visible'
    : 'hidden';
}, 300);

});

require("getting-started-with-component")

require関数の定義が追加されています。

build.css

抜粋。外部ライブラリのcssファイルと結合された上で、こんな感じでベンダープレフィックスが追加されます。

/**
 * Address differences between Firefox and other browsers.
 */

hr {
  -moz-box-sizing: content-box;
  -webkit-box-sizing: content-box;
  box-sizing: content-box;
  height: 0;
}

元のファイル

/**
 * Address differences between Firefox and other browsers.
 */

hr {
  -moz-box-sizing: content-box;
  box-sizing: content-box;
  height: 0;
}

*1:このリポジトリには他にも参考になる文章がたくさんあるので困ったときに見ると解決策が見つかります

*2:修正済みのbuilder-autoprefixer 1.0.2がリリースされています。今はこのエラーは起きません

ZeroClipboard

ZeroClipboardを紹介します。

ZeroClipboardとは

ブラウザからクリップボードに書き込むためのJavaScirptライブラリ。

githubで使われています。

f:id:ledsun:20140617142453p:plain

なぜZeroClipboard?

クリップボード操作はブラウザ依存(InternetExplorer専用)です。 ZeroClipboardはFlashを使ってクロスブラウザを実現しています。

どうやって使う?

  1. 公式サイトからzipファイルをダウンロード
  2. 解凍
  3. helloworld的なhtmlが公式サイトにあるので作成します。
<html>
  <body>
    <div id="d_clip_button" class="clip_button" data-clipboard-text="Copy Me!" title="Click to copy." style="border:1px solid black; padding:20px;">Copy To Clipboard</div>

    <script type="text/javascript" src="ZeroClipboard.js"></script>
    <script type="text/javascript">
      var client = new ZeroClipboard( document.getElementById('d_clip_button') );
    </script>
  </body>
</html>
  1. http-serverで動かす

ブラウザでローカルファイルとして開いても動きません。 http-serverの使い方はこちら*1

原理

指定したDOMの上に透明なFlashを配置してクリック操作をフックします。 そのため元のボタンのhoverやclickを指定するには調整が必要です。 イベントとCSSクラスが用意されています。 公式ドキュメントを参照してください。

またJavaScriptで元のボタンをclickしても動きません。

*1:公式サイトにはデモを試すにはgh-pagesブランチをcloneしろって書いてあるけど・・・・無い。

設定なし、コマンド一つで起動できる簡易httpサーバー http-server

nodeapps/http-serverを紹介します。

これはなに?

設定なし、コマンド一つで起動できる簡易httpサーバーです。Node.jsで実行します。

どうやって使うの?

インストール

brew install node
npm install http-server -g

起動

http-server .

ブラウザで http://localhost:8080/ を開いてみてください。 引数で指定したディレクトリをrootとして起動します。デフォルトポートは8080です。

どんなときに使うの?

次のライブラリは組み込んだhtmlファイルを直接ブラウザで開いても動きません。

HTTPサーバに配置して動かします。http-serverを使えば、現在のディレクトリで動かせます。

git diffを美しく

diff-hightlightを使ってdiffを見やすくします。

前提

Mac です。

gitインストール

brew install git

diff-highlightにパスを通す

ln -s /usr/local/share/git-core/contrib/diff-highlight/diff-highlight /usr/local/bin/diff-highlight

~/.gitconfigに追記

[pager]
        log = diff-highlight | less
        show = diff-highlight | less
        diff = diff-highlight | less

感想

  • 普段はSourceTree使っている。必要なかった
  • 2年更新されてないのに標準に取り込まれないのはどういう事情なのだろう?

PresentationとかDomainとかSeparateとか

Presentation Domain Separationとは?

Presentation Domain Separation。 プログラムをプレゼンテーションロジックとドメインロジックに分けること

Separate Domain from Presentationとは?

Separate Domain from PresentationPDSを導入するリファクタリングの名前

Separated Presentationとは?

Separated PresentationMVCPDSを強化したパターン。MVCにObserverパターンを適用する

Before:

  1. ControllerはModelを変更する
  2. Controllerは「変更したModelに対応するプレゼンテーションを更新するViewのインタフェース」を呼び出す
  3. Viewは指定されたプレゼンテーションを更新する

After:

  1. ViewはModelの変更を監視する
  2. ControllerはModelを変更する
  3. Modelは変更をObserverに通知する
  4. Viewは変更されたModelに対応したプレゼンテーションを更新する

Controllerの処理が簡単になります。 ControllerがViewを呼び出す処理は残ります。 Modelを更新せずに見た目だけを変更する(警告ダイアログを表示など)場合は、ControllerがViewを呼び出します。

なぜ大抵の単体テストがクソなのか?

James O Coplien のWhy Most Unit Testing is wasteより

最後のまとめを和訳

ツッコミ大歓迎。

Keep regression tests around for up to a year — but most of those will be 
system-level tests rather than unit tests. 

回帰テストを一年間続けよう。ただしテストのほとんどは単体テストではなくシステムテストになる

Keep unit tests that test key algorithms for which there is a broad, formal, 
independent oracle of correctness, and for which there is ascribable business 
value. 

ビジネス価値をもたらすアルゴリズム単体テストを維持しよう

Except for the preceding case, if X has business value and you can text X 
with either a system test or a unit test, use a system test — context is 
everything. 

あるビジネス価値を保証するのに単体テストシステムテストどちらも選べるならシステムテストを選ぼう

Design a test with more care than you design the code. 

テストの設計はコードの設計より念入りに行おう

Turn most unit tests into assertions. 

単体テストは(契約としての)アサーションに置き換えよう

Throw away tests that haven’t failed in a year. 

一年間失敗しなかった単体テストは捨てよう

Testing can’t replace good development: a high test failure rate suggests 
you should shorten development intervals, perhaps radically, and make 
sure your architecture and design regimens have teeth 

テストは良い開発の代わりにはならない

If you find that individual functions being tested are trivial, double-check
 the way you incentivize developers’ performance. Rewarding coverage or
 other meaningless metrics can lead to rapid architecture decay. 

カバレッジやらの意味の無いメトリックスにこだわるとアーキテクチャはおかしくなる

Be humble about what tests can achieve. Tests don’t improve quality: 
developers do. 

テストの出来ることを過信しない。テストで品質は向上しない、開発者だけがそれをなす

免責

これは個人の翻訳です。内容、正確性を保証するものではありません。

Jim Coplien情報

著書

組織パターン 新装版 マルチパラダイムデザイン

Homebrewの設定

HomebrewMacのイケテルっぽいパッケージ管理ツールコマンドラインでアプリケーションのインストールや削除が出来ます。

Homebrew自身の設定

インストール

ruby -e "$(curl -fsSL https://raw.github.com/Homebrew/homebrew/go/install)"

環境変数PATHの設定

環境変数PATHを設定しコマンドの探索順序を変更します。 ~/.bash_profile~/.zprofileに以下の行を記述します。*1

export PATH=/usr/local/bin:$PATH

この設定をする理由

Homebrewはコマンドを/usr/local/binにインストールします。 このディレクトリはOS標準の/usr/binより優先順位が低い。

例えばHomebrewでrubyをインストールしても、OS標準のrubyが先に見つかり実行されます。

アップデート

brew update

設定確認

インストール状態をチェックするコマンドがあります。

brew doctor

Mac OSX に Homebrew をインストールする - asa nisi masaを参考に対処します。

パッケージのインストール

例えば

brew install zsh 
brew install node

cask

Homebrewはバイナリパッケージで配布されているアプリケーションをインストールできません。 Cask拡張を使います。

インストール

brew install caskroom/cask/brew-cask

パッケージのインストール

brew cask install sublime-text 
brew cask install dropbox 

*1:ログイン時に一回だけ設定されればいいのでprofileを使います。参考.bash_profileと.bashrcの違いは

プログラミングのためにその1

このエントリの意図

下のが、おもしろかったので真似します。

興味を持ったものを動かしてみる

Twitterはてなブックマークで新しいライブラリが話題になって、 APIや説明記事を読んでいるだけだと興味を維持できない。 動かしてみて新しい発見があるとやる気が維持できる。

直近ではReactJS動かしてみた。 JSXはいいんだけどメソッド名React.renderComponentがあまりしっくりこなかった。

動くサンプルをgithubにあげる

記憶力が無いので、頻繁に使わないコードはすぐ書き方を忘れてしまう。 Githubリポジトリを用意して、動いたサンプルコードを登録してる。 「書いた記憶はあるんだけど、細かいところ覚えてない」と思った時に見てる。

Githubな理由はいろんな環境から見える確率が高いから。 日立グループのようにGithubが見れない人は違うリポジトリを使うと良いと思う。

手順を説明する

単に動かすだけだと、なんで動いてるのかよくわからないし、 次に、もう少し違うことをやろうとしたら、できるかわからなくて不安。 説明する記事を書いてみる。

例えば

テディベア効果で疑問点が明確になって理解が深まる。

記事を書くのは面倒くさいので、枝葉を捨てて自分が気に入っているところに注力する気になれる。

定期的にプライベートのコードを書く

仕事でしかコードを書かないと、 学んだことを仕事のコードで試したくなる。 再利用しないのに汎用的に書いて無駄な工数使うとか。 でも色々試してみたい。

プライベートのコードを書く時間を確保して遊びのコードを書くと、ジレンマを解消できる。 自分の場合は通勤時間を使っている。

新しいライブラリも使い放題。 新しい記法も使い放題。 読めないと言われる筋合いがない! 「こいつはすげぇぜ!」ってコードが書けるようになったら仕事のコードに適用する。

起動とテストの自動化

通勤電車だとコードを書く時間が一回20分ぐらい。 アプリケーションの起動や動作確認に時間を使いたくない。

grunt

で起動して、ファイルを変更したらテストが走るぐらいまで用意しておくと、コードを書くことに集中できる。

新しい技法にチャレンジする

プログラミングするなら、知らない技法を使えるようになりたい。 通勤はプログラミング時間になったので、お風呂で技術書を読む。

最近は「JavaScriptで学ぶ関数型プログラミング

JavaScriptで学ぶ関数型プログラミング

下手くそな関数合成でも誰にも迷惑かけないのでやり放題。

TDD の Death and Rebirth まごころを君に

我が輩のTDD体験を語る

背景

ここ最近のTDDに関する話の噛み合なさっぷりよ・・・

大きな疑問

業務でxUnit使ったテストファーストやってる人はいるの?

TDDは死んでないと言うても、業務でxUnit使ったテストファーストやってる人はいるの? じゃあ、自分はどうなのか?自分語りをする。

前提

ここで話すTDD

XPの第一版にはそれ以外の要素は書いてなかった。 俺はそう刷り込まれた。 一番大事だと思う要素から話す。

「これからインスピレーションを得たもっといい方法があるよ」って話は喜んで聞きます。

我が輩の経験

3プロジェクト

  1. C言語のSDPパーサ
  2. C言語のOBEX通信モジュール
  3. C言語ツール

言語の性質は意図してません。たまたま経歴がそうだっただけです。

xUnitを使ったテストファーストの効用

よく言われる通りの効用がある

  • バグが減る
  • 設計が洗練される
  • 設計の練習になる

バグが減る

リリースまでに使う回数が増えるのでバグが減る。 手で動作確認するのに比べると100倍ぐらい多い回数実行できる。 たくさん実行すると、マルチスレッドのデッドロックを体験できることもある。

設計が洗練される

テストクラスはクライアントクラス以外のテスト対象クラスを使う視点になる。 簡易的な仕様変更になる。

下記の仕様に依存している暗黙の前提条件に気付くきっかけになる。

  • 呼び出し順
  • 初期値
  • 入力値の範囲

暗黙の前提条件を減らすと、他のプロジェクトに持って行きやすいポータブルなコードになる。

ただ、この効果は機能追加による現実の仕様変更に劣る。 テストコードは予想外の機能を追加しない。 現実の仕様変更より実践的であることはない。

設計の練習になる

プログラミング初心者の設計の練習によい。

オブジェクト指向入門の4.3 機能的トップダウンアプローチに

機能的トップダウン設計を検討した結果. この手法は重要なソフトウェアシステムの開発には
不向きであることが明らかになった. 小規模のプログラムや独立したアルゴリズムについては
トップダウン設計はなお設計の模範として有用である. また, 系統的に問題を解決できるので, 
初級プログラミングコースの教育法として役に立つことも確かである.

そして

トップダウン法では全てのシステムが最も抽象的なレベルでその第1の機能を記述できるもの
とされている. (中略)現実のシステムには頂点など存在しない。

ともある。

TDDでは

  1. テストコードで第1の機能を指定できる
  2. 機能的トップダウンアプローチが可能
  3. 系統的に問題を解決できる
  4. プログラミング初級者でも処理の設計がしやすい

この性質を利用してTDDの三角測量がなりたつ。

そしてレッド/グリーン/リファクタリングの黄金の回転をまわしながらオブジェクトを見つけ出す。 機能的なアプローチからオブジェクト指向にたどり着ける。

ただし、TDDの三角測量だけでデザインパターンを活用するオブジェクト指向設計にたどりつくのは困難。 慣れてきたらオブジェクト指向のこころを読もう。 (※これは個人の経験に基づく感想であり、効果を保証するものではありません。)

xUnitを使ったテストファーストの前提条件

第1の機能がないと始められない。

事前の設計が必要。 ERモデリングだけでは不十分。 C言語ならモジュール構成。 OO言語なら、クラス単位まで行かないまでもクラス群単位のおおざっぱなオブジェクト構成が必要。 インタフェースの名前と入出力が必要。

三角測量と逆向きになるのでTDD BootCampから実案件への移行には苦労するのではないかと思う。

もうやってない

もう6年やってない。

予算1000万の業務用WEBアプリの場合、オブジェクトの構成が決まらない。 レイヤーはプレゼンテーション、サービス、DAOぐらいで大体固定。 しかし、オブジェクト単位の仕様は画面仕様依存で決まらない。

仮に事前に設計してテストコードを書いても、 画面を見せながら仕様検討・変更する。 機能が変わるとテストコードの修正がコストになる。

画面の機能から作り始めるとユニットテストを書くマインドに切り替えるコストが高すぎる。 それよりユーザのマインドに切り替えた方が以下の良い気付きが得られる。

  • この画面は必要か?
  • 画面のレイアウトから機能が読み取りやすいか?
  • 画面を複数に分けた方がよいか?

画面の機能が8割固まるまでは手で回帰テストを回している。 この時はどの機能がどのプロダクトコードに依存しているか把握している。 どの機能を動かして確認すればよいかわかっているので、 手でやってもそれなりの効率で回帰テストが回せる。

xUnitを使った「後追い自動テスト」はやってる

ある程度機能が固まってから後追いでテストコードを書くことはある。

数年前に作ったライブラリに機能追加するときはテストコードがあるととても心強い。 例えば Alhambra というライブラリのテストはMSTestで書いている。

この手のライブラリをメンテナンスしたい時は、ライブラリのメンテナンスをしたいのではなくライブラリを使用しているプログラムを修正したい。 ライブラリの修正や動作確認に時間を使いたくない。 自動テストがあれば2年ぶりの機能追加でも、テストを実行するだけで既存機能を壊してないとわかる。

ただし、これはxUnitでEnd-to-Endの自動回帰テストを書いている。 実際のデータベースを使って実行している。 ユニットテストとしては悪いと言われているデータベースに依存したテスト。

※これは上記ライブラリの使用を推奨するものではありません。 EntityFrameworkでもLINQtoSQLでも好きなものを使ってください。

自動回帰テスト

作れるならEnd-to-Endの機能テストを自動化した方がうれしい。 テストコードが無駄にならずに内部構成を修正できる。

xUnitレベルのテストではレイヤーの追加・削除やデザインパターンの適用などで オブジェクトの構成を変更すると、かなりのテストコードを捨てたり直したたりすることになる。

結論

「xUnit使ったテストファースト」は死んだ。

あとは知らん

参考文献

XPエクストリーム・プログラミング入門―ソフトウェア開発の究極の手法オブジェクト指向入門オブジェクト指向のこころ