2015年10月17日土曜日

Windows10でRedPen (CLI) を使う

マニュアルなどの文書チェックツールRedPenをWindows10のコマンドラインから使う方法について記載する。Windows環境で動かそうとしたら2, 3引っかかったので、その解消方法メモ。あと、オマケとしてサクラエディタで編集中のファイルをチェックする方法も。

利用環境は次のとおり。
  • RedPen 1.4.0
  • Windows 10
  • Java 1.8.0 update 45

1. インストール

Java 1.8.0以降をインストールしておく。環境変数JAVA_HOMEも設定しておくこと。

リリースページから、redpen-1.4.0.tar.gzをダウンロードして、任意のフォルダに展開する。以降、展開したフォルダをINSTALL_DIRと表記する。

2. 微調整

そのまま実行すると次の2つの問題があるので、微調整する。
  • チェックメッセージが文字化けしている。
  • カレントディレクトリにjsフォルダが作成される。
文字化けを解消するには、INSTALL_DIR\bin\redpen.batの12行目に-Dfile.encoding=UTF-8を追加する。これでチェックメッセージの文字化けが解消する (コマンドプロンプト上だと、代わりに実行ログが文字化けしてしまうけれど許容している)(完全には解決できなかったのが気持ち悪いけれどIssueを出してみた)。
set JAVA_OPTS=%JAVA_OPTS% -Dfile.encoding=UTF-8
jsフォルダの作成を抑止するには、設定ファイルINSTALL_DIR\conf\redpen-conf-ja.xmlの<validator name="JavaScript" />を次のように書き換える。JavaScriptで実装されたチェックルールを使う予定がないなら、代わりに消しても良い。
<validator name="JavaScript">
    <property name="script-path" value="INSTALL_DIR\js" />
</validator>
メモ: Writing RedPen extension with JavaScript | RedPen BLOGRedPenのValidatorをJavaScript で書くには - ククログ(2015-08-29)を読むと、プロパティscript-pathが優先で、未指定なら環境変数REDPEN_HOME\jsを参照しそうなものだけれど、未指定だとカレントディレクトリ\jsを参照しようとして存在しないときは作成してしまうみたい (このコミットを見ると、次のヴァージョンではログが出るだけになりそう)。詳しい原因は不明。環境変数REDPEN_HOMEはredpen.bat内で設定されているから、設定漏れではなさそう。redpen.batにecho %REDPEN_HOME%を追記してみたら、ちゃんとINSTALL_DIRが表示されるているし (ForkしてUnit Testを書いてみたけれどNGだったので、Issueを出してみた)。

3. 実行確認

任意のディレクトリ (微調整の確認のため、最初はINSTALL_DIR以外がよい) でコマンドプロンプトを実行し、次のコマンドを実行する。
INSTALL_DIR\bin\redpen -c INSTALL_DIR\conf\redpen-conf-ja.xml INSTALL_DIR\sample-doc\ja\sampledoc-ja.txt
下記のような実行結果が表示されるはず。
(前略)
[2015-10-17 20:15:52.673][INFO ] cc.redpen.validator.JavaScriptValidator - JavaScript validators directory: INSTALL_DIR\js
sampledoc-ja.txt:1: ValidationError[SentenceLength], 文長("101")が最大値 "100" を超えています。 at line: 最近利用されているソフトウェアの中には複数の計算機上で動作 (分散)するものが多く存在し、このような分散ソフトウェアは複数の計算機で動作することで大量のデータを扱えたり,高負荷な状況に対処できたりします。
(以下略)

(オマケ) サクラエディタで編集中のファイルのチェック

[ツール] > [外部コマンドを実行]で、下記の通り設定して[実行]ボタンを押すと、編集中のファイルをチェックできる。一度実行すれば、Ctrl+F, Enterでチェックできて便利。
名前: "INSTALL_DIR\bin\redpen.bat" -c "INSTALL_DIR\conf\redpen-conf-ja.xml" "$F"
標準出力を得る: チェックボックスをオンにして"UTF-8"を選ぶ。
標準出力リダイレクト先: アウトプットウィンドウ
ちなみに、アウトプットウィンドウ上だと実行ログもチェックメッセージも文字化けしない。

2015年8月9日日曜日

テストデータを管理する方針

『継続的デリバリー』の、テストデータの管理についての記述を整理してみた。該当する章節は、
  • 12.5 テストデータを管理する
  • 12.6 データの管理とデプロイメントパイプライン
