一覧からテンプレートに転記するVBA
Gitを復習する
プロジェクトでGitを使うことが多いですが、SVN育ちのためプル・プッシュ以外のGit操作がよく分かっていませんでした。
「こわいよ Git」状態だったので、Gitについて改めて復習しました。
Git復習に役立ったサイト
以下のサイトでGitを復習しました。
・Gitとは
サルでもわかるGit入門 〜バージョン管理を使いこなそう〜【プロジェクト管理ツールBacklog】
・ブランチ・マージ・リベースについて
こわくない Git
・リベースはやめた方がいい理由
なぜ git rebase をやめるべきか - Frasco
・ブランチ戦略
見えないチカラ: A successful Git branching model を翻訳しました
・EclipseでのGit操作
Eclipse EGit の使い方(1/2) | hiromasa.another :o)
EclipseでのGit操作
今のプロジェクトでEGitを使っているので、EclipseでのGit操作を試してみました。(メモベースですみません。。)
マージ
- ブランチAからブランチBを作成
- ブランチBでファイルAを修正してコミット
- マージ
- ブランチAで何もコミットしないで、ブランチBをマージ → Fast-Forwardでマージされる(マージコミットは作られない)
- ブランチAでファイルBを修正してコミットし、ブランチBをマージ → Non Fast-Forwardでマージされる(マージコミットが作られる)
- ブランチAでファイルAを修正してコミットし、ブランチBをマージ → 競合が発生するので、手動でマージする(マージコミットが作られる)
Eclipse EGit の使い方(2/2) | hiromasa.another :o)
ブランチからのマージ1
コンフリクトの解消
逆マージ
- ブランチAからブランチBを作成
- ブランチAでファイルAを修正してコミット
- ブランチBでファイルAを修正してコミット
- ブランチBでブランチAをマージ(逆マージ) → 競合が発生するので、手動でマージする(マージコミットが作られる)
- ブランチAでブランチBをマージ(再マージ) → Fast-Forwardでマージされる(マージコミットは作られない)
Git の疑問。トピックブランチで作業中に、master ブランチで重要な変更が加えられた。どうすればよい? - 彼女からは、おいちゃんと呼ばれています
プルリク競合
- 逆マージ → 競合が発生するので、手動でマージする(マージコミットが作られる)
- マージコミットをプッシュ
軽微な競合であれば、GitHub上で直接修正するのもありです。
Resolving a merge conflict on GitHub - User Documentation
プル
- 別ユーザでファイルAを修正してコミット&プッシュ
- プル
- ローカルで何もコミットしないで、プル → Fast-Forwardでマージされる(マージコミットは作られない)
- ローカルでファイルBを修正してコミットし、プル → Non Fast-Forwardでマージされる(マージコミットが作られる)
- ローカルでファイルAを修正してコミットし、プル → 競合が発生するので、手動でマージする(マージコミットが作られる)
- ローカルでファイルAを修正してコミットしないで、プル → 競合が発生するので、未コミットファイルをスタッシュする
git pull を強制し、リモートでローカルを上書きする方法 | WWWクリエイターズ
git pull について、おさらい
EGit逆引きリファレンス(ブランチ操作関連) - Qiita
git stash
git stash apply
コミットの書き換え
直前のコミットを修正する
commit --amend
- Gitステージングビューで「コミット・メッセージ」の右横にある「修正(前のコミットを編集)」をクリック
- 「コミット・メッセージ」にコミットコメントが表示されるので、コメントを修正したい場合は修正
- コミットファイルを追加したい場合は対象ファイルをステージング
- コミットファイルを修正したい場合は対象ファイルを修正してステージング
- コミットボタン押下
過去のコミットを打ち消す
revert
- ヒストリービューから打ち消したいコミットを選択して右クリック
- 「コミットを戻す」をクリック
コミットを捨てる
reset
- ヒストリービューから戻りたいコミットを選択して右クリック
- リセット > ソフト or 混合 or ハード を選択
※消したいコミットではなく、戻りたいコミット(消したいコミットの1つ前のコミット)を指定すること
コミットを抜き取る
cherry-pick
- ヒストリービューで「すべてのブランチおよびタグを表示」をクリック
- チェリーピックしたいコミットを選択して右クリック
- 「チェリーピック」をクリック
コミットの履歴を書き換える
rebase -i
コミットコメントを修正する
- ヒストリービューから修正したいコミットの1つ前のコミットを選択して右クリック
- 「対話式リベース」をクリック
- 修正したいコミットを選択して「リワード」をクリック
- アクションが「REWORD」に変わったら「開始」をクリック
- コミットコメントを修正してOKボタン押下
コミットを修正する
- ヒストリービューから修正したいコミットの1つ前のコミットを選択して右クリック
- 「対話式リベース」をクリック
- 修正したいコミットを選択して「編集」をクリック
- アクションが「EDIT」に変わったら「開始」をクリック
- ファイルを修正して、索引に追加
- 必要であればコミットコメントを修正して、コミットボタン押下
- コミットが完了したら「継続」をクリック
複数のコミットを1つにまとめる
- ヒストリービューからまとめたいコミットを複数選択して右クリック
- 変更 > スカッシュを選択
- コミットコメントを入力してOKボタン押下
※キャンセルしてもスカッシュされてしまうので注意
ブランチ上のコミットを一つにまとめてマージする
merge --squash
- 対象プロジェクトを右クリック
- チーム > マージを選択
- マージするブランチを指定し、「マージ・オプション」で「スカッシュ」を選択し、マージボタン押下
- マージ対象ファイルがステージングされるので、「コミット・メッセージ」を入力して、コミットボタン押下
JUnit(SpringJUnit)のTips
JUnit(SpringJUnit)のTipsを随時更新していきます。
SpringJUnit4ClassRunner 対 SpringRunner
@RunWithの指定はSpringJUnit4ClassRunnerとSpringRunnerがありますが、機能的には全く一緒のため、名前が短いSpringRunnerがベターです。
Spring 4.3 テスト関連の主な変更点 - Qiita
Spring 4.3から、SpringJUnit4ClassRunnerの別名クラスとしてSpringRunnerクラスが追加されます。名前が短くてナイスです :thumbsup: 利用できる機能はSpringJUnit4ClassRunnerと全く一緒で、SpringJUnit4ClassRunnerも引き続き利用可能です。
ControllerテストのNestedServletException
ControllerのテストでControllerが例外をthrowした場合、NestedServletExceptionに例外がラップされて再throwされます。
SystemExceptionResolverを有効にしていないため、例外ハンドリングされずにNestedServletExceptionがサーブレットコンテナに通知される。 NestedServletExceptionのgetCauseメソッドにより取得された例外から、Controllerで期待した例外がthrowされていることを検証する。
@Valueのついたprivate変数の設定
テスト対象クラスの@Valueのついたprivate変数に値を設定する方法は以下の2つがあります。
- ReflectionTestUtils.setField()を使う
- @SpringBootTestでテストプロパティを読み込ませる
テストクラスの可読性向上
テストメソッド内で、フィクスチャのセットアップが長くて複雑になると、テストクラスが読みづらくなります。
テストクラスの可読性を向上させるためには、フィクスチャのセットアップが鍵になるので、セットアップ方法をまとめました。
テストフィクスチャとは
テストフィクスチャもしくはフィクスチャとはテストで扱うデータやオブジェクトの状態、テスト実行環境などを指します。
ユニットテストのフィクスチャには以下の要素が含まれます。
- テスト対象オブジェクト
- 入力値
- 期待値
- テスト対象オブジェクトが依存するオブジェクトの操作(状態)
- ファイルなどの外部リソース
- データベースなどの外部システム
- モックオブジェクト
セットアップパターン
フィクスチャをセットアップする方法は複数あり、テスト対象に合わせて適切なセットアップパターンを選択することが可読性の向上につながります。
インラインセットアップ
テストメソッド内でフィクスチャのセットアップを行います。
テストメソッド内でテストコードが完結するので、テストコードの見通しが良くなります。
セットアップが長くて複雑な場合はテストメソッドが長くなり、可読性が下がります。
暗黙的セットアップ
セットアップメソッド(@Before)でセットアップを行います。
セットアップメソッドは各テストメソッドが実行される前に暗黙的に実行されます。
テストメソッドはテストの実行と検証が中心になるため、何をテストするかが明確になります。
テストメソッドごとにセットアップメソッドを定義することはできませんが、Enclosedテストランナーを使用すれば、テストクラスを複数のクラスに構造化し、クラスごとにセットアップメソッドを定義できます。
生成メソッドでのセットアップ
各テストメソッド(テストクラス)で共通した初期化処理をメソッドに抽出してセットアップを行います。
外部リソースでのセットアップ
xmlやyamlにテストデータを設定し、ライブラリに読み込ませることでセットアップを行います。
長くて複雑なセットアップコードを消すことができますが、テストクラスとテストデータが分離されてしまいます。
外部リソースを使う場合はテストクラスからなるべく近いところに置くべきだと思います。
Groovyでのセットアップ
Groovyを使えば宣言的なコードでセットアップを行えます。
パラメータ化テスト
パラメータのバリエーションテストには、@Theoryと@DataPointを使ったパラメータ化テストが有効です。
セットアップパターンの使い分け
短くて単純なメソッドのテスト
インラインセットアップが有効です。
長くて複雑なメソッドのテスト
暗黙的セットアップが有効です。
生成メソッドと外部リソースでのセットアップはテストコードとテストデータが離れてしまうのがネックで、Groovyはプロジェクトによって使える使えないがあると思います。
セットアップのバリエーションが複数ある場合はEnclosedテストランナーと暗黙的セットアップの組み合わせが効果的です。
Enclosedテストランナーのメリット
- テストクラスを構造化できる
- クラスごとに暗黙的セットアップを定義できる
フィクスチャをクラス変数に保持する
プロダクションコードでは不用意にクラス変数を使うべきではないですが、テストコードではテストデータをクラス変数にするのはありだと思います。
以下の例では処理が単純過ぎるので効果はありませんが、実際の処理やテストデータは複雑になるので、テストデータをクラス変数に保持するとテストの実行と検証が楽になります。
public class SampleBeanTest { private SampleBean sampleBean; @Before public void setUp() throws Exception { sampleBean = new SampleBean("test"); } @Test public void test() { SampleBean2 actual = sampleMethod(sampleBean); assertThat(actual.getStr2(), is(sampleBean.getStr())); } public SampleBean2 sampleMethod(SampleBean sampleBean) { return new SampleBean2(sampleBean.getStr()); } public class SampleBean { private String str; public SampleBean(String str) { this.str = str; } public String getStr() { return str; } } public class SampleBean2 { private String str2; public SampleBean2(String str2) { this.str2 = str2; } public String getStr2() { return str2; } } }
ただし、クラス変数でテストコードを制御するのは可読性が下がるのでやめたほうがいいです。(クラス変数によってアサートを切り替えるとか)
参考
JUnit実践入門 ── 体系的に学ぶユニットテストの技法:書籍案内|技術評論社
第6章 テストのコンテキスト
第7章 テストフィクスチャ
第8章 パラメータ化テスト
JUnitのアサートの悩み
JUnitのアサートは基本的にisを使えば事足りますが、JavaBeanやListをアサートする場合に悩みます。
JavaBeanのプロパティやListの要素を1つずつアサートするのは面倒ですが、カスタムマッチャーを作るのも面倒だったり・作るほどでもない場合があります。
そうした場合にどうすべきか考えてみました。
isの注意点
org.hamcrest.CoreMatchers.is()は対象クラスのequalsメソッドでアサートを行います。
そのクラスがequalsメソッドをオーバーライドしていない場合、デフォルトのequalsメソッド、つまり、インスタンスの同一性をアサートします。
public class SampleBeanTest { @Test public void test() { SampleBean actual = new SampleBean("test"); SampleBean expected = new SampleBean("test"); assertThat(actual, is(expected)); // AssertionError } public class SampleBean { private String str; public SampleBean(String str) { this.str = str; } public String getStr() { return str; } } }
JavaBeanのアサート
org.hamcrest.Matchers.samePropertyValuesAs()を使えばJavaBeanごとアサートできます。
上記のサンプルコードはsamePropertyValuesAsを使えばアサートが成功します。
public class SampleBeanTest { @Test public void test() { SampleBean actual = new SampleBean("test"); SampleBean expected = new SampleBean("test"); assertThat(actual, is(samePropertyValuesAs(expected))); // OK } }
ただし、プロパティがJavaBeanの場合はインスタンスの同一性がアサートされるので注意が必要です。
@Test public void test() { SampleBean actual = new SampleBean("test", new SampleBean2("test2")); SampleBean expected = new SampleBean("test", new SampleBean2("test2")); assertThat(actual, is(samePropertyValuesAs(expected))); // AssertionError } public class SampleBean { private String str; private SampleBean2 sampleBean2; public SampleBean(String str, SampleBean2 sampleBean2) { this.str = str; this.sampleBean2 = sampleBean2; } public String getStr() { return str; } public SampleBean2 getSampleBean2() { return sampleBean2; } } public class SampleBean2 { private String str; public SampleBean2(String str) { this.str = str; } public String getStr() { return str; } } }
参考
JUnit4 で JavaBeans の assertThat を簡潔に書きたい
List(Stringとラッパークラス)のアサート
org.hamcrest.Matchers.contains()を使えば全ての要素を指定してアサートできます。
public class SampleBeanTest { @Test public void test() { List<String> actual = Arrays.asList("test1", "test2", "test3"); assertThat(actual, contains("test1", "test2", "test3")); // OK } }
ちなみにisでもアサートできました。。
public class SampleBeanTest { @Test public void test() { List<String> actual = Arrays.asList("test1", "test2", "test3"); List<String> expected = Arrays.asList("test1", "test2", "test3"); assertThat(actual, is(expected)); // OK } }
参考
JUnit4 で List の assertThat を簡潔に書きたい
List(JavaBean)のアサート
今のところ、よい方法が思いつかないです。
Listを要素ごとにアサートするのは面倒ですし、Listをfor文でアサートするのも良くないです。
ifやforなどのロジックはテストコードの可読性を下げますし、for文はどこでエラーになったのかがわかりづらいです。
ベターな方法は以下ですかね?
- リストサイズをアサート
- 要素ごとにsamePropertyValuesAsでアサート
それかカスタムマッチャーを作るかです。。
List
JUnit4/カスタムmatcherを作る [Ore Base]