Mock HttpActionContext for Unit Test - c#

I have a custom authorization attribute seen below and I am trying to write a unit test to test its functionality.
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (actionContext.Request.Headers.Authorization != null)
{
// get the Authorization header value from the request and base64 decode it
string userInfo = Encoding.Default.GetString(Convert.FromBase64String(actionContext.Request.Headers.Authorization.Parameter));
// custom authentication logic
if (string.Equals(userInfo, string.Format("{0}:{1}", "user", "pass")))
{
IsAuthorized(actionContext);
}
else
{
HandleUnauthorizedRequest(actionContext);
}
}
else
{
HandleUnauthorizedRequest(actionContext);
}
}
protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext)
{
actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized)
{
ReasonPhrase = "Unauthorized"
};
}
My problem is that when I try to test this I get "System.NullReferenceException: Object reference not set to an instance of an object." I have tried to set the actionContext's request.headers.authorization value but it has no setter. When I try mocking the HttpActionContext it says it cannot convert from a mock HttpActionContext to a real one. Below is my test code
public class HttpBasicAuthorizeAttributeTest
{
private HttpBasicAuthorizeAttribute ClassUnderTest { get; set; }
private HttpActionContext actionContext { get; set; }
[TestMethod]
public void HttpBasicAuthorizeAttribute_OnAuthorize_WithAuthorizedUser_ReturnsAuthorization()
{
var context = new Mock<HttpActionContext>();
context.Setup(x => x.Request.Headers.Authorization.Parameter).Returns("bzUwkDal=");
ClassUnderTest.OnAuthorization(context);
}
[TestInitialize]
public void Initialize()
{
ClassUnderTest = new HttpBasicAuthorizeAttribute();
actionContext = new HttpActionContext();
}
}
*Left out the assert until I can even get the HttpActionContext to work

You can use the actual objects and provide it to the mock in order to exercise the method under test as Moq in unable to mock the non-virtual members.
[TestMethod]
public void HttpBasicAuthorizeAttribute_OnAuthorize_WithAuthorizedUser_ReturnsAuthorization() {
//Arrange
var context = new HttpActionContext();
var headerValue = new AuthenticationHeaderValue("Basic", "bzUwkDal=");
var request = new HttpRequestMessage();
request.Headers.Authorization = headerValue;
var controllerContext = new HttpControllerContext();
controllerContext.Request = request;
context.ControllerContext = controllerContext;
//Act
ClassUnderTest.OnAuthorization(context);
//Assert
//...
}

Related

I created this Http Azure function with SendGrid and not sure how to write a unit test to call it with different email test case