それから12.5から参照されている、
  • 8.5.1 受入れテストにおける状態
の3箇所。特に断りの無い限り、『継続的デリバリー』のいう受入れテストのデータ管理を念頭に置いている。

テストデータの管理で問題となるのは、1) パフォーマンス、2) テストの分離の2つ。xUTPだとそれぞれSlow TestsSeparation of Concernsに対応しているように思う。

どちらの問題でも、真っ先に原因として挙がるのがデータベース。ユニットテストならデータベースアクセス移譲先 (デザインパターンDAO相当) をテストダブルで置き換えられる。これはパフォーマンスにも分離にも効く。DBの状態も含めてテストしたい場合、インメモリDB (H2, SQLite, JavaDBなど) を使うこともできる。

データベースに限らず、テストとデータのつながりを管理するアプローチには、次の3つがある。
  1. テストの分離: 各テスト用のデータは、そのテストからしか見えないようにする。
  2. 順応型テスト: テストがデータ環境を調べ、実際のデータに合わせて振る舞うようにする。
  3. テストの順序づけ: テストの実行順序を予め決めておいて、一つ前のテストの出力を次のテストの入力とする。
スケールするのは1だけだと言っている。3は分かりやすい無理ゲー。こんな強く依存していたらスケールさせられない。2は一見良さげに見えるけれど、分離しきれていないと、別のテストで実際のデータが思いもよらない状態になっていることがある。xUTPでもTest SmellsとしてConditional Test Logicが挙がっている。

「テストの分離」のためには、まずテスト終了時にテスト前の状態に戻すとある。そうしないと、次のテストに今のテストのデータが見えてしまう。ただこれにはオプションもある。『システムテスト自動化標準ガイド』や『実践テスト駆動開発』では、テスト対象が登録したデータはテスト終了時に消さずに、テスト開始時に消すことを勧めている。もう一つの方法は、データを機能分割すること。これができるかどうかは、テスト対象の特性に強く依存する。

データを分割し、巨大で複雑なデータ構造への依存を減らすには、まずデータを整理しないといけない。著者はこんな風に言っている。
何よりもまず、プロダクションデータのダンプを取得して受入れテスト用にテストデータベースに投入したいという誘惑に負けないこと。
統制のとれた最小限のデータセットを保守しよう。
整理の取っかかりとなるのが、次の3種類のデータの区別。
  1. テスト固有のデータ: 一意でなければならない。テストの分離の手段になる。
  2. テストが参照するデータ: テストには関係するがふるまいには影響しないデータ。そこら中で使われるマスタデータ類を指していると思う。
  3. アプリケーションが参照するデータ: アプリケーションを立ち上げるのに必要なデータ。

一言で言うと、正しく動くと分かっている(契約による設計の言葉を使うと、事前条件を満たしている)開始位置を特定し、テスト開始時にその状態を復元する。言ってしまえばこれだけなんだけれど、そのためにはテスト対象とテストデータをよく理解する必要がある。それも一部のテストだけじゃない。うまくテストを分離するには、全体を俯瞰する必要がある。

なお、その開始位置を復元するのには、アプリケーションのAPIを利用するよう勧めている。理由は3つ。
  • システムを矛盾した状態に持ち込ませない。
  • データベースやAPIのリファクタリングの影響を避けられる。
  • APIのテストにもなる。
できないときは、事前条件をアサートする防御的なテストコードにしたり、アサートを相対的(例えば、レコードが3件あることではなくて、3件増えたことにする)にしたりする。

References


2015年8月2日日曜日

JUnitのRunner (Enclosed, Theories, Categories) を併用したときの動き

JUnit4の次のRunnerの関係を整理してみた。なお、JUnitのバージョンは4.12。
  • Enclosed
  • Theories
  • Categories

論理的にテストクラスを分類しつつ、並列化するための分類もしたくなることがあるので、JUnitの仕組みでどこまでできるか知っておきたくて。

では、早速。

EnclosedとTheoriesは併用できる。外部クラスEnclosingTheoryをJUnit実行すれば、内部クラスEnclosedTheoryがJUnit実行される。これでTheoriesを使いたいけれど、テストクラスを分けたくない場合は大丈夫。
@RunWith(Enclosed.class)
public class EnclosingTheory {
    @RunWith(Theories.class)
    public static class EnclosedTheory {
        @Theory
        public void testTheory(Fixture f) throws Exception {
            // test method.
        }
    }
}

