【翻訳】TDD is Fun

@solnicが、DHHの例の記事へのカウンター的な記事をポストしてまして、自分のために読んでみたらよい内容だと思ったので、翻訳してみました。翻訳ミスとかあると思いますが、、、すみませんです。。。

TDD is Fun

Posted by solnic on Apr 23 2014

著 solnic 2014年4月23日

Today DHH published a blog post about TDD being dead (to him at least). It’s really not that surprising since from what I know (please correct me if I’m wrong) David’s experience is mostly based on building web apps with Rails. I get that, I really do. For me practicing TDD in a rails environment is much harder than when I work on my OSS libraries. There are many reasons why TDD in Rails is just a bit harder than it could be but that’s a big, separate subject. In this post I want to explain that TDD can be fun!

今日、DHHは (少なくとも彼にとっては) TDDが終わったことについてブログ記事を発表しました。私の知っているところでは (もし私が間違っていたら指摘してください)、Davidの経験のほとんどがRailsでのウェブアプリ構築に基づいているので、これは全く驚くようなことではありません。私にはよくわかりますし、本当にそうだと思います。私にとって、Rails環境でTDDを実践することは、自分のOSSライブラリで実践する時よりずっと大変です。RailsでのTDDがあるべきより少しばかり大変なのには多くの理由がありますが、それは大きな別の議題なのです。この記事では、TDDは面白いということを説明しましょう!

it’s not as strict as you think

There are many guidelines in TDD. That you should focus mostly on unit testing. That you should be using mocks and stubs in isolated unit tests. That you should have contract tests. And so on. That list can be pretty long.

あなたが考えているほど厳格ではない

TDDには多くのガイドラインがあります。「主に単体テストにフォーカスすべきだ」や「独立した単体テストではモックやスタブが使われるべきだ」とか、「contract testsを利用するべきだ」などです。そのリストはかなり長いです。

Those are guidelines, they make a lot of sense and personally I found them to be useful but for me, when it comes to the core of TDD…it’s this:

それらはガイドラインであり、それらは大いに意味があり、個人的には有用であると理解していますが、TDDのコアというのは…以下だと思います。

Write a test that describes expected behavior of your system and make it pass.

Yes, that’s it. That’s how your journey begins. It doesn’t have to be a unit test. It can be an integration test. It can be an acceptance test (which is great when building web apps!). You simply know what kind of test you are able to write at a given point in time based on your current knowledge. This knowledge will be expanding as you go through red/green/refactor cycles. You will see missing interfaces, you will be able to introduce new objects and test-drive them from the start because after few refactorings you will know, trust me, you will know how they should look like.

自分のシステムにおいて期待する振る舞いを記述するテストを書き、それをパスさせなさい

そう、これなんです。これが出発点ということなのです。単体テストである必要もありません。承認テストかもしれませんし、受け入れテストでもかまいません (それは、ウェブアプリを作る場合には、とても有効です!)。その時の知識に基づいて、その時点でどんなテストを作成できるかをあなたはよく分かっているはずです。その知識はred/green/refactorサイクルを通して、拡大していくでしょう。そして、足りないインターフェースを理解し、最初から新しいオブジェクトやテスト駆動を導入するでしょう。というのも、数回のリファクタリングの後に、あなたは分かるように、いや本当に、あなたはテストがどのようにあるべきか分かるようになるからなのです。

That’s how tests are guiding you, that’s how you’re designing your system based on the feedback of your tests. It’s a process, a journey that involves many small refactorings. You don’t stop after making your first test pass. You move on and discover missing pieces of the puzzle. It’s a lot of fun and it gives you great confidence that the code you just wrote works exactly like you want.

そのようにして、テストはあなたを導き、あなたはテストの結果に従って自分のシステムを設計するでしょう。それは、多くの小さなリファクタリングを伴うプロセスであり、長い工程なのです。最初のテストをパスしたからといって、止まってはなりません。どんどん進んで、パズルの足りないピースを探してください。それはとても面白く、あなたが書いたコードがまさに思いどおりに動く大きな自信を与えてくれます。

Unit testing

It seems like DHH confuses unit testing with TDD. Unit testing is part of TDD and it’s a very crucial part. Most of the nastiest, hardest to fix bugs exist because of a poor unit test coverage. That’s a fact.