I was asked to create this Http azure function bellow. I'm trying to write a mock unit test to call this processEmail. I'm guessing my req will be the entry point. My unit test should have different email value to test. If I could have an example from my function bellow that would be great.
public async Task<IActionResult> ProcessEmail(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)]
HttpRequest req, ILogger log) {
log.SmpLogInformation("Initializing SendGrid ProcessEmail");
var client =
new SendGridClient("key");
var requestBody = new StreamReader(req.Body).ReadToEnd();
var data = JsonConvert.DeserializeObject<EmailContent>(requestBody);
if(data == null) {
throw new ArgumentNullException("Can't proced further");
}
var message = new SendGridMessage();
message.AddTo(data.Email);
message.SetFrom(new EmailAddress("ff.com"));
message.AddContent("text/html", HttpUtility.HtmlDecode(data.Body));
message.SetSubject(data.Subject);
log.SmpLogDebug("Email sent through Send Grid");
await client.SendEmailAsync(message);
return (ActionResult)new OkObjectResult("Submited sucessfully");
}
public class EmailContent {
public string? Email { get; set; }
public string? Subject { get; set; }
public string Body { get; set; } = "";
}
}
Firstly, you need to mock your SendGridClient otherwise you will be making actual requests during your unit tests which wouldn't be great.
Looking at the SendGrid code, SendGridClient implements an interface ISendGridClient. Instead of new'ing up the client using var client = new SendGridClient("key");, you could use dependency injection to inject an instance of ISendGridClient via the constructor:
public class ProcessEmail
{
private readonly ISendGridClient _client;
public ProcessEmail(ISendGridClient client)
{
_client = client;
}
You can then remove this line:
var client = new SendGridClient("key");
And then a slight change to this line to use the injected object:
await _client.SendEmailAsync(message);
Then when you come to write your unit test, you will be able to have a mock for the ISendGridClient interface which allows you to setup and verify behaviour of objects. Here's an example using Moq:
[TestClass]
public class ProcessEmailTests
{
private readonly Mock<ISendGridClient> _mockSendGridClient = new Mock<ISendGridClient>();
private readonly Mock<ILogger> _mockLogger = new Mock<ILogger>();
private ProcessEmail _processEmail;
private MemoryStream _memoryStream;
[TestInitialize]
public void Initialize()
{
// initialize the ProcessEmail class with a mock object
_processEmail = new ProcessEmail(_mockSendGridClient.Object);
}
[TestMethod]
public async Task GivenEmailContent_WhenProcessEmailRuns_ThenEmailSentViaSendgrid()
{
// arrange - set up the http request which triggers the run method
var expectedEmailContent = new ProcessEmail.EmailContent
{
Subject = "My unit test",
Body = "Woohoo it works",
Email = "unit#test.com"
};
var httpRequest = CreateMockRequest(expectedEmailContent);
// act - call the Run method of the ProcessEmail class
await _processEmail.Run(httpRequest, _mockLogger.Object);
// assert - verify that the message being sent into the client method has the expected values
_mockSendGridClient
.Verify(sg => sg.SendEmailAsync(It.Is<SendGridMessage>(sgm => sgm.Personalizations[0].Tos[0].Email == expectedEmailContent.Email), It.IsAny<CancellationToken>()), Times.Once);
}
private HttpRequest CreateMockRequest(object body = null, Dictionary<string, StringValues> headers = null, Dictionary<string, StringValues> queryStringParams = null, string contentType = null)
{
var mockRequest = new Mock<HttpRequest>();
if (body != null)
{
var json = JsonConvert.SerializeObject(body);
var byteArray = Encoding.ASCII.GetBytes(json);
_memoryStream = new MemoryStream(byteArray);
_memoryStream.Flush();
_memoryStream.Position = 0;
mockRequest.Setup(x => x.Body).Returns(_memoryStream);
}
if (headers != null)
{
mockRequest.Setup(i => i.Headers).Returns(new HeaderDictionary(headers));
}
if (queryStringParams != null)
{
mockRequest.Setup(i => i.Query).Returns(new QueryCollection(queryStringParams));
}
if (contentType != null)
{
mockRequest.Setup(i => i.ContentType).Returns(contentType);
}
mockRequest.Setup(i => i.HttpContext).Returns(new DefaultHttpContext());
return mockRequest.Object;
}
}
Full function code:
public class ProcessEmail
{
private readonly ISendGridClient _client;
public ProcessEmail(ISendGridClient client)
{
_client = client;
}
[FunctionName("ProcessEmail")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("Initializing SendGrid ProcessEmail");
var requestBody = new StreamReader(req.Body).ReadToEnd();
var data = JsonConvert.DeserializeObject<EmailContent>(requestBody);
if (data == null)
{
throw new ArgumentNullException("Can't proceed further");
}
var message = new SendGridMessage();
message.AddTo(data.Email);
message.SetFrom(new EmailAddress("ff.com"));
message.AddContent("text/html", HttpUtility.HtmlDecode(data.Body));
message.Subject = data.Subject;
log.LogDebug("Email sent through Send Grid");
await _client.SendEmailAsync(message);
return (ActionResult)new OkObjectResult("Submited sucessfully");
}
public class EmailContent
{
public string? Email { get; set; }
public string? Subject { get; set; }
public string Body { get; set; } = "";
}
}

FakeXRMEasy: Using AddFakeMessageExecutor to override update request behaviour

I am trying to create a test for a situation where an Update request throws an exception. Is this possible to do using FakeXRMEasy? I have tried using AddFakeMessageExecutor, but at the moment it is not working:
My fake message executor class:
public class UpdateExecutor : IFakeMessageExecutor
{
public bool CanExecute(OrganizationRequest request)
{
return request is UpdateRequest;
}
public OrganizationResponse Execute(
OrganizationRequest request,
XrmFakedContext ctx)
{
throw new Exception();
}
public Type GetResponsibleRequestType()
{
return typeof(UpdateRequest);
}
}
Use in test:
fakeContext.Initialize(new Entity[] { agreement });
fakeContext.AddFakeMessageExecutor<UpdateRequest>(new UpdateExecutor());
fakeContext.ExecuteCodeActivity<AgreementConfirmationWorkflow>(fakeContext.GetDefaultWorkflowContext());
And in the workflow the update request is called:
var workflowContext = executionContext.GetExtension<IWorkflowContext>();
var serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
IOrganizationService service = serviceFactory.CreateOrganizationService(workflowContext.UserId);
/// some code to retrieve entity and change attributes ///
service.Update(entity);
I wanted this to throw an exception, but at the moment the update request is completing successfully. How can I make this work?
IFakeMessageExecutor only works when you call IOrganizationService.Execute method. So, if you change your service.Update(entity); line of code for service.Execute(new UpdateRequest { Target = entity}); it should work.
Here is a full working example for reference:
CodeActivity
public class RandomCodeActivity : CodeActivity
{
protected override void Execute(CodeActivityContext context)
{
var workflowContext = context.GetExtension<IWorkflowContext>();
var serviceFactory = context.GetExtension<IOrganizationServiceFactory>();
var service = serviceFactory.CreateOrganizationService(workflowContext.UserId);
var accountToUpdate = new Account() { Id = new Guid("e7efd527-fd12-48d2-9eae-875a61316639"), Name = "A new faked name!" };
service.Execute(new UpdateRequest { Target = accountToUpdate });
}
}
FakeMessageExecutor instance
public class FakeUpdateRequestExecutor : IFakeMessageExecutor
{
public bool CanExecute(OrganizationRequest request)
{
return request is UpdateRequest;
}
public OrganizationResponse Execute(OrganizationRequest request, XrmFakedContext ctx)
{
throw new InvalidPluginExecutionException("Throwing an Invalid Plugin Execution Exception for test purposes");
}
public Type GetResponsibleRequestType()
{
return typeof(UpdateRequest);
}
}
Test (uses xUnit test lib)
[Fact]
public void UpdateAccount_WithUpdateExecutorThrowingAnException_ExceptionThrown()
{
//Assign
var context = new XrmFakedContext
{
ProxyTypesAssembly = Assembly.GetAssembly(typeof(Account))
};
var account = new Account() { Id = new Guid("e7efd527-fd12-48d2-9eae-875a61316639"), Name = "Faked Name" };
context.Initialize(new List<Entity>() { account });
context.AddFakeMessageExecutor<UpdateRequest>(new FakeUpdateRequestExecutor());
var service = context.GetOrganizationService();
//Act
//Assert
Assert.Throws<InvalidPluginExecutionException>(() => context.ExecuteCodeActivity<RandomCodeActivity>(account));
}

User.Identity.GetUserId() Owin Moq unit test

I have ChangePassword method where I have User.Identity.GetUserId() to find UserId.
Problem: It always return null. Don't understand why.
I read in another post that the GetUserById use below line of code to find Id. I am not sure how do I mock ClaimsTypes.NameIdentifier.
return ci.FindFirstValue(ClaimTypes.NameIdentifier);
ChangePassword method (method to be unit testes)
public async Task<IHttpActionResult> ChangePassword(string NewPassword, string OldPassword)
{
_tstService = new TestService();
IdentityResult result = await _tstService.ChangePassword(User.Identity.GetUserId(), OldPassword, NewPassword);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
Unit Test
var mock = new Mock<MyController>();
mock.CallBase = true;
var obj = mock.Object;
obj.ControllerContext = new HttpControllerContext { Request = new HttpRequestMessage() };
obj.Request.SetOwinContext(CommonCodeHelper.mockOwinContext());
IPrincipal user = GetPrincipal();
obj.ControllerContext.RequestContext.Principal = user;
var result = await obj.ChangePassword(dto);
//GetPrincipal
public static IPrincipal GetPrincipal()
{
var user = new Mock<IPrincipal>();
var identity = new Mock<IIdentity>();
identity.Setup(x => x.Name).Returns("User1#Test.com");
identity.Setup(p => p.IsAuthenticated).Returns(true);
user.Setup(x => x.Identity).Returns(identity.Object);
Thread.CurrentPrincipal = user.Object;
return user.Object;
}
IOwinContext mocking code
public static IOwinContext mockOwinContext()
{
var owinMock = new Mock<IOwinContext>();
owinMock.Setup(o => o.Authentication.User).Returns(new ClaimsPrincipal());
owinMock.Setup(o => o.Request).Returns(new Mock<OwinRequest>().Object);
owinMock.Setup(o => o.Response).Returns(new Mock<OwinResponse>().Object);
owinMock.Setup(o => o.Environment).Returns(new Dictionary<string, object> { { "key1", 123 } });
var traceMock = new Mock<TextWriter>();
owinMock.Setup(o => o.TraceOutput).Returns(traceMock.Object);
var userStoreMock = new Mock<IUserStore<IfsUser>>();
userStoreMock.Setup(s => s.FindByIdAsync("User1#ifstoolsuite.com")).ReturnsAsync(new IfsUser
{
Id = "User1#test.com",
FirstName = "Test",
LastName = "User1",
Email = "User1#test.com",
UserName = "User1#test.com",
});
var applicationUserManager = new IfsUserManager(userStoreMock.Object);
owinMock.Setup(o => o.Get<IfsUserManager>(It.IsAny<string>())).Returns(applicationUserManager);
return owinMock.Object;
}
Your GetPrincipal can be updated to use claims.
public static IPrincipal GetPrincipal() {
//use an actual identity fake
var username = "User1#Test.com";
var identity = new GenericIdentity(username, "");
//create claim and add it to indentity
var nameIdentifierClaim = new Claim(ClaimTypes.NameIdentifier, username);
identity.AddClaim(nameIdentifierClaim);
var user = new Mock<IPrincipal>();
user.Setup(x => x.Identity).Returns(identity);
Thread.CurrentPrincipal = user.Object;
return user.Object;
}
Here is an example that shows how the above approach works.
public partial class MiscUnitTests {
[TestClass]
public class IdentityTests : MiscUnitTests {
Mock<IPrincipal> mockPrincipal;
string username = "test#test.com";
[TestInitialize]
public override void Init() {
//Arrange
var identity = new GenericIdentity(username, "");
var nameIdentifierClaim = new Claim(ClaimTypes.NameIdentifier, username);
identity.AddClaim(nameIdentifierClaim);
mockPrincipal = new Mock<IPrincipal>();
mockPrincipal.Setup(x => x.Identity).Returns(identity);
mockPrincipal.Setup(x => x.IsInRole(It.IsAny<string>())).Returns(true);
}
[TestMethod]
public void Should_GetUserId_From_Identity() {
var principal = mockPrincipal.Object;
//Act
var result = principal.Identity.GetUserId();
//Asserts
Assert.AreEqual(username, result);
}
[TestMethod]
public void Identity_Should_Be_Authenticated() {
var principal = mockPrincipal.Object;
//Asserts
Assert.IsTrue(principal.Identity.IsAuthenticated);
}
}
}
You have some design issues. Creating a concrete TestService will cause problems if it as connecting to an actual implementation. That becomes an integration test. Abstract that dependency as well.
public interface ITestService {
Task<IdentityResult> ChangePassword(string userId, string oldPassword, string newPassword);
}
public abstract class MyController : ApiController {
private ITestService service;
protected MyController(ITestService service) {
this.service = service;
}
public async Task<IHttpActionResult> ChangePassword(string NewPassword, string OldPassword) {
IdentityResult result = await service.ChangePassword(User.Identity.GetUserId(), OldPassword, NewPassword);
if (!result.Succeeded) {
return GetErrorResult(result);
}
return Ok();
}
}
Also you should not mock the System under test. You should mock the dependencies of the SUT. Based on your method to be tested and what you indicated in the comments that MyController is an abstract class, the following test should apply
[TestClass]
public class MyControllerTests {
public class FakeController : MyController {
public FakeController(ITestService service) : base(service) { }
}
[TestMethod]
public void TestMyController() {
//Arrange
var mockService = new Mock<ITestService>();
mockService
.Setup(m => m.ChangePassword(....))
.ReturnsAsync(....);
var controller = new FakeController(mockService.Object);
//Set a fake request. If your controller creates responses you will need this
controller.Request = new HttpRequestMessage {
RequestUri = new Uri("http://localhost/api/my")
};
controller.Configuration = new HttpConfiguration();
controller.User = GetPrincipal();
//Act
var result = await controller.ChangePassword("NewPassword", "OldPassword");
//Assert
//...
}
}

How to unit test AntiforgeryToken filter for web api 2

I am trying to write unit test case using MsTest for custom filter which has the logic to validate the Antiforgerytoken for POST method in ASP.NET WEB API 2 project.
[AttributeUsage(AttributeTargets.Method, Inherited = true)]
public class ValidateJsonAntiForgeryTokenAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
try
{
string cookieToken = null;
string formToken = null;
if (actionContext.Request.IsAjaxRequest())
{
IEnumerable<string> tokenHeaders;
if (actionContext.Request.Headers.TryGetValues("__RequestVerificationToken", out tokenHeaders))
{
string[] tokens = tokenHeaders.First().Split(':');
if (tokens.Length == 2)
{
cookieToken = tokens[0].Trim();
formToken = tokens[1].Trim();
}
}
if (cookieToken != null && formToken !=null)
{
AntiForgery.Validate(cookieToken, formToken);
}
else
{
AntiForgery.Validate();
}
}
}
catch (Exception ex)
{
ErrorSignal.FromCurrentContext().Raise(ex);
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Forbidden);
}
}
}
In the below code IsAjaxRequest is an extension method
public static class HttpRequestMessageExtensions
{
public static bool IsAjaxRequest(this HttpRequestMessage request)
{
IEnumerable<string> headers;
if (request.Headers.TryGetValues("X-Requested-With", out headers))
{
var header = headers.FirstOrDefault();
if (!string.IsNullOrWhiteSpace(header))
{
return header.ToLowerInvariant() == "xmlhttprequest";
}
}
return false;
}
}
Here my issue how to mock the IsAjaxRequest and how to pass actionContext parameter to the OnActionExecuting method.
Can anyone help me to provide some code samples regarding this?
IsAjaxRequest is an extension method which mean it is a static method.
You shouldn't mock static methods. You should test it as a part of your method under test behavior.
The following example shows how to test invalid request:(I removed the :: ErrorSignal.FromCurrentContext().Raise(ex); since I didn't know which assembly to add... so add the missing assert\s in your test...)
[TestMethod]
public void TestMethod1()
{
var target = new ValidateJsonAntiForgeryTokenAttribute();
var requestMessage = new HttpRequestMessage();
requestMessage.Headers.Add("X-Requested-With", new[] {"xmlhttprequest"});
var fakeDescriptor = new Mock<HttpActionDescriptor>();
var controllerContext = new HttpControllerContext {Request = requestMessage};
var context = new HttpActionContext(controllerContext, fakeDescriptor.Object);
target.OnActionExecuting(context);
Assert.AreEqual(HttpStatusCode.Forbidden, actionContext.Response.StatusCode);
}
You can mock the static method if you really need to. There's my example of the test with Typemock isolator:
[TestMethod, Isolated]
public void TestValidate()
{
//How to fake action context to further passing it as a parameter:
var fake = Isolate.Fake.AllInstances<HttpActionContext>();
Isolate.Fake.StaticMethods<HttpActionContext>();
//How to mock IsAjaxRequset:
var request = new HttpRequestMessage();
Isolate.WhenCalled(() => request.IsAjaxRequest()).WillReturn(true);
//Arrange:
ValidateJsonAntiForgeryTokenAttribute target = new ValidateJsonAntiForgeryTokenAttribute();
Isolate.WhenCalled(() => AntiForgery.Validate()).IgnoreCall();
//Act:
target.OnActionExecuting(fake);
//Assert:
Isolate.Verify.WasCalledWithAnyArguments(() => AntiForgery.Validate());
}

How do I use NinjectSelfHostBootstrapper in my tests and have more than one test pass?

Here's my NUnit test code (simplified to isolate the issue):
public class MyController : ApiController
{
[HttpGet]
[Route("api/route")]
public string Route1()
{
return "route";
}
}
[TestFixture]
public class MyTestFixture
{
private NinjectSelfHostBootstrapper _bootstrapper;
[SetUp]
public void SetUp()
{
var config = new HttpSelfHostConfiguration("http://localhost:8767");
config.MapHttpAttributeRoutes();
var kernel = new StandardKernel();
this._bootstrapper = new NinjectSelfHostBootstrapper(() => kernel, config);
this._bootstrapper.Start();
}
[TearDown]
public void TearDown()
{
if (this._bootstrapper != null)
{
this._bootstrapper.Dispose();
}
}
[Test]
public void MyTest1()
{
using (var client = new HttpClient())
{
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:8767/api/route");
var response = client.SendAsync(request).Result;
var content = response.Content.ReadAsStringAsync().Result;
Assert.That(content, Is.EqualTo("\"route\""));
}
}
[Test]
public void MyTest2()
{
using (var client = new HttpClient())
{
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:8767/api/route");
var response = client.SendAsync(request).Result;
var content = response.Content.ReadAsStringAsync().Result;
Assert.That(content, Is.EqualTo("\"route\""));
}
}
}
When I run that using the default NinjectSelfHostBootstrapper the first test passes fine, but the second throws an exception, saying:
A registration already exists for URI 'http://localhost:8767/'.
When I examine the code for these two files:
https://github.com/ninject/Ninject.Web.WebApi/blob/master/src/Ninject.Web.WebApi.Selfhost/NinjectWebApiSelfHost.cs
https://github.com/ninject/Ninject.Web.Common/blob/master/src/Ninject.Web.Common.SelfHost/NinjectSelfHostBootstrapper.cs
I can see why - an HttpSelfHostServer is created in the latter and OpenAsync() is called on the server, but CloseAsync() isn't called on the server - is this a bug or am I doing something wrong? How do I get NinjectSelfHostBootstrapper to work with subsequently called tests?

Categories