4
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

オーバーライドしていないメソッドをスタブに置き換える

Posted at

概要

オーバーライドしていないvirtualでないメソッドや, 継承元の無いsealedなクラスなどの動作を, スタブに置き換える.

問題のコード

class Motor	// このクラスはライブラリとかで用意されているものとする
{
	/// <summary>
	/// モーターの回転速度を取得する
	/// </summary>
	public int GetSpeed()
	{
		// このコードは, 実機にモーターが
		// 繋がっていないと動作しない.
		// (テスト環境では動作させることはできない)
	}
}

class SampleClass	// 自分で作ったクラス
{
	/// <summary>
	/// テスト対象のメソッド
	/// </summary>
	/// <param name="motor">処理したいモーター</param>
	public void SampleMethod(Motor motor)
	{
		int currentSpeed = motor.GetSpeed();

		// currentSpeedを取得して何か処理する.
		// 省略する.
	}
}

このコードは, 特別なデバイス(実機)上で動作させるものとする.
実機にはモーターが接続されており, Motorクラスを通じて制御をおこなう.

このSampleMethodメソッドのテストを作成し, CIツール(Travis CIとかAppVeyorとか)で自動テストさせたい.
でも, CIツールの環境では, モーターが接続されていないので, GetSpeed()メソッドは動作しない.
テストの時は, GetSpeed()は呼ばず, 代わりのメソッドを呼ぶようにしたい.

ここまでであれば, 自分が作ったクラスのインスタンスメソッドをスタブに置き換えるのと同じである.
しかしここでは, 上記の記事とは異なり, Motorクラスは自分で作ったものでなくライブラリなどで提供されているもので, 自分では手を入れることができないものとする.
つまり, 上記の記事にあるような"インタフェースに依存させる"ということができない.
また, virtualが付いてないので, Motorクラスを継承してGetSpeed()の動作を上書き(オーバーライド)することもできない.

以降で, Motorインスタンスをスタブに置き換えて, SampleMethodメソッドをテストできるようにしてみる.

ラップするクラスを作る

インタフェースに依存させることもできず, 動作をオーバーライドすることもできないともあれば, もうしょうがないのでMotorをラップするクラスを作るぐらいしかない.

具体的には次のようにする.

class MotorWrapper
{
	public MotorWrapper()
	{
		Motor instance = new Motor();
		GetSpeed = instance.GetSpeed;
	}

	public Func<int> GetSpeed{ get; }
}

こちらで説明した, "メソッドを格納できる変数"を利用している.
使う側では, 次のようになるだろう.

class SampleClass
{
	/// <summary>
	/// テスト対象のメソッド
	/// </summary>
	/// <param name="motor">処理したいモーター</param>
	public void SampleMethod(MotorWrapper motor)
	{
		int currentSpeed = motor.GetSpeed();

		// currentSpeedを取得して何か処理する.
		// 省略する.
	}
}

MotorWrapperインスタンスを受け取れば, あとはMotorのときと同じようにGetSpeed()を実行できる.

スタブに入れ替えてみる

では, テストコードで, GetSpeed()メソッドの動作を入れ替えてみる.

[TestClass]
public class SampleClassTest
{
	[TestMethod, Description("SampleMethodメソッドのテスト")]
	public void SampleMethodTest()
	{
		// Arrange (準備)
		SampleClass instance = new SampleClass();
		MotorWrapper inputMotor = new MotorWrapper();
		inputMotor.GetSpeed = GetSpeedStub;	// GetSpeedの動作を入れ替える

		// Act (実行)
		instance.SampleMethod(inputMotor);

		// Assert (検証)
		// 何か処理が行われたことを確認する
		// 今回は省略
	}

	/// <summary>
	/// Motor.GetSpeedメソッドのスタブメソッド
	/// </summary>
	private int GetSpeedStub()
	{
		// 何もしない
		return 0;
	}
}

もうちょっと改善

ラムダ式を知っていると, もうちょっと簡単に入れ替えられる.

inputMotor.GetSpeed = () => 0;	// GetSpeedの動作を入れ替える

おしまい.

"メソッドを格納する変数"を使うと, Intellisenseによるメソッドの説明が出てこなくなるのがつらい.
インタフェースを用意していないライブラリを恨むしかないのか...

4
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?