技術の犬小屋

Webプログラミングを餌に生きる犬のメモ帳

Posts in the オブジェクト指向 category

仕事でPHPのユニットテストを書いている。普段はユニットテストの手法としてブラックボックステストを行っているが、他の方法も調べたのでメモを残しておく。
 

代表的なテスト手法

やみくもにテストを増やしても、費やした時間に対して効果的なテストをしているとは限らない。
ここで紹介する代表的なテスト手法に従ったテストを書くことで短時間で十分なテストを作ることが出来る。
 
ユニットテストには、主に大枠として以下の2つのテスト方針が存在する。

  • ホワイトボックステスト
  • ブラックボックステスト

 
以下より、それぞれのテスト方針の詳細とその中で行われるテスト手法を説明する。
 

ホワイトボックステスト

ホワイトボックステストとは、ソースコード中の文や分岐、条件などを実行してバグを見つけるテストである。実行してバグを見つけるので、どの文、分岐、条件も1回は実行する必要がある。つまり、ホワイトボックステストでは、テストで実行した文、分岐、条件の割合が、テストの進捗を計る指標となる。この割合を「カバレッジ率(網羅率)」という。
ホワイトボックステストでは、カバレッジ率が100%になるのに必要なだけテストケースを作成する。
また、テストで確認する対象(文、分岐、条件)により、ホワイトボックステストの指標は、ステートメントカバレッジ(命令網羅)、ブランチカバレッジ(分岐網羅)、コンディションカバレッジ(条件網羅)の3つに分類されている。
 

ステートメントカバレッジ(命令網羅)

ソースコード中にある全ステートメント(命令文)のうち、1回でもテストで実行されたステートメントの割合。ステートメントカバレッジの「ステートメント」とはコードの「行」のことである。ステートメントカバレッジでは「分岐」については考えず、if文がtrueであろうが、falseであろうが、とにかくその行が実行されれば良い。開発現場ではC0カバレッジと呼ばれる。
 

ブランチカバレッジ(分岐網羅)

ソースコード中にある全ブランチ(分岐)のうち、1回でもテストで実行された分岐の割合。分岐で条件全体が成立したとき(trueのとき)と不成立のとき(falseのとき)の両方が実行される必要がある。開発現場ではC1カバレッジと呼ばれ、ブランチカバレッジはステートメントカバレッジよりも強い評価基準となり,ブランチカバレッジが100%の場合は,必然的にステートメントカバレッジも100%になる。
 

コンディションカバレッジ(条件網羅)

分岐に設定されている1つ1つの条件について、成立したときと不成立のときの両方がテストで実行された割合。1つの分岐に複数の条件が設定されている場合、各条件ごとに実行の有無を確認する。一見するとブランチカバレッジと似ているが、定義は異なる。例えば、以下のようなテスト対象のコードがあるとする。

if(a == 0 && b < 0) {
……
}

この場合、a = 0b = 0のテストケースとa = 1b = -1のテストケースを作成してテストを行えば、コンディションカバレッジのカバレッジ率は100%になる。しかし、2つのテストケースともif文自体はtrueにならないので、ブランチカバレッジのカバレッジ率は100%にはならない。
 
これら以外にも更に強い評価基準を持ったホワイトボックステストの指標が存在する。しかし、強い評価基準であればあるほど、相対的にテストの作成には時間がかかるようになる。
 

ブラックボックステスト

ブラックボックステストとは、ある入力に対し、仕様通りの正しい結果が返されるかどうかを確認するテストである。ホワイトボックステストとは異なり、ブラックボックステストではソースコードを見ない。内部処理は考えず、入力とそれに対する結果(戻り値)を照らし合わせてテストの合否を判定する。
ブラックボックステストでは、次に挙げる「同値分割法」と「境界値分割法」を用いる。これらはどちらか一方を行えば良いのではなく、必ず両方とも行う必要がある。
 

同値分割法

プログラムが期待する入力値である「有効同値」とそれ以外の入力値である「無効同値」のそれぞれに代表値を用意し、それらを入力値としてプログラムを実行した結果を確認する方法。たとえば、有効同値が0以上かつ1000未満の場合、無効同値は0未満かつ1000以上になる。
 

境界値分割法

有効同値と無効同値の境界となる値を入力値としてプログラムを実行し、その結果を確認する方法。例えば、有効同値が0以上かつ1000未満の場合、無効同値は0未満かつ1000以上で、その境界値は-1、0、999、1000となる。
 

どのテスト手法を採用すればいいのか

