3

認証プロジェクトを使用した新しい VS2013 デフォルト MVC を使用した単体テストについて学習しようとしています。最初にテストしたいことの 1 つは、ユーザーの登録です。(既に MS テスト済みのコードであるため、これを単体テストする必要はないことはわかっていますが、基本を理解するためにこれを使用したいと考えています)。また、新しいメンバーシップ コードはより「テスト可能」であるため、独自のメンバーシップ インターフェイスなどを作成する必要がないとも聞いています。

NSubstitute を偽のフレームワークとして使用しています。

Account Controller を見る -> Register() async メソッド

namespace WebApplication1.Controllers
{
    [Authorize]
    public class AccountController : Controller
    {
        public AccountController()
        : this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
        {
        }

        public AccountController(UserManager<ApplicationUser> userManager)
        {
            UserManager = userManager;
        }

        public UserManager<ApplicationUser> UserManager { get; private set; }

        ...


       //
       // POST: /Account/Register
       [HttpPost]
       [AllowAnonymous]
       [ValidateAntiForgeryToken]
       public async Task<ActionResult> Register(RegisterViewModel model)
       {
           if (ModelState.IsValid)
           {
                var user = new ApplicationUser() { UserName = model.UserName };
                var result = await UserManager.CreateAsync(user, model.Password);
                if (result.Succeeded)
                {
                    await SignInAsync(user, isPersistent: false);
                    return RedirectToAction("Index", "Home");
                }
                else
                {
                   AddErrors(result);
                }
           }

           // If we got this far, something failed, redisplay form
           return View(model);
       }

簡単なテスト (例: Register_RegisterValidUser) を書きたい場合、どうすればよいでしょうか? 何らかの形で UserManager を置き換える必要があることはわかっていますが、これはうまくいきませんでした:

var substitute = Substitute.For<UserManager<ApplicationUser>>();

Task.FromResult を使用して async Task<> 関数をバイパスする必要があることも理解していますが、CreateAsync() および SigninAsync() メソッドから有効なオブジェクトを返す方法がわかりません。

誰かがサンプル テスト コードを手伝ってくれませんか? どうもありがとう!

4

2 に答える 2

1

NSubstitute でユーザー マネージャーをモックするには、これを使用する必要があります。

var userStore = Substitute.For<IUserStore<ApplicationUser>>();
var userManager = Substitute.For<UserManager<ApplicationUser>>(userStore);

これで、メソッドの結果も偽造できます。例えば:

userManager.FindByNameAsync(Arg.Any<string>()).Returns(Task.FromResult(new ApplicationUser()));

これがお役に立てば幸いです。

于 2014-11-07T14:43:33.503 に答える
1

試験案内

いくつかの動作を単体テストできますが、正しい RedirectToAction 値を受け取るかどうかを確認するだけの単一の単体テストの方向性を示します。例えば、

return RedirectToAction("インデックス", "ホーム");

テスト容易性の向上

あなたの質問であなたが言及した

また、新しいメンバーシップ コードはより「テスト可能」であるため、独自のメンバーシップ インターフェイスを作成する必要がないと聞いています。

これは事実ですが、実装をもう少しテストしやすくするために、少し調整を加えます。このようにして、単体テストは、これから行うテストの特定の動作に純粋に集中できます。つまり、SUT (System Under Test) をよりテストしやすくすることができます。

  await SignInAsync(user, isPersistent: false);

SignInAsync はプライベート メソッドだと思います。このメソッドには、おそらく SUT に挿入できる別の実装に抽出できるいくつかの動作があるはずです。これをISignInManagerと呼ぶことができます

public interface ISignInManager {
    Task SignInAsync(ApplicationUser user, bool isPersistent);
}

これの利点は、ISignInManager の動作を挿入して SignIn タスクを実行できるようになり、SUT がよりテストしやすくなることです。単体テストでのモック/スタブが少なくなり、テストの記述と理解が容易になることがわかるはずです。

単体テスト

MSTest メソッドの新しい async/await の使用法を利用できます。これにより、私たちが書いていた複雑で信頼性の低いテストが簡素化されます。

正しいリダイレクト ルート コントローラー/アクション メソッドを検証する単体テストは、次のように記述できます。

    [TestMethod]
    public async Task Register_RegisterValidUser_EnsureRedirectToIndexActionHomeController()
    {
        // Arrange
        var userManagerStub = Substitute.For<IUserManager<ApplicationUser>>();
        var tcs = new TaskCompletionSource<IdentityResult>();
        tcs.SetResult(new IdentityResult(true));
        userManagerStub.CreateAsync(Arg.Any<ApplicationUser>(), Arg.Any<string>()).Returns(tcs.Task);

        var signInManagerStub = Substitute.For<ISignInManager>>();

        signInManagerStub.Setup(s => s.SignInAsync(It.IsAny<ApplicationUser>(), It.IsAny<bool>())).Returns(Task.FromResult(true));

        var sut = new AccountController(userManagerStub) { SignInManager = signInManagerStub.Object };

        // Act
        var result = await sut.Register(new RegisterViewModel() { Password = "fakePw" }) as RedirectToRouteResult;

        // Assert
        Assert.AreEqual<string>("Index", result.RouteValues["action"].ToString());
        Assert.AreEqual<string>("Home", result.RouteValues["controller"].ToString());
    }

上記は分離フレームワークとして NSubstitute を使用していますが、Moq バージョンに興味がある場合は、以下を参照してください。

    [TestMethod]
    public async Task Register_RegisterValidUser_EnsureRedirectToIndexHome()
    {
        // Arrange
        var userManagerStub = new Mock<IUserManager<ApplicationUser>>();
        var tcs = new TaskCompletionSource<IdentityResult>();
        tcs.SetResult(new IdentityResult(true));
        userManagerStub.Setup(s => s.CreateAsync(It.IsAny<ApplicationUser>(), It.IsAny<string>())).Returns(tcs.Task);

        var signInManagerStub = new Mock<ISignInManager>();
        signInManagerStub.Setup(s => s.SignInAsync(It.IsAny<ApplicationUser>(), It.IsAny<bool>())).Returns(Task.FromResult(true));

        var sut = new AccountController(userManagerStub.Object) {SignInManager = signInManagerStub.Object};

        // Act
        var result = await sut.Register(new RegisterViewModel() { Password = "fakePw" }) as RedirectToRouteResult;

        // Assert
        Assert.AreEqual<string>("Index", result.RouteValues["action"].ToString());
        Assert.AreEqual<string>("Home", result.RouteValues["controller"].ToString());
    }
于 2013-11-10T11:39:46.270 に答える