miso_soup3 Blog

主に ASP.NET 関連について書いています。

何をテストしたいのか? をハッキリする

問題

同じ内容のテストを2度以上書いてしまった時、以下の問題が発生している可能性があります。

- 何をテストしたいのかハッキリしていない。
- 技術的負債を抱えたコードを書いている。

ユーザーが、オレンジを食べるメソッドを作ります。
これをTDDで実装します。
テスト対象は、UserクラスのEatメソッドです。

public class User
{
	// オレンジを食べます
	public void Eat(Orange orange)
	{
		throw new NotImplementedException();
	}
}

[TestFixture]
public class UserTest
{
	[Test]
	public void Eat_Test()
	{
		//いまからテストかくところ
	}
}

Orangeクラスには既に、食べられるメソッドが実装してあるものとします。
すでにテストも書いてあります。

Orangeクラスは食べられると、Volumeプロパティが1減るそうです。

public class Orange
{
	/// <summary>
	/// ユーザにより、食べられます。
	/// </summary>
	/// <param name="user">ユーザ</param>
	public void EatenBy(User user)
	{
		this.Volume = this.Volume - 1;
		//...他、食べられた処理など
	}
}

[TestFixture]
public class OrangeTest
{
	[Test]
	public void EatenBy_Test()
	{
		var orange = new Orange() { Volume = 3 };
		var user = new User();
		
		 // 実行
		orange.EatenBy(user);

		int expect = 2;
		Assert.AreEqual(expect, orange.Volume);
	}
}

普通の人は、このメソッドを利用して、UserのEatメソッドを実装しようと思います。
TDD開始!!

同じテストを2度書いてしまう、まずいテストの書き方。↓

[TestFixture]
public class UserTest
{
	[Test]
	public void Eat_Test()
	{
		var user = new User();
		var orange = new Orange() { Volume = 3 };

		//実行
		user.Eat(orange);

		int expect = 2;
		Assert.AreEqual(expect, orange.Volume);
	}
}

Orangeのテストコードと同じようなことを書いています。

よりよい書き方はこうです。↓

[TestFixture]
public class UserTest
{
	[Test]
	public void Eat_Test()
	{
		var user = new User();
		Mock<Orange> orangeMock = new Mock<Orange>();

		orangeMock.Setup(orange =>
			orange.EatenBy(user));

		//実行
		user.Eat(orangeMock.Object);

		orangeMock.VerifyAll();
	}
}

おまけの実装コード

public class User
{
	// オレンジを食べます
	public void Eat(Orange orange)
	{
		orange.EatenBy(this);
	}
}

テストをしたい内容は、
OrangeのEatenByを正しい引数で呼び出しているか?
であり、
ユーザがオレンジを食べたら、オレンジの量が1コへっているかどうか?
ではありません。

また、このまずいテストの書き方は、1つ技術的負債を抱えています。
(1コ減るのではなくて、2コ減らして!と仕様変更が来た場合、
OrangeTestと、UserTestのテストを書きなおさなくてはなりません。)

おしまい

上の例では、何をテストしたいのか、はっきりしないままコードを書いたために、
よくないコードが生まれてしまいました。

TDDでは、何をテストしたいのか?をちゃんとハッキリすることが大事。

補完

テストツールはNUnit、Moqを使っています。
例のコードのままだと、以下のエラーがでます。

failed: System.NotSupportedException : Invalid setup on a non-virtual (overridable in VB)

UserのEatメソッドを、virtual にするとモックが機能します。

TDDサイト

TDD.NET http://www.tdd-net.jp/
Strategic Choice http://d.hatena.ne.jp/asakichy/ テスト駆動開発カテゴリ
TDDアンチパターン http://blog.james-carr.org/2006/11/03/tdd-anti-patterns/

...ちゃんと勉強しなかったために、無駄な時間を過ごしました orz