Groovyによる宣言的セットアップ
Groovyによる宣言的セットアップ
jUnit実践入門の第7章「テストフィクスチャ」で、フィクスチャ(テストデータ)のセットアップ方法が紹介されています。フィクスチャのセットアップにはインラインセットアップや暗黙的セットアップ、外部リソースからのセットアップなど色々な方法がありますが、個人的にGroovyによる宣言的セットアップというのが気になったので実際にやってみました。
Groovyプラグインのインストール
Groovy用のEclipseプラグインがあるので、まずはそれをインストールします。
Help->Install New Software...を選ぶとウィンドウが表示されるのでAddボタンをクリックして以下の内容を入力します。
Name | Location |
---|---|
Groovy Eclipse Plugin | http://dist.springsource.org/release/GRECLIPSE/e4.3/ |
インストールが終わっていつも通り再起動すると、コンテキストメニューのNewにGroovy関連が追加されます。試しにGroovy Classを作ってみるとJava Classと同様のウィザードが表示されるので、クラス名を「Hello」にしてFinishボタンをクリックするとHello.groovyが作成されます。
で、ここで一つ問題が。Groovyの書き方が分からない…w まぁそれは今回の主目的からは外れるので取り敢えず置いておくことにします。
セットアップを書いてみる
ユーザー情報を操作するDaoクラスのテストのセットアップをGroovyで書く、という仮想シナリオです。
まずはユーザー情報から。
public class User { private String userId; private String password; private String email; private Date birthDay; private Integer gender; /* * getter/setter省略。 */ }
次にこのユーザー情報を生成するセットアップ。これはGroovyです。
class UserDaoTestHelper { static User createUser_通常ユーザーの場合() { new User( userId: "m-namiki", password: "pass", email: "example@sample.com", birthDay: new Date("2014/01/01 00:00:00"), gender: 0 ) } }
で、最後にこれを使ったテストコードです。
public class UserDaoTest { @Test public void test() { User user = UserDaoTestHelper.createUser_通常ユーザーの場合(); assertThat(user.getUserId(), is("m-namiki")); assertThat(user.getPassword(), is("pass")); assertThat(user.getEmail(), is("example@sample.com")); assertThat(user.getBirthDay(), is(new DateTime(2014, 1, 1, 0, 0, 0).toDate())); assertThat(user.getGender(), is(0)); } }
セットアップの部分でどんなデータなのかを分かりやすく書けるので、なかなか使い勝手は良さそうです。ただGroovy側で色々頑張ろうとするとそれは本末転倒なことになってしまうのでそこら辺の線引きをきちんと決めておかないと混乱の元になりそうなので注意しないとダメですね。
自分がテストを書く場合だと最近はインラインセットアップが多いですが、以前SAStrutsを使ったときにDBに対するテストを行う際はExcelでデータを作成する、というやり方がチームの人にかなり好意的に受け入れられていましたし、自分でも分かりやすく感じました。ただこれもデータを参照するのに外部ファイルをイチイチ開かなきゃいけないのがちょっと手間で、できればテストコードから簡単に、かつ見易い状態で参照できるのが良いはずなので、Groovyによるセットアップをプロジェクトに導入してみて色々試していきたいと思います。
実際にやってみた
上記はだいぶ前に書いたのですが(3ヶ月前…)ずっと下書きに入っててアップするのを忘れてました。なので、現在担当しているプロジェクトで導入してみた顛末も追記しておきます。
フィクスチャ自体は上記の通りHelperクラスを作ってそこで作成するようにした結果、だいぶコード量が少なくなってテストコードも見通しがよくなりました。
ただMavenでビルドする際にプラグインの追加等をしておかないと、*.groovyがコンパイルされず、当然それを利用しているクラスもコンパイルエラーとなるので、pom.xmlの設定をメモしておきます。
まずはdependenciesに以下を追加。
<dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy-all</artifactId> <version>1.8.9</version> </dependency>
次にpluginsに以下を追加。
<plugin> <groupId>org.codehaus.gmaven</groupId> <artifactId>gmaven-plugin</artifactId> <version>1.4</version> <configuration> <providerSelection>1.8</providerSelection> <sourceEncoding>UTF-8</sourceEncoding> </configuration> <executions> <execution> <goals> <goal>generateTestStubs</goal> <goal>testCompile</goal> </goals> <configuration> <sources> <fileset> <directory>${pom.basedir}/src/test/java</directory> <includes> <include>**/*.groovy</include> </includes> </fileset> </sources> </configuration> </execution> </executions> <dependencies> <dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy-all</artifactId> <version>1.8.9</version> </dependency> </dependencies> </plugin>
これで、例えば mvn test
等を実行した際にGroovyが使えるようになります。
日本語文字化け問題
上記のplugin -> configuration -> sourceEncoding で文字コードを指定していますが、実はこれは効きません。gmavenのJIRAを見ても解決済みとなっていますが、実行すると.groovyに日本語が含まれている場合はコンパイルエラーとなってしまいます。
回避策としては、Windowsの場合、以下のように環境変数を設定してやると上手くいきますが、なんとも釈然としないですね...。
set MAVEN_OPTS="-Dgroovy.source.encoding=UTF-8"
参考にさせて頂いたページ
Team Geek
Team Geek ―Googleのギークたちはいかにしてチームを作るのか
- 作者: Brian W. Fitzpatrick,Ben Collins-Sussman,角征典
- 出版社/メーカー: オライリージャパン
- 発売日: 2013/07/20
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (18件) を見る
正直もっと早く読めば良かったと思える本です。例えばこちらのブログ、小野和俊のブログ:HRTの原則 〜ソフトウェア開発はバーでしっとり語り合うように 〜にも書かれていますが、言っていることが正しくとも言い方一つで人間関係は大きく変化してしまいます。(悪い方への変化は特に簡単です) そこでお互いが謙虚に、相手を尊敬・信頼して接するようになれば、少なくとも悪い方向には進まないんじゃないかと思います。
かくいう自分もHRTを欠いた振る舞いのせいでチームの人との人間関係を悪化させてしまったことがありますし(これは今でも後悔してます)、逆に本書の言うところの「悪いマネージャー」に遭遇してストレスで身体を壊してしまったこともあります。
コンピュータを扱う仕事とはいえ、最後は人対人であることを肝に銘じて、今後はチームを良い方向に進められていければなと思います。
jUnit実践入門
JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)
- 作者: 渡辺修司
- 出版社/メーカー: 技術評論社
- 発売日: 2012/11/21
- メディア: 単行本(ソフトカバー)
- 購入: 14人 クリック: 273回
- この商品を含むブログ (67件) を見る
チーム開発実践入門 ~共同作業を円滑に行うツール・メソッド (WEB+DB PRESS plus)を読み終わったので、引き続きjUnit実践入門を読み始めます。もうすぐ次のプロジェクトが始まるのでそこではTDDを導入していければと思ってます。今でもテストは書いてますが、先にテストを書くというのが徹底できていないので。
チーム開発実践入門を読んでみて
本に書かれている「こうあるべき」という部分といまのプロジェクトを比較しながら読んでみると色々足りてない部分が明確になったので、次のプロジェクトではなるべく取り入れていきたいと思います。今の現場で、書かれているツール等を導入するのはそんなにハードルは高くないはず、というか多分他のチームで導入しているものがあるはずなので、そこは上手く情報を共有して貰いながらやっていければ良いかなと思います。
第3章まで読了。DBマイグレーションの部分がちょっと理解できてないのはこの類のツールを使ったことがなくてどういう動きをするのか分かってないからだと思う。とりあえず先に進もう。
— ナミキマサトシ[撃沈] (@m_namiki) 2014, 4月 19
寝て起きて5章まで読了。チケット管理はやれてるしsvnとも連携させてるから問題ないかな。ただこの前保守・運用の部分に問題がありそうなのが分かったので、そこを上手くチケットベースに変えていけるといいかもしれない。
— ナミキマサトシ[撃沈] (@m_namiki) 2014, 4月 19
CIは今のプロジェクトではやれてないけど、次のプロジェクトでは必ず導入しよう。基本的にビルドはMavenだしテストコードもちゃんと書いていくので。っていうか前のプロジェクトでは導入してたんだよな。
— ナミキマサトシ[撃沈] (@m_namiki) 2014, 4月 19
6章、7章読了。今のプロジェクトだとデプロイ自動化はちょっと難しそうだけどweb側はやりたい。LBAをこっちがコントロールできるなら。
— ナミキマサトシ[撃沈] (@m_namiki) 2014, 4月 20
リグレションテスト、いまはデバッガさんたちにお願いしてるけど、これも自動化していきたいところ。
— ナミキマサトシ[撃沈] (@m_namiki) 2014, 4月 20
で、Twitterでも呟きましたが、DBマイグレーションの部分があんまりよく理解できていないです。そもそもどういう動きをするのかとか、嬉しい部分は何なのかとかがイマイチ分かっていないので、ここはもうちょっと周辺情報を漁ってみたいと思います。
Sublime Textの教科書とチーム開発実践入門
Web制作者のためのSublime Textの教科書 今すぐ最高のエディタを使いこなすプロのノウハウ
- 作者: 上野正大,杉本淳,前川昌幸,森田壮,こもりまさあき
- 出版社/メーカー: インプレスジャパン
- 発売日: 2014/03/20
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
チーム開発実践入門 ~共同作業を円滑に行うツール・メソッド (WEB+DB PRESS plus)
- 作者: 池田尚史,藤倉和明,井上史彰
- 出版社/メーカー: 技術評論社
- 発売日: 2014/04/16
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (2件) を見る
このところSublimeTextを使っていて、各所で絶賛される意味が分かってきたのでもうちょっと便利な使い方はないかなと思い買ってみました。主にMarkdownとRubyを書くときに使っていますが、まだちゃんと使いこなせていないので。
チーム開発実践入門の方は 著者の池田さんがブログで仰ってた内容に惹かれて買ってみました。以下当該部分を引用します。
むしろのこの『チーム開発実践入門』という本は、『GitHub実践入門』、『Jenkins実践入門』、『JUnit実践入門』といった書籍の間をつなぐミッシングリンクのような本です。
最近現場で段々とチームメンバーが増えており、プロジェクトを上手に推進させていくにはどうしたら良いかと考えることが多くなってきたので、ここらでチーム開発とはなんたるかをもう一度しっかりと考えてみたいと思います。
チケット駆動開発とGit逆引き入門
- 作者: 小川明彦,阪井誠
- 出版社/メーカー: 翔泳社
- 発売日: 2012/08/24
- メディア: 大型本
- クリック: 31回
- この商品を含むブログ (7件) を見る
- 作者: 松下雅和,船ヶ山慶,平木聡,土橋林太郎,三上丈晴
- 出版社/メーカー: シーアンドアール研究所
- 発売日: 2014/04/09
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (4件) を見る
チケット駆動開発はだいぶ前に買っていましたが載せてなかったので。最近サービスの保守・運用でうまく情報共有できていないことが発覚したため、開発チームだけではなく関係者全員(企画や他のプロジェクトも含めて)でチケットベースでやりとりが出来ないかな、と考えています。今は情報が散見していて、一目でいまこのサービスに何が起こっているのか、過去にどんなことが起こっていたのかが分からず、また不具合があっても担当者間でのメールベースのやりとりで終わってしまっているので、これをもうちょっとどうにかしたいと思ってます。開発チーム内ではチケット駆動が上手くいってきているので、うまく流用というかパターンに組み込めると良いなと思ってるんですが、はてさてどうなるか。
Git逆引き入門はまだイマイチやりたいことと実際のコマンドが結びつかないことがあるので、あったら便利そうだと思い買ってみました。で、本に書かれているSourceTreeというソフトが便利そうだったので早速入れてみようと思います。個人的にはCUIの方が好きなんですが、チームに導入しようとするとGUIの方が受け入れられやすいんですよね。SourceTreeには専用ターミナルも付属しているとのことなので、CUI/GUIのどちらでも使えるというのはいいなと思います。
ntpの導入をTDDライクにやってみる
serverspecでテストを実行できるようになったので、ntpの導入をTDDライクに進めながらやってみようと思います。まずはテストケースから。
describe package('ntp') do it { should be_installed } end describe file('/etc/ntp.conf') do it { should be_file } its(:content) { should match /server ntp.nict.jp/} end describe service('ntpd') do it { should be_enabled } it { should be_running } end
実行してみると当然失敗します。
Failed examples: rspec ./spec/default/base_spec.rb:63 # Package "ntp" should be installed rspec ./spec/default/base_spec.rb:67 # File "/etc/ntp.conf" should be file rspec ./spec/default/base_spec.rb:68 # File "/etc/ntp.conf" content should match /server ntp.nict.jp/ rspec ./spec/default/base_spec.rb:72 # Service "ntpd" should be enabled rspec ./spec/default/base_spec.rb:73 # Service "ntpd" should be running C:/Ruby193/bin/ruby.exe -S rspec spec/default/base_spec.rb failed
次にレシピですが、きっとopscodeにあると思って検索してみたらやっぱりありました。なのでこちらを使ってみます。まずはBerksfileに追加。
# chef-repo/Berksfile site :opscode (中略) cookbook 'ntp'
で、インストール。
berks install --path cookbooks
これでcookbooksにntpが追加されたので、個別のパラメータを追加しています。設定はattributes/default.rbに行います。serversを追加するのと同期を行うような設定を追加します。serversは公開NTPサーバを2つ追加しておきます。
# default attributes for all platforms #default['ntp']['servers'] = [] # The default recipe sets a list of common NTP servers (COOK-1170) default['ntp']['servers'] = ['ntp.nict.jp', 'ntp.jst.mfeed.ad.jp'] # The default recipe sets a list of common NTP servers (COOK-1170) default['ntp']['peers'] = [] default['ntp']['restrictions'] = [] # internal attributes (中略) #default['ntp']['sync_clock'] = false default['ntp']['sync_clock'] = true
ここまで来て気付いたんですが、タイムゾーンの設定を行わなきゃダメそうです。CentOSの場合、タイムゾーンの設定は/etc/sysconfig/clockを書き換えてやれば良さそうなので、まずは今の状態を確認しておきます。
[vagrant@localhost ~]$ cat /etc/sysconfig/clock ZONE="UTC"
で、これを下記のように書き換えるようにして、レシピのtemplates/default/clock.erbとして保存しておきます。
# clock.erb ZONE="Asia/Tokyo" UTC="false"
次にrecipes/default.rbをこのファイルを使うように編集します。
file "/etc/localtime" do content IO.read("/usr/share/zoneinfo/Asia/Tokyo") action :nothing end template "/etc/sysconfig/clock" do source 'clock.erb' owner 'root' group 'root' mode '0644' notifies :create, resources(:file => "/etc/localtime"), :immediately end log "done copy to /etc/sysconfig/clock."
clock.erbを書き換えた後に/usr/share/zoneinfo/Asia/Tokyoを/etc/localtimeにコピーするようにしてみました。executeリソース側でacitonが:nothingとなっているのはtemplateリソースから呼ばれたときだけ実行されるようにです。
最後にVagrantfileにntpのレシピを使うように定義します。
config.vm.provision "chef_solo" do |chef| chef.cookbooks_path = ["./chef-repo/cookbooks", "./chef-repo/site-cookbooks"] chef.add_recipe "yum" chef.add_recipe "rbenv" chef.add_recipe "ruby_env" chef.add_recipe "nodejs" chef.add_recipe "ntp" end
これでvagrant provision
を実行します。
vagrant provision INFO: template[/etc/sysconfig/clock] backed up to /var/chef/backup/etc/sysconfig/clock.chef-20140402081837.835605 INFO: template[/etc/sysconfig/clock] updated file contents /etc/sysconfig/clock INFO: template[/etc/sysconfig/clock] sending createaction to file[/etc/localtime] (immediate) INFO: file[/etc/localtime] backed up to /var/chef/backup/etc/localtime.chef-20140402081838.206681 INFO: file[/etc/localtime] updated file contents /etc/localtime INFO: done copy to /etc/sysconfig/clock. INFO: package[ntp] installing ntp-4.2.6p5-1.el6.centos from base repository INFO: cookbook_file[/etc/ntp.leapseconds] created file /etc/ntp.leapseconds INFO: cookbook_file[/etc/ntp.leapseconds] updated file contents /etc/ntp.leapseconds INFO: cookbook_file[/etc/ntp.leapseconds] owner changed to 0 INFO: cookbook_file[/etc/ntp.leapseconds] group changed to 0 INFO: cookbook_file[/etc/ntp.leapseconds] mode changed to 644 INFO: template[/etc/ntp.conf] backed up to /var/chef/backup/etc/ntp.conf.chef-20140402171846.501042 INFO: template[/etc/ntp.conf] updated file contents/etc/ntp.conf INFO: execute[Stop ntpd in preparation for ntpdate]ran successfully INFO: execute[Stop ntpd in preparation for ntpdate]sending stop action to service[ntpd] (immediate) INFO: execute[Force sync system clock with ntp server] ran successfully INFO: service[ntpd] enabled INFO: service[ntpd] started INFO: template[/etc/ntp.conf] sending restart action to service[ntpd] (delayed) INFO: service[ntpd] restarted INFO: execute[Force sync system clock with ntp server] sending start action to service[ntpd] (delayed) INFO: Chef Run complete in 60.733069975 seconds INFO: Running report handlers INFO: Report handlers complete
templateとファイルコピーが正常に行われ、その後にntpのインストールが動作しているのが分かります。続いてテストの実行ですが、タイムゾーンの設定を追加したのでテストケースも追加しておきます。また公開NTPサーバも2つにしたのでそれのテストも増やします。
describe file('/etc/sysconfig/clock') do it { should be_file } its(:content) { should match /ZONE="Asia\/Tokyo"/ } its(:content) { should match /UTC="false"/ } end describe file('/etc/ntp.conf') do it { should be_file } its(:content) { should match /server ntp.nict.jp/} its(:content) { should match /server ntp.jst.mfeed.ad.jp/} end
うーん、2行になってしまっていますが、この書き方で良いんでしょうか。もうちょっとスマートな方法がありそうですがここはこのまま実行してみます。
rake spec C:/Ruby193/bin/ruby.exe -S rspec spec/default/base_spec.rb ......................... Finished in 33.55 seconds 25 examples, 0 failures
無事テストが通ってntpのインストールとタイムゾーンの設定がうまくいったようです。最後に実際に時間が変更されているか確認します。
[vagrant@localhost ~]$ date Wed Apr 2 22:54:12 JST 2014
おぉ、タイムゾーンも変更されて時間もちゃんと設定されました:-) ただあんまりTDDライクに進められた気がしないので、次はもうちょっとちゃんと進められたらいいなぁ。
参考にさせて頂いたページ
digをインストールする
チュートリアル環境にnslookup
もdig
も入っていなかったのでインストールしておきます。
[vagrant@localhost ~]$ dig -bash: dig: command not found [vagrant@localhost ~]$ nslookup -bash: nslookup: command not found [vagrant@localhost ~]$
[CentOS] digコマンドをインストールするの巻 - TrippyBoyの愉快な日々 を参考にすると、bind-utilsを入れればいいことが分かったのでレシピとテストケースに追加します。
# chef-repo/site-cookbooks/ruby_env/recipes/default.rb package "bind-utils" do action :install end
# spec/default/base_spec.rb describe package('bind-utils') do it { should be_installed } end
あとはvagrant provision
してテストケースを実行するだけ。
cd rails3_tutorial vagrant provision rake spec C:/Ruby193/bin/ruby.exe -S rspec spec/default/base_spec.rb ................ Finished in 33.19 seconds 16 examples, 0 failures
テストが通ったので実際に確認。
[vagrant@localhost ~]$ which dig /usr/bin/dig [vagrant@localhost ~]$ which nslookup /usr/bin/nslookup
無事にインストールできました:-)
あとどうでも良いことですが、記事の書き方をはてな記法からmarkdown記法に変更してみました。GitHubでも使えるし覚えておく必要があるかなと。この書き方はかなり楽ですね。改行が半角スペース2つっていうのが微妙に面倒ですが。