ホワイトボックステスト、ブラックボックステストのどちらか片方だけ行っても十分なユニットテストとはいえない。テストをする観点がソースコードなのか(ホワイトボックステスト)、振る舞いなのか(ブラックボックステスト)で異なるためである。カバレッジ率が高くても振る舞いが間違っていたらバグとなり、同様に振る舞いが合っていてもカバレッジ率を意識しないとコードの中でテストをしていない箇所に気付くことが出来ず、バグを生んでしまう可能性がある。
 
とはいえ、我々がユニットテストにかけることが出来る時間は限られている。全てのテストケースをテストクラスで記述しよう、などと気負ったりせず、ある程度自分の納得するまでテストケースを記述したあとは、以降のテストでテストケースが必要になったときにテストケースを追加するように考えるほうが気が楽である。
 
 
以上
 
 
参考
書籍 プログラミング現場の単体テスト

PHP 5.4で調査や検証を行い,分かったことがあったのでまとめておく。
 

プリミティブ型と参照型

PHPの変数の型には,大きく分けて「プリミティブ型」と「参照型」の2つが存在する。ここでは説明を省きたいので,それぞれの違いについては,この記事をご閲覧いただきたい。
 

PHPにおける値渡しと参照渡し

上記の説明から,プリミティブ型の変数への代入では「値渡し」が行われ,参照型の変数への代入では「参照渡し」が行われていることが理解出来る。
 
PHPのコードを書いて実験してみる。

$a = 1;
$b = $a;
$a = $a + 1;
echo $b; //1 を出力する

 
プリミティブ型を変数に代入するときは,値渡しが行われるので,$bに$aを代入した後で,$aに対して操作を行っても$bには影響を与えない。
 

$a = new DateTime('2000-01-01');
$b = $a;
$a->add(new DateInterval('P1D')); //1日加算 2000-01-02
echo $b->format('Y-m-d');         //2000-01-02 を出力する

 
参照型を変数に代入するときは,参照渡しが行われるので,$bに$aを代入した後で,$aに対して操作を行うと$bに影響を与える。
 

僕の知ってる参照渡しと違うんだけど…

あなたの知っている参照渡しは,おそらく以下のようなコードではないだろうか。

$a = 1;
$b = &$a;
$a = $a + 1;
echo $b; //2 を出力する

 
プリミティブ型を変数に代入するときは,通常の場合は値渡しが行われるが,&を使うことで参照渡しを行うことが出来るので,$bに$aを代入した後で,$aに対して操作を行うと$bに影響を与える。
 
あまり聞かないが,これは「値の参照渡し」とでも呼べば覚えやすい気がする。
 
※ちなみにメソッドに対して,値の参照渡しを行うときは,「参照返し」も一緒に行わないと, return されたときに値のコピーが発生してしまうので注意が必要である。
 

参照型のコピーを作成する方法

では,PHPで参照型のコピーを作成するにはどうすれば良いのか。上記で示した参照型の代入の例のように,単純に参照型を変数に代入するだけでは,変数がお互いに影響を与えあってしまうので,これでは使い勝手がよくない。
 
そこで,PHPにはcloneという命令が用意されており,これを使うことで参照型が格納された変数のコピーを作成することが出来る。
 

<?php
$a = new DateTime('2000-01-01');
$b = clone $a;
$a->add(new DateInterval('P1D')); //1日加算 2000-01-02
echo $b->format('Y-m-d');         //2000-01-01 が出力される

 
参照型を変数に代入するときは,通常の場合は参照渡しが行われるが,cloneを使うことで値渡しを行うことが出来るので,$bに$aを代入した後で,$aに対して操作を行っても$bには影響を与えない。
 
これは「参照の値渡し」と呼ばれる。
 

まとめ

変数に何かを代入するときは,以下の4つのことを意識する。

  • 値渡し(値の値渡し)
  • 参照渡し(参照の参照渡し)
  • 値の参照渡し
  • 参照の値渡し

 
 
以上
 
 
参考
PHP: リファレンス渡し – Manual
オブジェクト変数の値渡し – PHPリファレンス
PHPが糞言語なのはどう考えても参照をポインタだと思っているお前らが悪い – なんたらノート第三期ベータ

先日,東北デベロッパーズコミュニティの6周年記念ワークショップ祭りに参加した。テスト駆動開発の要領,Gradleの導入方法,JUnitでのテストの書き方などを講師の方に指南していただいたので,忘れないようにまとめておく。
 

テスト駆動開発とは