単体テスト

DHHは単体テストとTDDを混同しているようです。単体テストはTDDの一部、極めて重要な一部です。たいていのバグフィックスが不快で大変なのは、単体テストカバレッジが不十分だからです。これは真実です。

Some people are afraid of unit testing though and this is also not that surprising. It’s mostly because the cost of maintaining a big unit test suite is high. It’s very easy to spend a lot of time writing many small, focused unit tests that soon turn out to be obsolete and that hurts a lot.

一部の人が単体テストをいやがっているが、それもまたやはり驚くようなことではありません。というのも、大抵、大規模な単体テスト一式をメンテナンスするにはコストがかかるからです。すぐに使い物にならなくなり、苦痛うを伴う小さくて焦点の合わさった単体テストを書くことに多くの時間が簡単にかかってしまいます。

I think this happens because of two reasons:

  • Writing unit tests too early
  • Using mocks and stubs prematurely

私はこれには2つ理由があると思います。

  • 単体テストをあまりに早くに書いている
  • モックやスタブを使う段階ではないのに使っている

We’ve been told that a unit test excercises a unit in complete isolation from the surrounding environment especially its dependencies. That’s a really nice concept and it works very well. But…you really need to feel confident about what you’re doing before you introduce unit tests. This can be achieved rather quickly after 1 maybe 2 red/green/refactor cycles after making some higher level test pass. You will see missing abstractions and you will know what kind of an interface you want to have. That’s a great moment to start writing unit tests.

単体テストは、取り巻く環境、特にその依存性から完全に独立して用いるものだと教えられてきました。それはとてもよい概念で、とてもうまく働きます。しかし、単体テストを導入する前に、あなたが行おうとしていることに自信を持つことが必ず必要なのです。これは、上位レベルのテストが通った後、red/green/refactorサイクルを1,2回実施した後、かなりすばやく達成できます。足りないアブストラクションを理解し、どういう種類のインターフェースをもつべきか理解できるでしょう。そのときが、単体テストを書くべき時なのです。

There are also cases where you’re just prototyping something. This is really a bad moment for unit testing.

何かをただプロトタイピングしている場合があります。こういう時に単体テストを書くことは最悪です。

Another pain-point in unit testing that probably discouraged DHH is using mocks and stubs. If you do things “by the book” you would immediately start mocking dependencies and stubbing various method calls. For me it never works like that. Sometimes I would mock an interface as soon as I discover it. Another time I would just wait with mocking until I feel really confident that a given collaborator object shouldn’t be treated as an internal implementation detail. There are many guidelines here as well, learn about them and keep them in mind but don’t follow them strictly all the time.

たぶん、DHHのやる気を失わせたユニットテストの中での問題点は、おそらくモックとスタブを使うことでしょう。もしあなたが「型通り」に行おうとすれば、すぐに依存性をモックし始め、さまざまなメソッドの呼び出しをスタブすることでしょう。私は、これはうまく働かないと考えます。時々、私はインターフェースを見つけるとすぐに、それをモックします。別の場合には、ある共同して動くオブジェクトを内部の実装詳細として取り扱うべきでないということに、本当に自信が持てるまでずっと、モックを使うのを待つでしょう。多くのガイドラインがあり、それは学ぶべきですし、心に留めておくべきですが、それらに常に厳密に従う必要はありません。

It’s fun but it takes time to learn

You can’t buy a book about TDD and learn it in a month. It takes a lot of time and that’s why trying to be very strict about various TDD guidelines (especially the ones about unit testing) will be frustrating.

面白いが学ぶのに時間はかかります

TDDに関する本を買って、それを1か月で習得することはできません。長い時間がかかり、そのため、様々なTDDガイドライン (特に単体テストに関するものは) にあまり厳密であろうとすると、いらいらすることになるでしょう。

This doesn’t change the fact TDD is a lot of fun. Just be cool with it. If you manage to teach yourself to write tests first, you already gain a lot. Break the rules whenever you feel they are blocking you or slowing you down. If you broke a rule and ended up with problematic code you’ll at least learn why following that rule in a given context is a good idea.

