.NETテストプログラミング入門5
先ずは解答となるテストコードを掲載し、そのテストコードを元に解説します。
using System;
using System.IO;
using System.Threading;
static class ConsoleTester
{
private static Semaphore lockObj = new Semaphore(1, 1);
public static bool ConsoleOutCheck(
string right, Action proc, bool yield )
{
//ロック開始
lockObj.WaitOne();
//出力先を変えてテストを行いやすくする
TextWriter tmp = Console.Out;
StringWriter writer = new StringWriter();
Console.SetOut( writer );
//テスト対象を実行
Thread.Sleep( 100 );
proc();
//出力設定を元に戻す
if ( yield == true ) Thread.Yield();
Console.SetOut( tmp );
//出力値をテスト
bool result = writer.ToString().Equals( right );
//ロック解除
lockObj.Release();
//テスト結果を返す
return result;
}
}
class Piyo
{
public void Greeting()
{
Console.WriteLine( "おはピヨ♪" );
}
}
class HelloWorld
{
public void FirstProgramming()
{
Console.WriteLine( "Hello World!" );
}
}
class Test
{
static void Main( string[] args )
{
//Piyoクラスのテストを実行
string piyoMessage = null;
Thread pt = new Thread( () => {
Piyo piyo = new Piyo();
string right = "おはピヨ♪" + Environment.NewLine;
bool result = ConsoleTester.ConsoleOutCheck(
right,
() => piyo.Greeting(),
false );
piyoMessage = result == true ?
"Piyoテスト成功" : "Piyoテスト失敗";
} );
//HelloWorldクラスのテストを実行
string helloMessage = null;
Thread ht = new Thread( () => {
HelloWorld hello = new HelloWorld();
string right = "Hello World!" + Environment.NewLine;
bool result = ConsoleTester.ConsoleOutCheck(
right,
() => hello.FirstProgramming(),
true );
helloMessage = result == true ?
"HelloWorldテスト成功" : "HelloWorldテスト失敗";
} );
//並行的にテスト
pt.Start();
ht.Start();
pt.Join();
ht.Join();
//テスト結果を表示
Console.WriteLine( piyoMessage );
Console.WriteLine( helloMessage );
}
}
以上のように前回のテストコードを少し改良するだけで、並行的にテストが出来るようになります。このテストコードはポイントが3つあります。1つめのポイントは、同期オブジェクトを使用し排他制御をしている事です。ConsoleクラスのOutプロパティのように静的プロパティを使用する場合、並行処理の結果を仕様通りにする為に排他制御をせねばなりません。複数のスレッドが同時に静的なプロパティの値を変更すると、静的な値に依存するクラスは整合性のある結果を出せません。詳しくは今後.NET並行処理プログラミングの記事を連載しますのでそちらの方を読んで下さい。
2つめのポイントは、テストを実行するクラスのメソッドを静的にする事です。テストコードもまた普通のプログラムです。他者が理解しやすいように考慮せねばなりません。このテストは静的な値に依存するので、分かりやすいように静的メソッドにせねばなりません。そうしないとこのテストコードを書いていない人は、オブジェクト指向プログラミングは通常インスタンスを使用するので、インスタンスな値に依存するテストだという前提を持ちます。従って、注意を促すために静的メソッドにする必要があります。
3つめのポイントは、テストを実行するクラスを静的にする事です。コンソールの入出力機能を使用するこのクラスは明らかに静的なものです。何故ならば、Consoleクラスの静的なOutプロパティが関係するからです。コンソールはそもそもグローバルかつ共有的なものです。それを他者に伝える為に静的なクラスでなければなりません。
2つめと3つめのポイントは、テストを並行処理しない場合でも同じ事が言えます。テストコードを漫然とコーディングしてはなりません。テストの性質に応じたテストクラスを設計&実装せねばなりません。何故ならば、テストコードは仕様書としての役割も果たすからです。テストの性質と異なるテストコードはプログラマに誤解を与えます。意外とテストコードを意識しない人が多々見受けられます。しかしながら、テストコードは仕様を示す大切な資産です。よく考えてテストコードをコーディングしましょう。