テスト駆動開発(TDD: test driven development)とは,プログラム開発手法の一種で,プログラムに必要な各機能について,最初にテストを書き(これをテストファーストと呼ぶ),そのテストが動作する必要最低限な実装を行った後,コードを洗練させる,という短い工程を繰り返すスタイルである。
テスト駆動開発は,以下のようなサイクルで開発が進んでいく。

  1. 失敗するテストを書く
  2. できる限り早く、テストがパスするような最小限のコード本体を書く
  3. コードの重複を除去する(リファクタリング)

 
具体的な例としては,次のようになる。

  1. helloメソッドの戻り値として,”Hello”を期待するテストを作成する
  2. テストを実行し,失敗していることを確認する
  3. helloメソッドにreturn "Hello";を記述する
  4. テストを実行し,成功していることを確認する
  5. helloメソッドに”1″という引数を与えた際の戻り値として,”こんにちは”を期待するテストを作成する
  6. テストを実行し,失敗していることを確認する
  7. helloメソッドをif(argument == 1){ return "こんにちは"; } else{ return "Hello"; }に書き換える
  8. テストを実行し,成功していることを確認する
  9. helloメソッドに”2″という引数を与えた際の戻り値として,”Bonjour”を期待するテストを作成する
  10. テストを実行し,失敗していることを確認する
  11. helloメソッドをif(arg == 1){ return "こんにちは"; } else if(arg == 2){ return "Bonjour"; } else { return "Hello"; }に書き換える
  12. テストを実行し,成功していることを確認する
  13. ……

 
テスト駆動開発は,このようにテストの失敗と成功を繰り返しながら進めていく。テストの実行環境ツールであるJUnitやxUnitでは,テストの失敗を赤いバー,成功を緑のバーで通知するため,上記のサイクルはRed/Green/Refactorと称される。
 

テスト駆動開発を行うための環境を構築する

今回は,以下のツールを使ってテスト駆動開発を実践する。

  • Eclipse (IDE)
  • Gradle (ビルドシステム)
  • JUnit (ユニットテストフレームワーク)

 
それぞれのツールについて簡単に説明すると,EclipseはJava言語のプログラム開発に特化したIDEである。GradleはMakeやAnt,Mavenのようなビルドシステムの一つ。JUnitは,テストを簡単に記述できるようにするためのテストフレームワークである。
 
以下より,各ツールのセットアップ方法を説明する。尚,Gradleのインストールは任意で構わない。Gradleをインストールしなくても本稿を読み進める上での支障はない。JUnitはEclipseのプロジェクトにあらかじめ含まれているので,個別にセットアップする必要はない。
 

Eclipseをセットアップする

Eclipseの公式サイトから,Eclipseをダウンロードし,インストールする。
 

Gradleをセットアップする

Gradleの公式サイトからGradleをダウンロードし,圧縮フォルダを解凍し,適当な場所に設置する。
解凍したフォルダのパスを環境変数GRADLE_HOMEに設定する。また、GRADLE_HOME/binにパスを通しておく。
 
ちなみに,yum,MacPorts,homebrewなどのパッケージ管理ツールからインストールする方法や,GVM(Groovy enVironment Manager)からインストールする方法もあるので,ターミナルが使えるのであれば,こちらの方法をお勧めする。
 
homebrewからGradleをインストールする場合は,以下のコマンドをターミナルに打ち込む。

#Gradleをインストールする
brew install gradle

#Eclipseプロジェクトのルートに移動して,GradleとEclipseを関連付ける
gradle eclipse

 
GVMからGradleをインストールする場合は,以下のコマンドをターミナルに打ち込む。

#Gradleをインストールする
gvm install gradle

#Eclipseプロジェクトのルートに移動して,GradleとEclipseを関連付ける
gradle eclipse

 

ユニットテストを書いてみる

ユニットテストを書き始めるにあたって,まずはプロジェクトを作成するところから順を追って説明する。
上部メニューの「ファイル」の「新規」から「Java プロジェクト」を選択する。
プロジェクト作成用のウィンドウが開くので,「プロジェクト名」を入力する。
 
project_name
 
「次へ」を選択し,「ライブラリー」のタブへ移動する。
 
add_library
 
「ライブラリーの追加」から「JUnit」を選択する。
「次へ」を選択し,JUnitのバージョンが4になっていることが確認できたら,「完了」を選択する。
プロジェクト作成のウィンドウの方も「完了」を選択する。
 
これでプロジェクトを作成することができた。
 
