【问题标题】:How to set the value of a query string in test method Moq如何在测试方法 Moq 中设置查询字符串的值
【发布时间】:2014-04-14 05:50:13
【问题描述】:

我有以下控制器操作方法,我正在为此方法编写单元测试

    try
    {
    if ( Session["token"] == null)
        {
            //checking whether the user has already given the credentials and got redirected by survey monkey by  checking the query string 'code'
            if (Request.QueryString["code"] != null)
            {
                string tempAuthCode = Request.QueryString["code"];
                Session["token"] = _surveyMonkeyService.GetSurveyMonkeyToken(ApiKey, ClientSecret, tempAuthCode, RedirectUri, ClientId);
            }
            else
            {
                //User coming for the first time directed to authentication page
                string redirectUrlToSurveyMonkeyAuthentication = _surveyMonkeyService.GetUrlToSurveyMonkeyAuthentication(RedirectUri, ClientId, ApiKey);
                return Redirect(redirectUrlToSurveyMonkeyAuthentication);
            }
        }    
        //User is in the same session no need for token again showing surveys without authentication
        var model = _surveyService.GetSurveys(User.Identity.Name);
        if (model.Count == 0)
            return View(CSTView.NoSurveyTracker.ToString());
        return View(CSTView.Index.ToString(), model);
    }
    catch (Exception e)
    {
        return DisplayErrorView(e);//Even this returns a redirect method 
    }

这是我为它编写的单元测试之一,

    [Test]
    public void GetIndexPage_Returns_View_With_ValidToken()
    {
        var mockControllerContext = new Mock<ControllerContext>();
        var mockSession = new Mock<HttpSessionStateBase>();
        mockSession.SetupGet(s => s["SurveyMonkeyAccessToken"]).Returns(SampleToken);
        mockSession.SetupGet(c => c["code"]).Returns(SampleTempAuthCode);
        mockControllerContext.Setup(p => p.HttpContext.Session).Returns(mockSession.Object);
        _surveyTrackerController.ControllerContext = mockControllerContext.Object;
        _surveyServiceMock.Setup(x => x.GetSurveys(TestData.TestData.SampleUserName)).Returns(SurveyTrackerList);
        var result = _surveyTrackerController.GetIndexPage();
        Assert.IsInstanceOf(typeof(ActionResult), result);
        Assert.AreEqual(((ViewResult)result).ViewName, "expected");
    }

当我尝试运行测试时,它抛出错误:对象引用未设置为 object 的实例,并且行号显示为 request.querystring,如何在测试方法中设置会话变量,以及任何人都可以建议我检查控制器操作返回类型的正确方法是什么。

【问题讨论】:

    标签: c# unit-testing nunit moq


    【解决方案1】:

    查询字符串

    您还需要模拟HttpRequestBase 对象中的查询字符串。为此,您需要构建对象图

    ControllerContext -> HttpContextBase -> HttpRequestBase

    由于您已经在模拟控制器实例的ControllerContext,您可以使用以下代码添加模拟的查询字符串:

    var queryString = new NameValueCollection { { "code", "codeValue" } };
    var mockRequest = new Mock<HttpRequestBase>();
    mockRequest.Setup(r => r.QueryString).Returns(queryString);
    var mockHttpContext = new Mock<HttpContextBase>();
    mockHttpContext.Setup(c => c.Request).Returns(mockRequest.Object);
    mockControllerContext.Setup(c => c.HttpContext).Returns(mockHttpContext.Object);
    

    会话

    对于模拟会话,使用上面配置的相同 http 上下文也返回一个模拟会话对象:

    var mockSession = new Mock<HttpSessionStateBase>();
    mockHttpContext.Setup(c => c.Session).Returns(mockSession.Object);
    //where mockHttpContext has been created in the code for the queryString above and setup to be returned by the controller context
    

    然后您可以像使用SetupGet 那样设置值,或者您也可以像在中那样使用Setup

    mockSession.Setup(s => s["token"]).Returns("fooToken")
    

    如果您想验证会话中是否设置了值,您可以在您的断言代码中添加如下内容:

    mockSession.VerifySet(s => s["token"] = "tokenValue", Times.Once);
    

    ActionResult 类型

    我通常做的是使用asoperator 将结果转换为所需的类型。如果无法进行转换,它将返回 null。所以断言可能看起来像这样:

    ViewResult result = controller.Index() as ViewResult;
    
    // Assert
    Assert.IsNotNull(result);
    Assert.AreEqual("fooView", result.ViewName);
    

    旁注

    如果您有许多类似的测试,其中代码使用会话和/或查询字符串,那么您将需要在每个测试中创建和配置相当多的模拟对象。

    您可以将 setup 方法添加到您的测试类(在每次测试之前运行),然后将使用模拟构建对象图的所有代码移到那里。这样,您在每个测试方法上都有一个新的控制器实例,并且每个测试的安排部分只需要设置模拟行为和期望。

    例如,如果您的测试类中有此设置代码:

    private HomeController _homeController;
    private Mock<HttpSessionStateBase> _mockSession;
    private Mock<HttpRequestBase> _mockRequest;
    
    [SetUp]
    public void Setup()
    {
        _mockRequest = new Mock<HttpRequestBase>();
        _mockSession = new Mock<HttpSessionStateBase>();
        var mockHttpContext = new Mock<HttpContextBase>();
        var mockControllerContext = new Mock<ControllerContext>();
    
        mockHttpContext.Setup(c => c.Request).Returns(_mockRequest.Object);
        mockHttpContext.Setup(c => c.Session).Returns(_mockSession.Object);
        mockControllerContext.Setup(c => c.HttpContext).Returns(mockHttpContext.Object);
    
        _homeController = new HomeController();
        _homeController.ControllerContext = mockControllerContext.Object;
    }
    

    每次测试的代码都将简化为:

    [Test]
    public void Index_WhenNoTokenInSession_ReturnsDummyViewAndSetsToken()
    {
        // Arrange
        var queryString = new NameValueCollection { { "code", "dummyCodeValue" } };
        _mockSession.Setup(s => s["token"]).Returns(null);
        _mockRequest.Setup(r => r.QueryString).Returns(queryString);
    
        // Act
        ViewResult result = _homeController.Index() as ViewResult;
    
        // Assert
        Assert.IsNotNull(result);
        Assert.AreEqual("dummy", result.ViewName);
        _mockSession.VerifySet(s => s["token"] = "tokenValue", Times.Once);
    }
    
    [Test]
    public void Index_WhenTokenInSession_ReturnsDefaultView()
    {
        // Arrange
        _mockSession.Setup(s => s["token"]).Returns("foo");
    
        // Act
        ViewResult result = _homeController.Index() as ViewResult;
    
        // Assert
        Assert.IsNotNull(result);
        Assert.AreEqual(String.Empty, result.ViewName);
    }
    

    这些测试在哪里测试这个虚拟索引方法

    public ActionResult Index()
    {
        if (Session["token"] == null)
        {
            if (Request.QueryString["code"] != null)
            {
    
                Session["token"] = "tokenValue";
                return View("dummy");
            }
        }
        return View();
    }
    

    【讨论】:

    • 非常感谢.. 你能建议我如何测试返回 Redirect(URL) 方法吗?
    • 将控制器结果转换为 RedirectResult 并检查其 Url 属性
    • 我发现这个答案对模拟 HttpContext API 很有帮助。感谢作者@DanielJ.G.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-14
    • 2012-11-11
    • 1970-01-01
    • 2012-01-06
    • 1970-01-01
    • 2011-10-02
    • 1970-01-01
    相关资源
    最近更新 更多