最も簡単なアプローチ: 戻り値をオーバーライドする
戻り値をモックする場合、これは非常に簡単です。クラスを変更しUtility.Helper
て、 というプロパティを含めることができますOverrideLoggedInUserName
。誰かが を呼び出したときGetLogedInUserName()
に、オーバーライド プロパティが設定されている場合はそれが返されます。それ以外の場合は、HttpContext から値を取得する通常のコードを使用して戻り値を取得します。
public static class Helper
{
// Set this value to override the return value of GetLoggedInUserName().
public static string OverrideLoggedInUserName { get; set; };
public static string GetLoggedInUserName()
{
// Return mocked value if one is specified.
if ( !string.IsNullOrEmpty( OverrideLoggedInUserName ) )
return OverrideLoggedInUserName;
// Normal implementation.
string username = "";
if ( System.Web.HttpContext.Current.User.Identity.IsAuthenticated )
{
username = ( (System.Web.Security.FormsIdentity)HttpContext.Current.User.Identity ).Ticket.Name;
}
return username;
}
}
これにより、技術的にはモックではなくスタブである戻り値を効果的にオーバーライドできます( Martin Fowler による優れた記事Mocks Aren't Stubsによると) 。これにより、戻り値をスタブ化できますが、メソッドが呼び出されたかどうかをアサートすることはできません。とにかく、戻り値を操作したいだけであれば、これはうまくいきます。
これをテストで使用する方法を次に示します。
[ TestMethod ]
public void TestGetRajnis()
{
// Set logged in user name to be "Bob".
Helper.OverrideLoggedInUserName = "Bob";
SomeController s = new SomeController( new SomeService() );
var data = s.GetRajnis();
// Any assertions...
}
この設計には 1 つの欠点があります。これは静的クラスであるため、オーバーライド値を設定すると、設定を解除するまで設定されたままになります。そのため、null にリセットすることを忘れないでください。
より良いアプローチ: 依存関係を注入する
ログインしているユーザー名を取得するクラスを作成し、それを のコンストラクターに渡す方法をお勧めしますSomeController
。これを依存性注入と呼びます。このようにして、モック化されたインスタンスをテスト用に注入できますが、テストしていないときは (HttpContext からユーザーを取得する) 実際のインスタンスを渡します。これは、よりクリーンで明確なアプローチです。さらに、このアプローチを処理するように特別に設計されているため、使用しているモック フレームワークのすべての機能を活用できます。これがどのように見えるかです。
// Define interface to get the logged in user name.
public interface ILoggedInUserInfo
{
string GetLoggedInUserName();
}
// Implementation that gets logged in user name from HttpContext.
// This class will be used in production code.
public class LoggedInUserInfo : ILoggedInUserInfo
{
public string GetLoggedInUserName()
{
// This is the same code you had in your example.
string username = "";
if ( System.Web.HttpContext.Current.User.Identity.IsAuthenticated )
{
username = ( (System.Web.Security.FormsIdentity)HttpContext.Current.User.Identity ).Ticket.Name;
}
return username;
}
}
// This controller uses the ILoggedInUserInfo interface
// to get the logged in user name.
public class SomeController
{
private SomeService _service;
private ILoggedInUserInfo _userInfo;
// Constructor allows you inject an object that tells it
// how to get the logged in user info.
public SomeController( SomeService service, ILoggedInUserInfo userInfo )
{
_service = service;
_userInfo = userInfo;
}
public List< Rajnikanth > GetRajnis()
{
// Use the injected object to get the logged in user name.
string username = _userInfo.GetLoggedInUserName();
return _service.GetRajni( username );
}
}
これは、Rhino Mocks を使用してスタブ オブジェクトをコントローラーに挿入するテストです。
[ TestMethod ]
public void TestGetRajnis()
{
// Create a stub that returns "Bob" as the current logged in user name.
// This code uses Rhino Mocks mocking framework...
var userInfo = MockRepository.GenerateStub< ILoggedInUserInfo >();
userInfo.Stub( x => x.GetLoggedInUserName() ).Return( "Bob" );
SomeController s = new SomeController( new SomeService(), userInfo );
var data = s.GetRajnis();
// Any assertions...
}
ここでの欠点は、Helper.GetLoggedInUserName()
静的ではないため、コードのどこからでも呼び出すことができないことです。ただし、テストを終了するたびにスタブ化されたユーザー名をリセットする必要はなくなりました。静的ではないため、自動的にリセットされます。次のテスト用に再作成し、新しい戻り値を設定するだけです。
これが役立つことを願っています。