TDDがとても面白いことを変えるわけではありません。ただ冷静であるべきなのです。とにかくテストがうまく書けるようになれば、あなたは多くを得ます。テストがあなたを妨げたり、遅らせたりすると感じたらいつでも、ルールを破るべきです。ルールを破って問題を持つコードになってしまっても、その文脈ではルールに従うことは良いことであると、すくなくとも学べるでしょう。

Chill out, doesn’t matter if you get things right during first or fifth attempt, the process is all that counts. Write a test, make it pass, look at the code and take it from there.

冷静になりましょう。最初か5回目かの試みの間で正しさを得るかは問題ではありません。プロセスがそのすべての論点なのです。テストを書き、それをパスし、コードを見て、そこからそれを続ければいいのです。

【UNIX】ファイルの所有者、所有グループについて入門的にまとめてみた

ファイルの所有者、所有グループについて入門的にまとめてみた。

概要

  • 所有者、所有グループとは
  • 現在の状態の確認
  • 変更方法

所有者、所有グループとは

  • ファイルやディレクトリには、owner(所有者)、group(所属グループ)、others(それ以外)が設定されている
  • ファイルのパーミッションは、それらの単位で設定することができる
  • 所有者を変更できるのは、スーパーユーザのみ
  • 新規ファイルまたはディレクトリを作成した場合は、所有者は現在のユーザー、所属グループは作成者の所属グループ

現在の状態の確認

$ ls -l
total 12
drwxrwxr-x 2 user001 group001 4096 Apr 19 09:37 rails_root
-rw-rw-r-- 1 user002 group002    6 Apr 20 06:22 hoge.html
  • user001, user002 がファイルのowner
  • group001,group002 がファイルのgroup

変更方法について

ownerの変更方法

以下の方法で変更が可能

$ chown user file
  • fileの所有者をuserに変更する

$ chown -R user hoge
  • hoge ディレクトリ内の所有者をすべてuserにする
$ chown usre:user-group file
  • fileの所有者をuser にし、グループをuser-groupにする

groupの変更方法

$ chgrp user-group file
  • fileのグループをuser-groupに変更する
  • 変更が可能なのは、そのファイルの所有者またはスーパーユーザのみ

$ chgrp -R user-group etc
  • etcディレクトリ内の所属グループをすべてuser-groupにする

【UNIX】groupについてあれこれ

概要

  • グループとは
  • グループの確認
  • グループの作成、修正、削除
  • グループへの追加、グループからの削除
  • グループの変更
  • 参考URL

グループとは

  • ユーザーを論理的にまとめているもの
  • ファイルやディレクトリが所属しているグループに与えられている権限は、そのグループに所属しているユーザーにも与えられる
  • ユーザーは複数のグループに所属することができる
  • メインのグループをプライマリグループ、イニシャルグループと呼ぶ
  • それ以外のグループをサブグループと呼ぶ
  • ユーザーが作成したファイル、ディレクトリは、そのユーザーのプライマリグループが設定される

グループの確認

自分の所属しているグループの確認

$ groups
ec2-user wheel

グループ一覧の確認

$ cat /etc/group
root:x:0:
bin:x:1:bin,daemon
daemon:x:2:bin,daemon
sys:x:3:bin,adm
adm:x:4:adm,daemon

表示の見方

wheel:x:10:ec2-user
  • wheel グループ名
  • x 暗号化されたパスワード
  • 10 グループID
  • foo,bar サブグループとして所属しているユーザーのアカウント。カンマ区切りで表示される。

グループの作成、削除、修正

グループの作成

$ groupadd グループ名

オプション

-g GID
  • 作成するグループのIDを指定できる
  • オプションを指定しない場合は、システムが指定する

グループの削除

$ groupdel グループ名
  • 指定したグループを削除する

グループの修正

$ groupmod [-g GID] [-n NEWGROUP] グループ名
  • グループの情報を変更する
  • -g GIDGIDにIDを変更する
  • -n NEWGROUPでグループの名前をNEWGEROUPに変更する

グループへの追加、グループからの削除

グループへの追加

方法は2種類ある

  • usermod
  • gpasswd

usermodの場合