EnclosedとCategoriesは併用できない。外部クラスEnclosingTheoryにカテゴリOuterを付けて、Categoriesを使ったテストスイートを実行しても、テストが見つからない。
@RunWith(Enclosed.class)
@Category(Outer.class)
public class EnclosingTheory {
    @RunWith(Theories.class)
    public static class EnclosedTheory {
        @Theory
        public void testTheory(Fixture f) throws Exception {
            // test method.
        }
    }
}
@RunWith(Categories.class)
@IncludeCategory(Outer.class)
@SuiteClasses(EnclosingTheory.class)
public class CategorizedTestSuite {
    // NoTestsRemainException is thrown.
}

ただ、内部クラスEnclosedTheoryを直接指定すれば、実行させられる。Enclosedの甲斐がないと見るか、互いに独立した分類が使えると見るか、悩ましい。
@RunWith(Enclosed.class)
public class EnclosingTheory {
    @RunWith(Theories.class)
    @Category(Inner.class)
    public static class EnclosedTheory {
        @Theory
        public void testTheory(Fixture f) throws Exception {
            // test method.
        }
    }
}
@RunWith(Categories.class)
@IncludeCategory(Inner.class)
@SuiteClasses(EnclosedTheory.class)
public class CategorizedTestSuite {
    // run testTheory.
}

上記でテストが実行されるので、CategoriesとTheoriesは併用できることが分かる。

調べて見ると、CategoriesとEnclosedはそれぞれSuiteのサブクラスだった。併用できないのもさもありなん。

以下は、調べながら考えたことをつらつらと。

こうして調べて見ると、Categoryアノテーションで並列化のための分類をするのは筋が悪い気がしてきた。MECEに分割したいのだけれど、アノテーションの付け忘れやテストスイートへの追加忘れがありそう。何並列にするかによるけれど、上位のパッケージ構成でざっくり割っちゃった方が安全かなぁ。

CIとの相性も考える必要がある。AntのJUnitタスクから実行するなら、パッケージ構成やファイル命名規約に加えてFileSetで色々できる(開発端末上では使えないけれど)。一方、MavenはCategoryにも対応している。そろそろAntから卒業した方がいい気がしてきた……。

なお、調べるために書いたコードはso-c/junit4.12-categories-configuration-sampleにアップしてある。ここに書いたスニペットよりゴチャゴチャしているけれど、ちゃんと動くのでこれはこれで。

2015年8月1日土曜日

GitHubにPJを作成し、Eclipse (EGit) でCloneし、Mavenプロジェクトにする

EclipseプロジェクトをEGitでGitHubにPushするの手順がいまいち使いにくかったので、EclipseをMarsにしてMavenを使ったやり方を整理してみた。まだ気になるところはあるけれど、一旦こんなところで。

GitHub上にリポジトリを作る

GitHubにログインして、右上の"+"メニューから"New Repository"を選び、リポジトリ作成画面を表示させる。必要な項目を入力して、"Create repository"ボタンを押すと、リポジトリが作られる。

入力内容によってREADME.mdなんかが出来たり出来なかったりするけれど、この投稿はファイルができているケースを想定して続ける。

Eclipseとリポジトリを連携する

httpsプロトコルで連携する。sshでも連携できるけれど、その場合は事前に公開鍵を作成しGitHubに登録しておく必要がある。

Windows > Show View > Git Repositoryを選び、そのビューを表示させる。"Clone a Git Repository"を選び、Location > URIにGitHubリポジトリの"HTTPS Clone URL"を入力する(リポジトリ初期表示時はHTTPSではなくSSHなので、HTTPSリンクを押して切り変える)。Hostなどが自動的に埋められるので、AuthenticationにGitHubのユーザとパスワードを入力し、"Next"ボタンを押す。Local Destinationを選んでFinishを押すと、そこにリポジトリのCloneができる。

リポジトリからEclipseにプロジェクトをimportする

EclipseのGit Repositoryビューで、リポジトリを右クリックして、"Import Projects"を選ぶ。"Import as general project"を選んで、"Next"ボタン、"Finish"ボタンを順に押していくとimportされる。

general projectを選んでいるのは、リポジトリを作成するときにファイルが作られているから。こうしてからこの後にMavenプロジェクトにするのと、完全に空にしておいてこの時に新しいMavenプロジェクトとして作成するのとどちらがベターなんだろう。

