目次
概要
.NETの単体テストにおけるモックライブラリ「Moq」を使用して、インターフェースのプロパティの振る舞いを定義する方法です。 単に値を返すだけの「読み取り専用」のような振る舞いと、値の代入を記憶して保持する「自動実装プロパティ」のような振る舞いの2パターンを解説します。 テスト対象のコードがプロパティの値を読み取るだけなのか、更新して再利用するのかによって使い分けます。
仕様(入出力)
- 入力
- モック化したいインターフェース定義
SetupGet: プロパティが呼ばれた際に返す固定値SetupProperty: プロパティの初期値(任意)
- 出力
SetupGetを設定した場合: 何度呼び出しても設定した固定値を返す。setを行っても値は変わらない(または設定によりエラー)。SetupPropertyを設定した場合: 通常のクラスのプロパティのように、setで代入された値を保持し、getでその値を返す。
- 前提
- NuGetパッケージ
Moqがインストールされていること。
- NuGetパッケージ
基本の使い方
特定のプロパティが参照されたときに、常に決まった値を返すように設定する基本形です。
// モックの作成
var mock = new Mock<IUserSettings>();
// Themeプロパティが呼ばれたら常に "Dark" を返すように設定
mock.SetupGet(x => x.Theme).Returns("Dark");
// 取得
var value = mock.Object.Theme; // "Dark"
コード全文
「固定値を返す設定」と「値を保持・更新できる設定」の2種類を比較できる完全なコンソールアプリケーションのコードです。
using System;
using Moq;
// 実行には NuGet で Moq パッケージのインストールが必要です
// dotnet add package Moq
namespace MoqPropertyExample
{
// モック対象のインターフェース
public interface IServerConfiguration
{
string HostName { get; set; }
int TimeoutSeconds { get; set; }
}
class Program
{
static void Main()
{
Console.WriteLine("--- 1. 固定値を返す設定 (SetupGet) ---");
RunSetupGetDemo();
Console.WriteLine("\n--- 2. 値の変更を追跡する設定 (SetupProperty) ---");
RunSetupPropertyDemo();
}
static void RunSetupGetDemo()
{
var mock = new Mock<IServerConfiguration>();
// HostNameプロパティが呼ばれたら、常に "db-server-01" を返す
// ※ 値をセットしても無視され、Getの結果は変わりません
mock.SetupGet(m => m.HostName).Returns("db-server-01");
var config = mock.Object;
Console.WriteLine($"初期値: {config.HostName}");
// 値を変更しようとしても、SetupGetで固定されているため反映されない(または上書きされない)
config.HostName = "web-server-99";
Console.WriteLine($"変更後: {config.HostName}");
}
static void RunSetupPropertyDemo()
{
var mock = new Mock<IServerConfiguration>();
// TimeoutSecondsプロパティを、通常のプロパティとして動作させる(値の保持)
// 第2引数で初期値を設定可能(ここでは 30)
mock.SetupProperty(m => m.TimeoutSeconds, 30);
var config = mock.Object;
Console.WriteLine($"初期値: {config.TimeoutSeconds}");
// 値を変更すると、モックオブジェクト内部でその値が保持される
config.TimeoutSeconds = 60;
Console.WriteLine($"変更後: {config.TimeoutSeconds}");
// さらに変更
config.TimeoutSeconds = 120;
Console.WriteLine($"再変更: {config.TimeoutSeconds}");
}
}
}
出力例
--- 1. 固定値を返す設定 (SetupGet) ---
初期値: db-server-01
変更後: db-server-01
--- 2. 値の変更を追跡する設定 (SetupProperty) ---
初期値: 30
変更後: 60
再変更: 120
カスタムポイント
- 戻り値の動的生成
Returns("FixedValue")の代わりにReturns(() => SomeFunction())を使用すると、アクセスされるたびに値を計算して返すことができます。
- 初期値の設定
SetupPropertyの第2引数は省略可能です。省略した場合、型に応じたデフォルト値(nullや0)で初期化されます。
注意点
- SetupGetの優先度
SetupGetで設定したプロパティに対して値をsetしても、例外は発生しませんが、次にgetした際にはSetupGetで定義した値が返されます。「値が変わらない」という挙動になるため、テスト対象が値の更新を前提としている場合は混乱の元になります。
- 階層構造のプロパティ
mock.Setup(m => m.Child.Property)のように階層深く設定する場合、中間のChildプロパティがnullを返さないように、親のモック設定も適切に行う必要があります(またはDefaultValueプロパティの設定でカバーする)。
- インターフェース定義の制約
- 当然ながら、インターフェースに
setアクセサがないプロパティに対してSetupPropertyは使用できません。
- 当然ながら、インターフェースに
応用
すべてのプロパティを一括で自動実装化する
プロパティが多数あり、すべてに対して値を保持させたい場合、個別に SetupProperty を書くのは手間です。SetupAllProperties を使うと一括設定できます。
var mock = new Mock<IServerConfiguration>();
// インターフェース内のすべてのプロパティを、値を保持可能な状態にする
mock.SetupAllProperties();
// 個別に初期値を設定したい場合のみ、後から設定可能
mock.Object.HostName = "localhost";
mock.Object.TimeoutSeconds = 500;
この方法は、単なるデータコンテナ(DTO)として振る舞うモックが必要な場合に非常に有効です。
まとめ
一括設定: 全プロパティを自動実装化したい場合は SetupAllProperties() が便利です。
固定値を返したい場合: SetupGet(m => m.Prop).Returns(値) を使用します。参照のみの用途に適しています。
値を更新・保持したい場合: SetupProperty(m => m.Prop, 初期値) を使用します。テスト中に状態が変わるオブジェクトに適しています。