$ usermod -G GROUPNAME USERNAME
  • usermodコマンドは、アカウント情報を変更できる
  • -Gオプションでグループの指定が可能
  • usermod コマンドの場合は、それまで所属したグループからは外れる

gpasswdの場合

$ gpasswd -a USERNAME GROPNAME
  • gpasswdコマンドで、グループの情報を変更できる
  • -a USERNAME で USERNAME をグループに追加する
  • それまで所属していたグループも残る

グループから削除

$ gpasswd -d USERNAME GROUPNAME
  • gpasswdに-dオプションを渡すことでグループから削除できる

グループの変更

以下の方法で指定したグループにログインが可能

$ newgrp グループ名
  • セッションを終了せずにグループを切り替える(グループIDを切り替える)
  • 一時的にグループを変えるときに実行する
  • サブグループとして所属しているグループには、パスワードなしでログインが可能
  • 所属していないグループにはパスワードが必要。
  • パスワードが設定されていないグループにはログインすることができない

参考URL

http://kazmax.zpp.jp/linux_beginner/etc_group.html
http://kazmax.zpp.jp/linux_beginner/lin1.htm
http://codezine.jp/unixdic/w/groupadd
http://www.linux-beginner.com/linux_kihon23.html
http://www.linux-beginner.com/linux_kihon24.html

【UNIX】user一覧の確認コマンド

以下のコマンドで確認できる

コマンド

$ less /etc/passwd  
ec2-user:x:500:500:EC2 Default User:/home/ec2-user:/bin/bash

表示の見方

  • ec2-user ユーザー名
  • x 暗号化されたパスワード
  • 500 ユーザーID
  • 500 グループID
  • EC2 Default User コメント。なければ表示されない
  • /home/ec2-user ユーザーのホームディレクトリ
  • /bin/bash ユーザーのログインシェル名

ユーザー名のみ抽出

覚えておくと割りと便利かも。

$ cut -d: -f1 /etc/passwd  
root
bin
daemon
adm
lp

GitHub Flow についてのまとめ

手順

  1. masterブランチは常にデプロイできるようにしておく
  2. 新しい作業をするときはmasterブランチから何をしているかわかりやすいブランチを作成する
  3. 作業をしたら作成したローカルリポジトリのブランチにコミットする
  4. 同名のブランチをGitHubのリモートリポジトリに作成し、定期的にpushする
  5. masterの差分が大きくなっている場合は、リモートのmasterをローカルのmaster にpullし、ローカルのmasterを作業ブランチにマージする
  6. フィードバックがほしい時は、Pull Requestを作成しやりとりする
  7. 作業を終わったら、GitHub上でmasterに対してPull Requestを出す。
  8. Pull Requestがmasterブランチにマージされたら、ただちにデプロイする

特徴

  • masterブランチを常にデプロイできるようにすること 小さい単位でデプロイする。こうすると大きなバグが複数入ることはない。小さいな単位のバグがはいるときは、そのコミットをrevertするか、修正したコミットを出して対応する。テストや継続的インテグレーションなどのアプローチが必須。

  • 新しい作業をするときはmasterブランチから何をしているかわかりやすいブランチを作成する 作業ブランチの名前をわかりやすいものにすることで、チームのメンバーがどのようなタスクを実施してるのかわかる。

  • 作業ブランチでのコミットの粒度を小さくする Pull Requestのレビュアーがわかるような粒度のコミットにすること。小さいコミットのほうがわかりやすし、レビュアーの負担にもならない。

  • 定期的にpushする pushはローカルと同じ名前のリモートリポジトリにpush する。他のメンバーがコードを見れるようになる。また、コードのバックアップにもなる。

  • Pull Requestを使う masterへのマージ依頼だけではなく、フィードバックがほしい時にPull Requestを出す。そうすれば間違いに早く気づく。マージではないPull Requestを送るときは、[WIP]をつけること。そうすれば間違ってマージするのを防げる。

参考URL

https://gist.github.com/Gab-km/3705015
http://www.atmarkit.co.jp/ait/articles/1401/21/news042.html

【翻訳】そう、コントローラのテストを書くべきなのです!

Rspecでcontroller のテストを書くべきなのかと思うことがあると思います。

Everyday Rails - RSpecによるRailsテスト入門の116ページに以下の記述がありました。