続いて,テストコードの対象となるプロダクトコードを作成する。
プロジェクトを右クリックし,「新規」から「クラス」を選択する。
パッケージ名を「main」,クラス名を「Sample」と名付け,「完了」を選択する。
作成したSample.javaに以下のコードを記述する。

package main;

public class Sample {

    public String say() {
        return "Hello, World!";
    }

}

 
プロダクトコードが完成したので,今度はこのコードに対してテストコードを作成する。
プロジェクトを右クリックし,「新規」から「JUnit テスト・ケース」を選択する。
パッケージ名を「main」,クラス名を「SampleTest」と名付け,「完了」を選択する。
作成したSampleTest.javaに以下のコードを記述する。

package main;

import main.Sample;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;

public class SampleTest {

    @Test
    public void test() {
        final Sample sut = new Sample();
        final String expected = "Hello, World!";
        final String actual = sut.say();
        assertThat(actual, is(expected));
    }

}

 
SampleTest.javaを右クリックし,「実行」から「1 JUnit テスト(1)」を選択する。
ここでJUnitによるテストが実行されるはずだ。JUnitのタブが表示され,緑色のバーが表示されていれば,テストは成功である。
この時点でエラーが発生している場合は,コードが間違えていないかどうかの確認や,Javaのコンパイラのバージョンを下げてみるなどして対応してほしい。
 
 
煩雑な文章になってしまったが,テスト駆動開発の流れとJUnitによるユニットテストの導入を説明した。JUnitによるユニットテストの書き方の詳細については,この記事だけではまとめきれないので,また次回の記事で取り上げる。
 
 
以上
 
 
参考
テスト駆動開発 – Wikipedia
東北デベロッパーズカンファレンス2014「6周年記念ワークショップ祭り!」JUnitハンズオン

WordPressのテンプレートタグについて勉強した。テンプレートタグの使い方について,簡単にメモを残しておく。
 

テンプレートタグとは

テンプレートタグとは,WordPressにおいて,ブログのデータを動的に表示したり,ブログをカスタマイズしたりするときに,テンプレートの中で使われるPHPのコードである。
 

テンプレートタグの種類

テンプレートタグは,大きく以下の2つに分かれる。

  1. ループので使うテンプレートタグ
  2. ループので使うテンプレートタグ

 
WordPressにおいて,記事や固定ページの取得など,特定の操作を行うためにはループを行う必要がある。WordPressは,ループによって繰り返し,記事や固定ページを走査し,指定した条件に合致するページが見つかった場合は,HTMLとして出力する。
 

テンプレートタグの使い方

上記でテンプレートタグの種類について言及したが,まず初めに「ループの外で使うテンプレートタグ」について説明する。
 

ループの外で使うテンプレートタグ

「ループの外で使うテンプレートタグ」には,使用頻度の高いものとして,bloginfo()というテンプレートタグがある。これは,WordPressで作成されたサイトの基本的な情報を取得するためのテンプレートタグである。
以下の表にbloginfo()の使用方法をまとめる。
 

bloginfo()の使い方
コード 意味
<?php bloginfo(‘url’); ?> ブログのURLを出力する。
<?php bloginfo(‘name’); ?> ブログの名前を出力する。
<?php bloginfo(‘description’); ?> ブログのキャッチフレーズを出力する。

 

ループの中で使うテンプレートタグ

続いて,「ループの中で使うテンプレートタグ」について説明する。
以下に,WordPressにおけるループのサンプルコードを示す。
 

<?php
    
    if( have_posts() ){    //投稿があるか調べる
        //投稿がある場合の処理

        while( have_posts() ){    //投稿された記事の数だけ繰り返す

            the_post();        //投稿情報を読み込む
            the_title();       //タイトルを表示する
            the_content();     //本文を表示する

        }
    } else {
        //投稿が無い場合の処理
        echo '記事がありませんでした。';
    }
?>

 
上記は,WordPressにおける最も基本的なループの記述方法である。このサンプルコードでは,WordPressに投稿された記事をwhile文によって走査し,記事が存在する場合に限り,投稿情報・タイトル・本文を出力する。
 
以下は,上記のサンプルコードを適切に分割し,扱いやすくしたコードである。可読性は落ちるが,このように記述することで使用面で様々な応用が利くようになる。
 

<?php if( have_posts() ) : ?>
    <?php while( have_posts() ) : the_posts(); ?>
        <?php the_post(); ?>
        <?php the_title(); ?>
        <?php the_content(); ?>
    <?php endwhile; ?>
<?php else : ?>
    <p>記事がありませんでした。</p>        
