I finally was able to get the HttpContext.Current to be not null by finding some code online. But I still have not be able to add custom headers to the request in my unit test. Here is my test:
[TestClass]
public class TagControllerTest
{
private static Mock<IGenericService<Tag>> Service { get; set; }
private TagController controller;
[TestInitialize]
public void ThingServiceTestSetUp()
{
Tag tag = new Tag(1, "people");
Response<Tag> response = new Response<Tag>();
response.PayLoad = new List<Tag>() { tag };
Service = new Mock<IGenericService<Tag>>(MockBehavior.Default);
Service.Setup(s => s.FindAll("username", "password", "token")).Returns(response);
controller = new TagController(Service.Object);
HttpContext.Current = FakeHttpContext();
}
public static HttpContext FakeHttpContext()
{
var httpRequest = new HttpRequest("", "http://kindermusik/", "");
var stringWriter = new StringWriter();
var httpResponce = new HttpResponse(stringWriter);
var httpContext = new HttpContext(httpRequest, httpResponce);
var sessionContainer = new HttpSessionStateContainer("id", new SessionStateItemCollection(),
new HttpStaticObjectsCollection(), 10, true,
HttpCookieMode.AutoDetect,
SessionStateMode.InProc, false);
httpContext.Items["AspSession"] = typeof(HttpSessionState).GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance,
null, CallingConventions.Standard,
new[] { typeof(HttpSessionStateContainer) },
null)
.Invoke(new object[] { sessionContainer });
httpContext.Request.Headers["username"] = "username"; //It throws a PlatformNotSupportedException exception
httpContext.Request.Headers["password"] = "password"; //.Headers.Add("blah", "blah") throws same error
httpContext.Request.Headers["token"] = "token"; //And so to .Headers.Set("blah", "blah")
return httpContext;
}
[TestMethod]
public void TagControllerGetTest()
{
// Arrange
Response<Tag> result = controller.Get();
// Assert
Assert.AreEqual(true, result.IsSuccess);
Assert.AreEqual(1, result.PayLoad.Count);
Assert.AreEqual("people", result.PayLoad[0].Name);
}
This is the code that is being tested.
public class TagController : ApiController
{
public IGenericService<Tag> _service;
public TagController()
{
_service = new TagService();
}
public TagController(IGenericService<Tag> service)
{
this._service = service;
}
// GET api/values
public Response<Tag> Get()
{
HttpContext context = HttpContext.Current;
string username = context.Request.Headers["username"].ToString();
string password = context.Request.Headers["password"].ToString();
string token = context.Request.Headers["token"].ToString();
return (Response<Tag>) _service.FindAll(username, password, token);
}
}
You can use this, it worked with:
Setting HttpContext.Current.Session in a unit test
User Anthony's answer, and add this code in GetMockedHttpContext:
request.SetupGet(req => req.Headers).Returns(new NameValueCollection());
then you can add:
HttpContextFactory.Current.Request.Headers.Add(key, value);
by this you can post headers. But unfortunately you have to use HttpContextFactory instead of HttpContext
Thanks to Adam Reed's blog it is possible to modify Headers collection using reflexion : MOCK HTTPCONTEXT.CURRENT.REQUEST.HEADERS UNIT TEST
HttpContext.Current = new HttpContext(
new HttpRequest("", "http://tempuri.org", ""), new HttpResponse(new StringWriter()));
NameValueCollection headers = HttpContext.Current.Request.Headers;
Type t = headers.GetType();
const BindingFlags nonPublicInstanceMethod = BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance;
t.InvokeMember("MakeReadWrite", nonPublicInstanceMethod, null, headers, null);
t.InvokeMember("InvalidateCachedArrays", nonPublicInstanceMethod, null, headers, null);
// eg. add Basic Authorization header
t.InvokeMember("BaseRemove", nonPublicInstanceMethod, null, headers, new object[] { "Authorization" });
t.InvokeMember("BaseAdd", nonPublicInstanceMethod, null, headers,
new object[] { "Authorization", new ArrayList{"Basic " + api_key} });
t.InvokeMember("MakeReadOnly", nonPublicInstanceMethod, null, headers, null);
I believe that in the API controller methods you can use "Request" property:
var testValue = this.Request.Headers.GetValues("headerKey").FirstOrDefault();
And then you can add test values in your unit tests this way:
var controller = new TestController();
controller.Request = new HttpRequestMessage();
controller.Request.Headers.Add("headerKey", "testValue");
Related
I am currently running unit tests on a controller that utilizes Url.Action to get the absolute path of two different routes. I am using Moq to set up the mock Action. The test is currently failing with the message 'Object reference not set to an instance of an object.'
[Edit - Clarifying the question]
When I debug the test, the Url shows to as a Mock<IUrlHelper> with an ActionContext property, but on the line where it calls the Url.Action, the Action property shows as null. What am I missing when setting up the Mock<IUrlHelper> that way the Url.Action doesn't come back as null?
I've checked multiple options for setting up Mock Url Actions, but nothing has seemed to work for me.
Here is the setup for the test
[Fact]
public async void SendGroup_PassesDetailsToNotificationService()
{
UrlActionContext actualContext = null;
var criteria = new NotificationCriteria { Section = 1, Subsection = 2 };
userSvc.GetNotificationGroupReturnValue = new List<NotificationRecipient>
{
new NotificationRecipient{ SmsAuth = true }
};
var actionContext = new ActionContext
{
ActionDescriptor = new ActionDescriptor(),
RouteData = new RouteData(),
};
var urlHelper = new Mock<IUrlHelper>();
urlHelper.SetupGet(h => h.ActionContext).Returns(actionContext);
urlHelper.Setup(h => h.Action(It.IsAny<UrlActionContext>()))
.Callback((UrlActionContext context) => actualContext = context);
controller.Url = urlHelper.Object;
var dummy = await controller.SendGroup(criteria);
Assert.Same(criteria, notificationSvc.SendGroupNotificationDetailUsed);
}
This is the code for the controller
[HttpPost]
public async Task<IActionResult> SendGroup([FromBody]NotificationCriteria criteria)
{
List<NotificationRecipient> recipients = (await userSvc.GetNotificationGroup(
criteria.Section, criteria.Subsection)).ToList();
if (!recipients.Any())
{
return BadRequest();
}
var uro = Url;
var sectionUrl = Url.Action("Index", "ReportHome", new {sectionId = criteria.Section}, Request.Scheme);
var profileUrl = Url.Action("Index", "Profile", null, Request.Scheme);
var detail = new NotificationDetail
{
SectionUrl = sectionUrl,
ProfileUrl = profileUrl,
Recipients = recipients
};
try
{
await notificationSvc.SendGroupNotification(detail);
}
catch
{
return StatusCode(500);
}
return Ok();
}
I'm writing a unit test using Moq and Microsoft's testing tools for an ASP.NET MVC 5 app that uses ASP.NET Identity. The test needs to simulate a logon failure in the Login POST action, such that it produces SignInStatus.Failure from the SignInManager.PasswordSignInAsync method of my AccountController:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login([Bind(Include = "Email,Password,RememberMe")]
LoginViewModel model, string returnUrl)
{
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password,
model.RememberMe, shouldLockout: true);
switch (result)
{
case SignInStatus.Success:
return Redirect(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
Here are the helper members/methods on my [TestClass]:
private Mock<HttpContextBase> _httpContextMock;
private Mock<HttpRequestBase> _httpRequestMock;
private Mock<HttpResponseBase> _httpResponseMock;
private MyApp.Controllers.AccountController _controller;
private Mock<ControllerContext> _controllerContextMock;
[TestInitialize]
public void SetupTests()
{
// Set up Moq
_httpContextMock = new Mock<HttpContextBase>();
_httpRequestMock = new Mock<HttpRequestBase>();
_httpResponseMock = new Mock<HttpResponseBase>();
_controller = new AccountController();
_controllerContextMock = new Mock<ControllerContext>();
}
private void SetContexts(IPrincipal user)
{
_httpContextMock.Setup(x => x.Items).Returns(new Dictionary<string, object>());
var sessionContainer = new HttpSessionStateContainer("id", new SessionStateItemCollection(),
new HttpStaticObjectsCollection(), 10, true,
HttpCookieMode.AutoDetect,
SessionStateMode.InProc, false);
//this adds aspnet session
_httpContextMock.Object.Items["AspSession"] = typeof(HttpSessionState).GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance,
null, CallingConventions.Standard,
new[] { typeof(HttpSessionStateContainer) },
null)
.Invoke(new object[] { sessionContainer });
_httpContextMock.Setup(x => x.User).Returns(user);
// https://stackoverflow.com/questions/674458/asp-net-mvc-unit-testing-controllers-that-use-urlhelper
_httpRequestMock.SetupGet(x => x.ApplicationPath).Returns("/");
_httpRequestMock.SetupGet(x => x.Url).Returns(new Uri("http://localhost/a", UriKind.Absolute));
_httpRequestMock.SetupGet(x => x.ServerVariables).Returns(new NameValueCollection());
_httpResponseMock.Setup(x => x.ApplyAppPathModifier(It.IsAny<string>())).Returns<string>(x => x);
var routes = new RouteCollection();
RouteConfig.RegisterRoutes(routes);
var helper = new UrlHelper(new RequestContext(_httpContextMock.Object, new RouteData()), routes);
_httpContextMock.SetupGet(x => x.Request).Returns(_httpRequestMock.Object);
_httpContextMock.SetupGet(x => x.Response).Returns(_httpResponseMock.Object);
// https://stackoverflow.com/questions/4066184/httpresponsebase-headers-are-empty-when-running-test
_httpResponseMock.Setup(r => r.OutputStream).Returns(new MemoryStream());
_httpResponseMock.Setup(r => r.Headers).Returns(new NameValueCollection());
var userStore = new Mock<IUserStore<ApplicationUser>>();
var userManager = new Mock<ApplicationUserManager>(userStore.Object);
var authenticationManager = new Mock<IAuthenticationManager>();
var signInManager = new Mock<ApplicationSignInManager>(userManager.Object, authenticationManager.Object);
var data = new Dictionary<string, object>()
{
{"a", "b"} // fake whatever you need here.
};
// https://stackoverflow.com/a/28574389/177416
_controller = new AccountController(
userManager.Object, signInManager.Object)
{
ControllerContext = new ControllerContext()
{
HttpContext = _httpContextMock.Object
}
};
_controller.ControllerContext.HttpContext.Items["owin.Environment"] = data;
_controller.Url = helper;
}
And here's my test method:
[TestMethod]
public async Task NoViewReturnedForFailedLogin()
{
// Arrange
var claimsIdentity = new ClaimsIdentity();
claimsIdentity.AddClaim(new Claim(ClaimTypes.Name, "fake#fake.org"));
var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
SetContexts(claimsPrincipal);
var vm = new LoginViewModel
{
Email = "faker#gmail.com",
Password = "Faker9999!",
RememberMe = false
};
_controllerContextMock.SetupGet(p => p.HttpContext.User.Identity.Name).Returns(vm.Email);
_controllerContextMock.SetupGet(p => p.HttpContext.Request.IsAuthenticated).Returns(false);
// Act
var result = await _controller.Login(vm, "http://www.google.com") as RedirectToRouteResult;
// Assert
// ? What do I assert?
}
As you can see, I have mock userStore, userManager, authenticationManager, and signInManager. If I step through the Login action, result always returns SignInStatus.Success.
Is there a way to have it fail? What should the test assert?
We are using C#, WebAPI, Moq and Nunit. I have a MarketingController which is an API controller. I need to mock
HTTPContext.Current.Items["MS_HttpRequestMessage"] as HttpRequestMessage
I tried these approaches:
In my
[TestInitialize]
Setup()
{
MockHttpContextBase = new Mock<HttpContextBase>(MockBehavior.Loose);
MockHttpSession = new Mock<HttpSessionStateBase>(MockBehavior.Loose);
var mockHttpContext = new MockHttpContext();
_marketingController = new MarketingController
{
ControllerContext = new HttpControllerContext()
//{
// HttpContext = mockHttpContext.HttpContext.Object
//}
};
var httpContext = new HttpContext(httpRequest, httpResponse);
context.SetupGet(e => e.Request).Returns(request.Object);
var sessionContainer = new HttpSessionStateContainer("id",
new SessionStateItemCollection(),
new HttpStaticObjectsCollection(), 10, true,
HttpCookieMode.AutoDetect,
SessionStateMode.InProc, false);
SessionStateUtility.AddHttpSessionStateToContext(httpContext, sessionContainer);
HttpContext.Current = httpContext;
HttpContext.Current.Items.Add("MS_HttpRequestMessage", "HelloMan");
}
In the actual code, I have a method to retrieve currentItems from the HttpContext.
protected HttpSessionStateBase GetMysessforServ()
{
var request = HttpContext.Current.Items[MS_HttpRequestMessage"] as HttpRequestMessage;
if (request == null)
return null;
var httpcontent = (HttpContextWraper)request.Properties[""]
....
....
....
....
return httpContext.session
}
Here my unit test are failing because the mock is getting executed and the call for HTTPContext.Current.Items["MS_HttpRequestMessage"] as HttpRequestMessage is always returning null.
I am also mocking HttpContext for other controller and it works fine but this one is api controller
Please advise
In my Asp.net core 1 app I have controller with following method:
[Microsoft.AspNetCore.Mvc.HttpPost()]
[Microsoft.AspNetCore.Mvc.RequireHttps]
public async System.Threading.Tasks.Task<Microsoft.AspNetCore.Mvc.IActionResult> Save()
{
if (ModelState.IsValid)
{
try
{
var pass = Request.Form["password"].ToString();
var pass1 = Request.Form["password1"].ToString();
if (!pass.Equals(pass1))
{
return View("~/Views/PasswordRecovery.cshtml");
}
}
catch (System.Exception ex)
{
return View("~/Views/Message.cshtml");
}
}
return View("~/Views/Message.cshtml");
}
I want to write a test for this method. So I have written this:
[Xunit.Fact]
public async System.Threading.Tasks.Task SavePassNotEqualTest()
{
var controller = new Controllers.PasswordRecoveryController(_mockRepo.Object);
var dic = new System.Collections.Generic.Dictionary<string, Microsoft.Extensions.Primitives.StringValues>();
dic.Add("password", "test");
dic.Add("password1", "test1");
var collection = new Microsoft.AspNetCore.Http.FormCollection(dic);
controller.Request.Form = collection; //request is null
var result = await controller.Save();
var viewResult = Xunit.Assert.IsType<Microsoft.AspNetCore.Mvc.ViewResult>(result);
Xunit.Assert.Equal("~/Views/Message.cshtml", viewResult.ViewName);
}
The problem is that I need to set some test values to Form, Form is in Request, and Request is NULL. I can not find, how can I create some not NULL request and fill it's Form with values.
EDIT
Answers helped me to finish up with following solution:
I've created a method that will return a FormCollection:
private Microsoft.AspNetCore.Http.FormCollection GetFormCollection()
{
var dic = new System.Collections.Generic.Dictionary<string, Microsoft.Extensions.Primitives.StringValues>();
dic.Add("password", "test");
dic.Add("password1", "test1");
return new Microsoft.AspNetCore.Http.FormCollection(dic);
}
And my test method is:
[Xunit.Fact]
public async System.Threading.Tasks.Task SavePassNotEqualTest()
{
var controller = new Findufix.Controllers.PasswordRecoveryController(_mockRepo.Object);
var httpContext = new Moq.Mock<Microsoft.AspNetCore.Http.HttpContext>();
httpContext.Setup( x => x.Request.Form).Returns(GetFormCollection());
controller.ControllerContext.HttpContext = httpContext.Object;
var result = await controller.Save();
var viewResult = Xunit.Assert.IsType<Microsoft.AspNetCore.Mvc.ViewResult>(result);
Xunit.Assert.Equal("~/Views/PasswordRecovery.cshtml", viewResult.ViewName);
}
If you pass a DefaultHttpContext to your controller, Request won't be null and you can assign the form to Request.Form. No mocking required.
[Xunit.Fact]
public async System.Threading.Tasks.Task SavePassNotEqualTest()
{
var controller = new Controllers.PasswordRecoveryController(_mockRepo.Object);
var dic = new System.Collections.Generic.Dictionary<string, Microsoft.Extensions.Primitives.StringValues>();
dic.Add("password", "test");
dic.Add("password1", "test1");
var collection = new Microsoft.AspNetCore.Http.FormCollection(dic);
// Give the controller an HttpContext.
controller.ControllerContext.HttpContext = new DefaultHttpContext();
// Request is not null anymore.
controller.Request.Form = collection;
var result = await controller.Save();
var viewResult = Xunit.Assert.IsType<Microsoft.AspNetCore.Mvc.ViewResult>(result);
Xunit.Assert.Equal("~/Views/Message.cshtml", viewResult.ViewName);
}
With Moq, you can do it like this:
var httpContext = new Mock<HttpContextBase>();
httpContext.Setup(c => c.Request.Form).Returns(delegate()
{
var formVaues = new NameValueCollection();
formVaues .Add("Id", "123");
formVaues .Add("Name", "Smith");
return formVaues ;
});
In Moq you can try to use Setup() or SetupGet() to teach it to return something that you need
something along the lines of
controller.SetupGet(x => x.Request.Form).Returns(collection);
I have a working application for which I need to now add a full set of unit tests. The current code stores the user information as follows:
HttpContext.Current.Session["UserInfo"] = userData;
I'm using moq for my testing, and my unit test has the following code:
var server = new Mock<HttpServerUtilityBase>(MockBehavior.Loose);
var response = new Mock<HttpResponseBase>(MockBehavior.Strict);
var request = new Mock<HttpRequestBase>(MockBehavior.Strict);
var context = new Mock<HttpContextBase>();
context.SetupGet(x => x.Request).Returns(request.Object);
context.SetupGet(x => x.Response).Returns(response.Object);
context.SetupGet(x => x.Server).Returns(server.Object);
var controller = new LoginController();
controller.ControllerContext = new ControllerContext(
context.Object,
new RouteData(),
controller);
JsonResult result = controller.LoginUser(
new LoginHelper {
userName = "myusername",
password = "invalidpassword"
}
) as JsonResult;
Of course when I get to where the login process tries to create the session data, I get an "Object reference not set to an instance of an object." error becuase HttpContext.Current is null.
Some research has shown me that using Current isn't exactly compatible with MSTest, so I understand that I might need to change the way I store/load my user information. However, I would like some advice on which way to go here.
I would appreciate any suggestions on either how to get my unit tests to work, or a different method to store the user info to make it more compatible with unit tests.
This was the solution I came up with. It is a combination of a few things I found in various places including here:
First, I created 2 classes .. MockHttpSession, and MockHelpers
public class MockHttpSession : HttpSessionStateBase
{
Dictionary<string, object> m_SessionStorage = new Dictionary<string, object>();
public override object this[string name]
{
get { return m_SessionStorage[name]; }
set { m_SessionStorage[name] = value; }
}
public override void Abandon()
{
// Do nothing
}
}
public class MockHelpers
{
public static HttpContext FakeHttpContext()
{
var httpRequest = new HttpRequest("", "http://localhost/", "");
var stringWriter = new StringWriter();
var httpResponse = new HttpResponse(stringWriter);
var httpContext = new HttpContext(httpRequest, httpResponse);
var sessionContainer = new HttpSessionStateContainer(
"id",
new SessionStateItemCollection(),
new HttpStaticObjectsCollection(),
10,
true,
HttpCookieMode.AutoDetect,
SessionStateMode.InProc,
false);
SessionStateUtility.AddHttpSessionStateToContext(httpContext, sessionContainer);
return httpContext;
}
}
Then I changed my unit test code to use these:
var server = new Mock<HttpServerUtilityBase>(MockBehavior.Loose);
var response = new Mock<HttpResponseBase>(MockBehavior.Strict);
var request = new Mock<HttpRequestBase>(MockBehavior.Strict);
var session = new MockHttpSession();
var context = new Mock<HttpContextBase>();
context.SetupGet(x => x.Request).Returns(request.Object);
context.SetupGet(x => x.Response).Returns(response.Object);
context.SetupGet(x => x.Server).Returns(server.Object);
context.SetupGet(x => x.Session).Returns(session);
HttpContext.Current = MockHelpers.FakeHttpContext();
var controller = new LoginController();
controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);
JsonResult result = controller.LoginUser(new LoginHelper { userName = "MyUserName", password = ""InvalidPassword }) as JsonResult;
This was able to successfully test the code with both valid and invalid passwords.
Thanks everyone for your help