“なぜコントローラをテストするのか? コントローラのメソッドを個別にテストするのにはちゃんとした理由がいくつかあります。 コントローラもメソッドを持ったクラスである。 この点についてはPiotr Solnicaが素晴らしいブログ記事を書いています。そしてRailsアプリケーションにおいて、コントローラはかなり重要なクラス(とメソッド)です。なので、スペック的にモデルと平等に扱うのは良い考えです。”

抜粋:: Aaron Sumner, Junichi Ito (伊藤淳一), AKIMOTO Toshiharu and Shinkou Gyo(魚振江). “Everyday Rails - RSpecによるRailsテスト入門”。 iBooks.

自分のために翻訳してみたので、載せておきます。
原文はこちらにあります。
Yes, You Should Write Controller Tests!

そう、コントローラのテストを書くべきなのです!

コントローラのテストをコーディングすることに意味がないと主張する人がいるのは本当に驚くべきことです。 おそらくもっともありがちなのは、アクションのテストは、ビューが適切に表示されるかのチェックとともに承認テストのなかでカバーされるという主張です。 正しいかといえば、間違っています!遅い承認テストですべての可能性を網羅したコントローラのアクションのシナリオをカバーできるといいはるつもりでしょうか? 例えば、起こるべきすべてのリダイレクトが承認テストの中でテストできると言い張るつもりなのでしょうか? また、承認テストで、すべての無効なリクエストはチェックされているのでしょうか? 存在するコントローラを完全にテストするために、一連の承認テストに27時間13分かけるとでもいうつもりですか? それでは、きっと、あなたの一連の承認テストは多少速くても、おそらくは「都合のよいシナリオ」しかカバーしない、…基本的に、テストがカバーすべき範囲をたくさん外すということになります。

コントローラについての単純な事実

コントローラについて、単純な事実があります。それは複数のメソッドをもつ一つのクラスだということです。もう一度言います。そうなんです、複数のメソッドをもつ一つのクラスなのです。 コントローラーはテストされるべきなのですし、全てのメソッド(アクション)がテストでカバーされるべきなのです。
なぜかといえば、アプリケーションの他のクラスでテストを書くのと同じ理由です。コードが想定通りに動くのか我々は確認したいものです。また、テストをする時には、コントローラーを薄くしておけばずっと楽になります。もし、コントローラ用にテストをコーディングするのが大した作業でないなら、おそらくそれは良いコントローラなのでしょう。 もし、mockのなかで溺れそうになるなら、多分、コントローラをリファクタリングする必要があるでしょう。

ここに、サンプルのcreateアクションをもったユーザーコントローラの例を示します:

class UsersController < ApplicationController
  before_filter :load_group

  rescue_from ActiveRecord::RecordNotFound do
    render :not_found
  end

  def create
    @user = @group.users.create(params[:user])

    if @user.persisted?
      redirect users_path, :notice => ‘User created!’
    else
      render :new
    end
  end

  private

  def load_group
    @group = Group.find(params[:group_id])
  end
end

ご覧のとおり、before_filter においてグループオブジェクトをロードし、そのオブジェクトを使って新しいユーザーを作成します。 非常に、非常に、単純なのです。もし、このコントローラ向けの承認テストをかけと言われても、多分グループが見つからないケースをカバーしたものはできないと断言できます。結局はみんな、普通は、楽観的なシナリオにもとづいて承認テストを書くものです。 もし、すべてのケースをカバーしようとすれば、たぶん納期に間に合わないことでしょう;)

しかし、すべてのpathをカバーすればよいだけではありません!コードの質についてもなのです。 上の例を見てもおそらく、コードのにおいなどしないでしょう。なので、createアクション向けのspecを書いてみましょう:

describe UsersController do
  describe "#create" do
    subject { post :create, :group_id => group_id, :user => attributes }

    let(:group_id) { mock(‘group_id’) }
    let(:group)    { mock(‘group’) }
    let(:user)     { mock(‘user’) }
    let(:users)    { mock(‘users’) }

    before do
      Group.should_receive(:find).with(group_id).and_return(group)
      group.should_receive(:users).and_return(users)
      users.should_receive(:create).with(attributes).and_return(user)
    end

    context ‘when attributes are valid’ do
      it ‘saves the user and redirects to the index page’ do
        user.should_receive(:persisted?).and_return(true)
        subject.should redirect_to(:users)
      end
    end

    context ‘when attributes are not valid’ do
      it ‘saves the user and redirects to the index page’ do
        user.should_receive(:persisted?).and_return(false)
        subject.should render_template(:new)
      end
    end
  end
