【问题标题】:RhinoMocks AssertWasCalled throws ExceptionRhinoMocks AssertWasCalled 抛出异常
【发布时间】:2015-05-26 07:12:18
【问题描述】:

我是 TDD 和 RhinoMocks 的新手。

我正在尝试测试 AssertWasCalled 但遇到问题。我测试的构造函数如下:

    public AccountControllerTests()
    {
        _webAuthenticator = MockRepository.GenerateMock<IWebAuthenticator>();
    }

而我的测试是这样的:

    [TestMethod]
    public void AccountControllerCallsWebAuthenticator_CreateSignInTicketForGoodLoginCredentials()
    {
        const string username = "good-username";
        const string password = "good-password";

        var model = new LoginModel { Username = username, Password = password };

        _webAuthenticator.Stub(w => w.Authenticate(username, password)).Return(true);

        var mockHttpContextBase = MockRepository.GenerateMock<HttpContextBase>();

        var accountController = new AccountController(_webAuthenticator);

        accountController.Login(model);

        _webAuthenticator.AssertWasCalled(x => x.CreateSignInTicket(mockHttpContextBase, username));
    }

我得到的错误是:

测试方法 Paxium.Music.WebUI.Tests.Controllers.AccountControllerTests.AccountControllerCallsWebAuthenticator_CreateSignInTicketForGoodLoginCredentials 抛出异常: Rhino.Mocks.Exceptions.ExpectationViolationException: IWebAuthenticator.CreateSignInTicket(Castle.Proxies.HttpContextBaseProxy7f274f09b6124e6da32d96dc6d3fface, "good-username");预期 #1,实际 #0。

我现在更改了我的代码如下 - 代码之前和之后:

之前:

public class AccountController : Controller
{
    private readonly IWebAuthenticator _webAuthenticator;

    public AccountController(IWebAuthenticator webAuthenticator)
    {
        _webAuthenticator = webAuthenticator;
    }

    [HttpGet]
    public ActionResult Login()
    {
        return View();
    }

    [HttpPost]
    public ActionResult Login(LoginModel model)
    {
        if (ModelState.IsValid)
        {
            if (_webAuthenticator.Authenticate(model.Username, model.Password))
            {
                _webAuthenticator.CreateSignInTicket(HttpContext, model.Username);

                return RedirectToAction("Index", "Home");
            }

            return View(model);
        }

        return View(model);
    }
}

之后:

public class AccountController : Controller
{
    private readonly IWebAuthenticator _webAuthenticator;
    private readonly HttpContextBase _contextBase;

    public AccountController()
    {

    }

    public AccountController(IWebAuthenticator webAuthenticator, HttpContextBase contextBase)
    {
        _webAuthenticator = webAuthenticator;
        _contextBase = contextBase;
    }

    [HttpGet]
    public ActionResult Login()
    {
        return View();
    }

    [HttpPost]
    public ActionResult Login(LoginModel model)
    {
        if (ModelState.IsValid)
        {
            if (_webAuthenticator.Authenticate(model.Username, model.Password))
            {
                _webAuthenticator.CreateSignInTicket(_contextBase, model.Username);

                return RedirectToAction("Index", "Home");
            }

            return View(model);
        }

        return View(model);
    }
}

我的测试现在通过了。当我的控制器被真正使用时,我如何在 contextBase 中注入?我正在使用 StructureMap。

【问题讨论】:

    标签: c#-4.0 tdd rhino-mocks-3.5


    【解决方案1】:

    您收到的错误消息表明 Assert 失败,即 webAuthenticator 对象使用这些特定参数调用(因此预期 #1,实际 #0 异常消息)。

    根据您提供的有限上下文,我怀疑 您的测试中的 HttpContextBase (mockHttpContextBase) 假实例与您的生产代码中传递给 webAuthenticator 的对象不同。 p>

    有两种方法可以解决这个问题:使断言不那么严格,或者确保生产代码使用假的 http 上下文对象。如果您不关心在此测试中将哪个 HttpContext 实例传递给 webAuthenticator,您可以使用参数匹配器(Rhinomocks 称它们为 argument constraints)。 在你的情况下,结果会是这样的:

    _webAuthenticator.AssertWasCalled(x => x.CreateSignInTicket(Arg<HttpContextBase>.Is.Anything, Arg<string>.Is.Equal(username)));
    

    【讨论】:

    • 我已更改代码以注入上下文库,但仍然有点卡住 - 已为我的原始问题添加了注释。希望这是做事的正确方式吗?
    • 好吧,我意识到注入 HttpContextBase 是错误的,所以我想我现在明白了有一个不太严格的断言。我让他跟着工作:_webAuthenticator.AssertWasCalled(x =&gt; x.CreateSignInTicket(Arg&lt;HttpContextBase&gt;.Is.Anything, Arg&lt;string&gt;.Is.Anything)); 但是下面说“无法在静态上下文中访问非静态方法 'Equals'”_webAuthenticator.AssertWasCalled(x =&gt; x.CreateSignInTicket(Arg&lt;HttpContextBase&gt;.Is.Anything, Arg&lt;string&gt;.Equals(username)));
    • 我的错,正确的语法是 Arg.Is.Equal(...) 或快捷方式 Arg(...)
    • 请注意,使用假的 HttpContext 仍然是一个完全有效的选择,尽管它看起来有点笨拙(例如 here )。当我走这条路时,我更喜欢在上面提供一个薄的抽象层,这样我就可以干净地独立测试我的逻辑,而不必在我的独立单元测试中“设置整个世界”。我在Gist 中举了一个相关的例子。
    • 在您的情况下,最后一种方法可能证明很困难,因为 Context 是控制器对象本身的属性。有一些方法可以解决这个问题(例如使用子类来测试方法),但这只会导致更笨重的代码只是为了让它接受测试。引用的 stackoverflow 答案或不太严格的参数匹配器可能是解决此问题的最佳方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-24
    • 2010-12-29
    • 2010-11-27
    • 2011-05-30
    • 1970-01-01
    • 2011-02-25
    相关资源
    最近更新 更多