ここで.gitignoreにgitignore/Global/Eclipse.gitignoreを追加しておくと後から誤コミットをしなくて済む。.classpathなどなくてもこの後Maven化するので問題ない。

インポートしたプロジェクトをMavenプロジェクトにする

importしたプロジェクトを右クリックして、Configure > Convert to Maven Projectを選ぶ。

J2SEに関するWarining "Build path specifies execution environment J2SE-1.5. There are no JREs installed in the workspace that are strictly compatible with this environment."が出る。pom.xmlに下記を追記して、Maven > Update Projectで解消すいる(参考:Maven Project を作成すると警告が出る - 電卓片手に)。
<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>2.3.2</version>
      <configuration>
        <source>1.8</source>
        <target>1.8</target>
      </configuration>
    </plugin>
  </plugins>
</build>

Mavenで依存するライブラリを追加する

右クリックして Maven > Add Dependenciesを選び、からライブラリを検索して追加すると、Maven Dependenciesに追加される。

あとは

好きなようにコードを書けばOK。ただし、Mavenのarchitypeを使っていないので、ソースフォルダなどは手で作らないといけない。GitHubのリポジトリは空にして、新しいプロジェクトを作ってimportすればarchitype使えそうだけれどどちらが楽だろうか。

参考

2015年2月18日水曜日

継続的テスト、継続的インテグレーション、継続的デリバリー

最近、テストとビルドの関係について考え直している。『継続的デリバリー』を読み始めて少しだけまとまってきたので、整理を兼ねてメモしておく。

結論から言うと、テストとビルドの結合はこれまで考えていたよりずっと密だった。継続的インテグレーション/継続的デリバリーは継続的テストを含んでいる。スピードを上げるには〈デプロイメント・パイプライン〉を実装する必要がある。

なお〈デプロイメント・パイプライン〉は『継続的デリバリー』の中核となるパターンで、今のところ、インスペクション、コンパイル、ユニットテスト、ビルド、デプロイ、インテグレーションテスト、システムテスト、リリースなど一連のプロセスをうまく組み合わせることだと理解している。

ただ、組み合わせる範囲が広過ぎて、どこからどう手を付けたらいいのやら。『継続的デリバリー』の続きにヒントがあると期待しているのだけれど。

さて、以降はこの考えに至るまでに考えたあれやこれや。

テストとビルドは切っても切り離せない。そもそも正しい構成のテストがテスト環境にデプロイされていないと、テストする意味が無い。『ソフトウェアテスト 293の鉄則』でも、ビルドに関する鉄則が出てくる。
鉄則170 ビルドスケジュールを調整せよ
鉄則171 ビルド引き渡し前のプログラマの仕事を把握せよ
鉄則172 ビルドの受け入れ準備は怠るな
鉄則173 ときにはビルドのテスト受け入れを拒否することも大切だ
鉄則174 まずはスモークテストで判断せよ

一方で、テスターがビルドしているイメージはない。ビルドしてデプロイするのは別の役割に思える。テスターがするとしても全員ではないだろう。テストレベルが上がれば、テスターの人数が試験環境数を上回る。

〈テスト自動化〉と言ったときも、ビルドの自動化までは想像しないことが多いと思う。Javaでよく使うツールでいうとJUnitやSeleniumとかが真っ先に思い浮かんで、Antやmavenまではなかなか辿り着かない。

そのせいか、〈継続的インテグレーション〉とか〈継続的デリバリー〉と聞くと、ビルドやデプロイのことが真っ先に思い浮かんで、今度はテストがなおざりになる。

でも〈継続的インテグレーション〉について改めて『継続的インテグレーション入門』を読み返したりしてみると、テスティングやインスペクションまで含んでいる。継続的インテグレーションのベストプラクティスにも、Make the build self-testingが並んでいる。正しくビルドできたことを、テストで確認する必要があるからだと思う。

つまり、テストから見ても、ビルドから見ても、お互いに強く必要としあっている。

という論理的な帰結は論理的な帰結として、実際的な問題としてインスペクションしてコンパイルしてユニットテストしてビルドして、データベースにデータ準備して実行環境を構築して、そこにデプロイしてインテグレーションテストやシステムテストしてリリースしようなんて、膨大なスキルセットを持つユニコーンか、せめてそれぞれのエキスパートと全体を見渡すアーキテクト的なリーダを務められる人が集まらなきゃできない。

それぞれどれも大変なのに、一貫した効率的なデプロイメント・パイプラインなんて、どうしたらうまく実装できるんだろうか。

References