end

何が起こったのでしょうか?グループオブジェクトに深く入り込み、そのユーザーを取得し、グループオブジェクトを用いて新たなユーザーを生成しているために、 specにはより多くのmockが必要になるし、そうすべきなのです。 ここでの例は、単純なものですが、もしアクションがもっと複雑で、もっと論理が分岐していて、 structural couplingももっとあるなら、specがいったいどんなものになるのかは想像できることでしょう。

簡単にこのアクションをリファクタリングしてみましょう:

class UsersController < ApplicationController
  # stuff

  def create
    @user = @group.create_user(params[:user])

    if @user.persisted?
      redirect users_path, :notice => ‘User created!’
    else
      render :new  
    end
  end

  # more stuff
end

こんどはテストは少し簡単になります:

describe UsersController do
  describe "#create" do
    subject { post :create, :group_id => group_id, :user => attributes }

    let(:group_id) { mock(‘group_id’) }
    let(:group)    { mock(‘group’) }
    let(:user)     { mock(‘user’) }

    before do
      Group.should_receive(:find).with(group_id).and_return(group)
      group.should_receive(:create_user).with(attributes).and_return(users)
    end

    context ‘when attributes are valid’ do
      it ‘saves the user and redirects to the index page’ do
        user.should_receive(:persisted?).and_return(true)
        subject.should redirect_to(:users)
      end
    end

    context ‘when attributes are not valid’ do
      it ‘saves the user and redirects to the index page’ do
        user.should_receive(:persisted?).and_return(false)
        subject.should render_template(:new)
      end
    end
  end
end

グループが見つからないケースもチェックする例も加えるべきです。これはかなりまれなケースではありますが、テストが必要であるという事実に変わりはないのです:

describe UsersController do
  subject { post :create, :group_id => group_id, :user => attributes }

  let(:group_id) { mock(‘group_id’) }
  let(:group)    { mock(‘group’) }

  describe ‘#create’ do
    context ‘when group is not found’ do
      before do
        Group.should_receive(:find).with(group_id).
          and_raise(ActiveRecord::RecordNotFound)
      end

      it { should render_template(:not_found) }
    end
  end
end

これです!小さいテスト、速いテスト。コードがカバーされている。コントローラーも薄い。ここで、ユーザーコントローラ向けのビューのspecも書いてみるのもいいかしれませんが、私は書くつもりはありません、それは面倒です。

総括

やはり、コントローラのためのテストは書くべきなのです。誰かに書くべきかと問われれば、あなたはイエスと答えるべきなのです。コントローラのテストを書かなくてよいとする有効な主張は存在しないのです。 コントローラも、結局はコードであり、テストでカバーされるべきなのです。 テストを設計に組み込めば、コントローラは満足できるものになりますし、さもなければ、滅茶苦茶なものに終わるでしょう。 適切なテストがなければ、コントローラーが、無駄のない、よく設計されたものかどうかを本当の意味で確かめることはできないのです。だから、コントローラ向けのテストを書くべきなのです!

もしまだ見ていないようなら、Avdi の「デメテルの法則」についての素晴らしいポストを読むべきでしょう。この記事でもふれたstructural couplingについて書かれています。

追記(20140408)

一応、元記事の方に掲載の確認?はとってあります。

広告を非表示にする

git push のオプション -u と --set-upstream

git push -u origin master

-u と --set-upstream は同じ意味です。このオプションをつけるとローカルリポジトリの現在のブランチの上流をorigin master に規定したことになります。
このオプションをつけると、次からは git push だけで上記のコマンドと同じことを実施できます。また、git pull だけでも git pull origin master と同じ意味になります。

参考リンク

http://qiita.com/ironsand/items/6c301fef730d53f35bc3 http://usualoma.hatenablog.com/entry/20110214/1297644592