问题
I have the following controller action method and I am writing a unit test for this method
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
}
And Here is one of the unit Test which I have written for it,
[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");
}
When I am trying to run the test its throwing error: Object reference not set to an instance of object , and the line number shows to request.querystring , How to set the session variables in test methods, and Can anyone suggest me what is the proper way to check a controller action return type.
回答1:
Query String
You will also need to mock the query string in the HttpRequestBase
object. For that you will need to build the object graph
ControllerContext
-> HttpContextBase
-> HttpRequestBase
As you are already mocking the ControllerContext
of your controller instance, you can use the following code to add the mocked query string:
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);
Session
For the mocked session, use the same http context configured above to also return a mock session object:
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
Then you can set the values the way you did using SetupGet
, or you can also use Setup
as in
mockSession.Setup(s => s["token"]).Returns("fooToken")
If you want to verify that a value was set on the session, you can add something like this to your assert code:
mockSession.VerifySet(s => s["token"] = "tokenValue", Times.Once);
ActionResult types
What I usually do is to cast the result to the desired type using the as
operator. It will return null if the conversion is not possible. So the assert may look like this:
ViewResult result = controller.Index() as ViewResult;
// Assert
Assert.IsNotNull(result);
Assert.AreEqual("fooView", result.ViewName);
Side Note
If you have many similar tests where the code is using the session and/or query string, there will be quite a few mock objects you need to create and configure on every test.
You could add a setup method to your test class (which is run before each test), and move there all the code that builds the object graph with the mocks. This way you have a fresh controller instance on every test method and the arrange part of every test will just need to setup the mocks behaviour and expectations.
For example, if you have this setup code in your test class:
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;
}
The code on every test will be reduced to something like this:
[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);
}
Where those tests are testing this dummy Index method
public ActionResult Index()
{
if (Session["token"] == null)
{
if (Request.QueryString["code"] != null)
{
Session["token"] = "tokenValue";
return View("dummy");
}
}
return View();
}
来源:https://stackoverflow.com/questions/22311805/how-to-set-the-value-of-a-query-string-in-test-method-moq