normalian blog

Let's talk about Microsoft Azure, ASP.NET and Java!

Moq.dll on ASP.NET MVC

昨日Moq.dllを使ってみたので、早速ASP.NET MVCに適用してみました。ここで、ControllerクラスのUserプロパティやら、HttpContextプロパティやらの設定はうまく行ったのですが、その他に色々問題があったのではまずはメモしてみる。

Moq.dllの適用先としては、わんくま同盟のデモ用に作った、以下のロジックコードに適用してみます。

[Authorize]
[HandleError]
public class BuyHistoryController : Controller
{
    readonly IOrderRepository<Order> orderRepository;

    public BuyHistoryController()
        //: this(new OrderRepository())
    {
    }

    public BuyHistoryController(IOrderRepository<Order> orderRepository)
    {
        this.orderRepository = orderRepository;
    }

    //
    // GET: /BuyHistory/
    public ActionResult Index()
    {
        string username = User.Identity.Name;
        try
        {
            var model = orderRepository.GetByUsername(username).OrderByDescending(order => order.OrderDate);
            if (model == null || model.Count() == 0)
            {
                return RedirectToAction("Empty");
            }
            else
            {
                return View(model);
            }
        }
        catch (Exception e)
        {
            Trace.WriteLine(e);
            return RedirectToAction("Sorry", "Result");
        }
    }
    (中略)
}

これに対するテストコードを以下で記述しました。

[TestMethod()]
public void 購入してないユーザがEmptyビューに飛ぶTest01()
{
    Type expected = typeof(RedirectToRouteResult);
    //ActionResult actual;

    //モック系の作成
    var mockControllerContext = new Mock<ControllerContext>();
    var mockHttpContextBase = new Mock<HttpContextBase>();
    var mockIdentity = new Mock<IIdentity>();
    var mockPrincipal = new Mock<IPrincipal>();
    var mockBuyHistoryController = new Mock<BuyHistoryController>(new TestOrderRepository());

    //ユーザ情報の設定
    mockIdentity.Setup(identity => identity.IsAuthenticated).Returns(true);
    mockIdentity.Setup(identity => identity.Name).Returns("購入してないユーザ");
    mockPrincipal.Setup(principal => principal.Identity).Returns(mockIdentity.Object);
    mockHttpContextBase.Setup(httpContextBase => httpContextBase.User)
        .Returns(mockPrincipal.Object);
    //コントローラにUserオブジェクト設定
    //mockBuyHistoryController.Setup(buyHistoryController => buyHistoryController.User)
    //    .Returns(mockPrincipal.Object);
    // ⇒これだとダメ、UserプロパティはhttpContext参照してたよ・・・orz

    //mockBuyHistoryController.Setup(buyHistoryController => buyHistoryController.HttpContext)
    //    .Returns(mockHttpContextBase.Object);
    // ⇒これもダメ、HttpContextプロパティはControllerContext参照してたよ・・・orz

    mockControllerContext.Setup(controllerContext => controllerContext.HttpContext)
        .Returns(mockHttpContextBase.Object);

    //コントローラ生成
    var controller = mockBuyHistoryController.Object;
    controller.ControllerContext = mockControllerContext.Object;

    //テスト実行
    var actual = controller.Index();
    //なぜか actual が null で帰ってくる・・・
    Assert.AreEqual(expected, actual.GetType());
}

上のテストコードを作成した程度で以下にハマって見ました。

  • 「やっべ、Mockインスタンスコンストラクタが渡せない!」⇒実はnew Mock<>()でコンストラクタ渡せる。
  • うっほ、Controller.Userプロパティが設定できない ⇒ Controller.csを見ると、普通にHttpContextプロパティ参照してる
  • うっほ、Controller.HttpContextプロパティが設定できない ⇒ Controller.csを見ると、普通にControllerContextプロパティ参照してる

という流れを経た後、とりあえずController.User.Nameからユーザ名を持ってくるサンプルを記述完了。

と・こ・ろ・が、このテストコードなら「return RedirectToAction("Empty");」の結果が返ってくると思っているのでsが、なぜかnullが帰ってくる。しかも例外がおきてない。うーん、色々と考える必要が有るなぁ…。