<?php endif; ?>

 
これはWordPressに倣った書式である。このように記述することで,複数人でテンプレートを編集した場合でも,一定以上の可読性を保つことができる。
 

ループに条件を設定する

ここで,ループに条件を設定する方法を説明する。記事や固定ページを取得する際に,最新の投稿から2番目の記事のみを取得したい,という場合があるとする。そのような場合はループに条件を設定することで出力を操作することができる。
ループに条件を設定するための基本的なテンプレートタグとして,query_posts()がある。ループを行う直前にquery_posts()を記述することで,query_posts()に渡された引数をループの条件として設定することができる。
以下のコードでは,query_posts()を使用し,ループに条件を設定している。
 

<?php query_posts( array( 'post_type' => 'post', 'posts_per_page' => 1, 'offset' => 1 ) ); ?>
<?php if( have_posts() ) : ?>
    <?php while( have_posts() ) : the_posts(); ?>
        <?php the_post(); ?>
        <?php the_title(); ?>
        <?php the_content(); ?>
    <?php endwhile; ?>
<?php else : ?>
    <p>記事がありませんでした。</p>        
<?php endif; ?>

 
post_typeには,取得したい投稿のタイプを指定し,posts_per_pageには,記事の取得数を指定している。offsetには,最新の記事から何個目の記事を取得するかを指定している。このサンプルコードでは,通常の記事のうち,最新のものから2件目のみを取得している。
 
WordPressのテンプレートタグについての説明は以上で終了となる。これ以上のテンプレートタグの詳細情報については,WordPress Codex 日本語版を参考にしてほしい。
 
 
以上
 
 
参考
WordPressで投稿記事情報の取得方法
テンプレートタグ/query posts – WordPress Codex 日本語版
WordPressテンプレートタグとは

Javaのリフレクションについて勉強したのでメモしておく。
 

リフレクションとは

リフレクション (reflection) とは,プログラムの実行過程でプログラム自身の構造を読み取ったり書き換えたりする技術のことである。通常リフレクションというと動的(実行時)リフレクションのことを指すが,静的(コンパイル時)リフレクションをサポートするプログラミング言語もある。リフレクションはSmalltalk,Java,.NET Frameworkのような仮想機械やインタプリタ上で実行されることを想定した言語でサポートされることが多く,C言語のような機械語として出力されることを想定した言語でサポートされることは少ない。
 

リフレクションの危険性

リフレクションを使用することは便利である反面,以下のような危険を伴うことに注意しなければならない。

クラス設計を破壊するかもしれない

リフレクションは,クラス設計者の意図に反したことが出来てしまう強力な機能である。クラスは,オブジェクト指向の基本概念である「カプセル化」に沿って作成されるので,オブジェクトの使用者が外部からクラス(インスタンス)を操作するには,必ずアクセサを使用しなければならないというルールが設けられる。しかし,リフレクションはこのルールを無視し,クラス(インスタンス)を直接操作することを可能にしてしまう。つまり,リフレクションは既存のクラス設計を破壊してしまう可能性がある。
 

コンパイル時はエラーの検出対象外である

リフレクションを使用した部分のコードは,コンパイラやリファクタリングツールのチェック機能外となり,コンパイル時にエラーを検出することができない。これにより,リフレクションによって引き起こされたエラーはプログラム実行時にのみ検出されるので,デバッグの敷居も上がってしまう。
 

リフレクションの使い方

リフレクションを使用することは,上記で説明したような危険性を伴うが,やはり適所で使用する分にはとても便利である。
以下より,Javaにおけるリフレクションの使い方を説明する。
 

サンプルコード

Javaでは,クラスを扱う為にjava.lang.Classというクラスが用意されている。これはjava.langパッケージに含まれているので,コード中でインポートを行う必要はない。
以下のコードはJavaのリフレクションを扱ったサンプルコードである。

Class cl = Class.forName("Foo");
Method method = cl.getMethod("hello");
method.invoke(cl.newInstance());

 
1行目では,Fooクラスのオブジェクトを取得している。2行目では,Fooクラスのオブジェクトから,helloメソッドのオブジェクトを取得している。3行目では,Fooクラスのオブジェクトから,新たなFooクラスのインスタンスを生成し,helloメソッドを呼び出している。
 
 
以上
 
 
参考
リフレクション (情報工学) – Wikipedia
実は怖いjavaリフレクションの話 | ジャイアントモリンキーのjavaテック
Javaリフレクションメモ(Hishidama’s Java Reflection Memo)
リフレクション – 愚鈍人