Mock the Request when testing a Razor Page - c#

I need to create a unit test for this function that resides inside the HomeModel razor page
public async Task<IActionResult> OnGetCurrencyAsync(string currency, CancellationToken ct = default)
{
var returnUrl = Request.Headers["Referer"].ToString();
var path = new System.Uri(returnUrl).LocalPath;
if (string.IsNullOrWhiteSpace(path) || !Url.IsLocalUrl(path))
returnUrl = Url.Content("~/");
var session = await _currentUserService.GetOrInitializeSessionAsync(ct);
if (!currency.IsNullOrEmpty())
{
session.Currency = currency;
await _currentUserService.SetSession(session, ct);
}
return Redirect(returnUrl);
}
Till now I've created the following test
[Fact]
public async Task Test1()
{
var returnUrl = "https://localhost:44317/paris";
var currentuserService = new Mock<ICurrentUserService>();
var options = new Mock<IOptions<Core.Configuration.AppSettings>>();
var navigationMenu = new Mock<INavigationMenu>();
var productModelService = new Mock<IProductModelService>();
var userSavedProductRepository = new Mock<IUserSavedProductRepository>();
var userSavedProductService = new Mock<IUserSavedProductService>();
var homePage = new HomeModel(currentuserService.Object, options.Object, navigationMenu.Object, productModelService.Object, userSavedProductService.Object, userSavedProductRepository.Object);
var res = await homePage.OnGetCurrencyAsync("EUR", CancellationToken.None);
Assert.IsType<RedirectResult>(res);
var redirectResult = (RedirectResult)res;
Assert.True(returnUrl == redirectResult.Url);
}
But when I execute it, I got that .Request is null..
How can I correctly set it up?

The PageContext of the subject PageModel needs a HttpContext that contains the desired Request setup to satisfy the subject under test.
Reference: Razor Pages unit tests in ASP.NET Core: Unit tests of the page model methods
//Arrange
var returnUrl = "https://localhost:44317/paris";
//...code omitted for brevity
// use a default context to have access to a request
var httpContext = new DefaultHttpContext();
httpContext.Request.Headers["Referer"] = returnUrl; //<--
//these are needed as well for the page context
var modelState = new ModelStateDictionary();
var actionContext = new ActionContext(httpContext, new RouteData(), new PageActionDescriptor(), modelState);
var modelMetadataProvider = new EmptyModelMetadataProvider();
var viewData = new ViewDataDictionary(modelMetadataProvider, modelState);
// need page context for the page model
var pageContext = new PageContext(actionContext) {
ViewData = viewData
};
//create model with necessary dependencies applied
var homePage = new HomeModel(currentuserService.Object, options.Object, navigationMenu.Object, productModelService.Object, userSavedProductService.Object, userSavedProductRepository.Object) {
PageContext = pageContext, //<--
Url = new UrlHelper(actionContext)
};
//Act
//...omitted for brevity

Related

How mock methods of repository in Service for Unit test

I try to have a unit test for my service, I mocked everything needed, How I can Mock repository methods that Service is calling so that has value and code is not breaking,
This is my unit test:
public async Task Updateuser_ReturnsResponse()
{
// Arrange
var request = new UpdateUserRequest()
{
Guid = new Guid("92296ac1-f8e1-489a-a312-6ea9d31d60f8"),
FirstName = "TestFirst",
LastName = "TestLast",
PhoneWork = "9495467845",
EmailWork = "test123#yahoo.com",
};
var respose = new UpdateUserResponse()
{
Success = true
};
var getGuidRequest = new GetGuidRequest()
{
Guid = request.Guid
};
var getGuidResponse = new GetGuidResponse()
{
Guid = request.Guid
};
var mockUserRepository = new Mock<IUserRepository>();
var mockAwsProxy = new Mock<IAwsProxy>();
mockUserRepository.Setup(s => s.UpdateUserAsync(request)).ReturnsAsync(respose);
mockUserRepository.Setup(i => i.GetGuidAsync(getGuidRequest)).ReturnsAsync(getGuidResponse);
var sut = new FromService.UserService(....);
// Act
var response = await sut.UpdateUserAsync(request);
// Assert
Assert.NotNull(response);
Assert.True(response.Success);
}
My problem is when calling - var response = await sut.UpdateUserAsync(request); It goese to service and this GuidResponse is empty so it break after as shows GuidResponse Null:
public async Task<UpdateUserResponse> UpdateUserAsync(UpdateUserRequest request)
{
if (request.EmailWork.HasValue() || request.Role.HasValue())
{
var GuidResponse = await userRepository.GetGuidAsync(new GetGuidRequest
{
Guid = request.Guid
});
// it breaks here because GuidResponse is Null.
if (GuidResponse.Guid != null && request.EmailWork.HasValue())
{
.......
It fails because the setup does not match what was actually given to the mock when the test was exercised.
Use It.Is<T>() to match the passed argument parameter
//...omitted for brevity
mockUserRepository
.Setup(_ => _.GetGuidAsync(It.Is<GetGuidRequest>(x => x.Guid == request.Guid)))
.ReturnsAsync(getGuidResponse);
//...omitted for brevity
assuming the mocked repository is what was injected into the SUT
Reference Moq Quickstart: Matching Arguments

Core 2 Razor Pages OnGetAsync Load Static File

Is there a way with Razor pages code behind to load a static file as I can do with a traditional MVC controller? I've been playing around with this for a few hours this morning and can't seem to find a way to accomplish this. Any input is appreciated!
Razor Page Code Behind:
public async void OnGetAsync()
{
var request = HttpContext.Request;
var sessionId = request.Query.FirstOrDefault().Value;
var session = await _httpService.ValidateSession(sessionId);
if (!string.IsNullOrEmpty(session?.UserId))
{
var claims = new List<Claim>()
{
new Claim(CustomClaimTypes.UserId, session.UserId),
new Claim(CustomClaimTypes.BuId, session.BuId),
new Claim(CustomClaimTypes.SecurityLevel, session.SecurityLevel)
};
var identity = new ClaimsIdentity(claims, "TNReadyEVP");
var principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal,
new AuthenticationProperties { ExpiresUtc = DateTime.Now.AddMinutes(60), IsPersistent = true, AllowRefresh = false });
var isAuthenticated = principal.Identity.IsAuthenticated;
Redirect("~/wwwroot/index.html");## Heading ##
}
else
{
RedirectToPage("./Error");
}
MVC Controller
public async Task<IActionResult> SignIn()
{
var sessionId = HttpContext.Request.Query.FirstOrDefault().Value;
var session = await _httpService.ValidateSession(sessionId);
if (!string.IsNullOrEmpty(session?.UserId))
{
var claims = new List<Claim>()
{
new Claim(CustomClaimTypes.UserId, session.UserId),
new Claim(CustomClaimTypes.BuId, session.BuId),
new Claim(CustomClaimTypes.SecurityLevel, session.SecurityLevel)
};
var identity = new ClaimsIdentity(claims, "TNReadyEVP");
var principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal,
new AuthenticationProperties { ExpiresUtc = DateTime.Now.AddMinutes(20), IsPersistent = true, AllowRefresh = false });
var isAuthenticated = principal.Identity.IsAuthenticated;
}
else
{
return Forbid();
}
return View("~/wwwroot/index.html");
}
First I'd like to say that Core 2 is absolutely terrible at reporting errors. Sometimes it does, sometimes code will fail with no Exception report. Aside from that Core 2 is great.
Here's the answer, you'll see I changed the method signature to return IActionResult which enables the use of RedirectToPage and File.
public async Task<IActionResult> OnGetAsync()
{
var request = HttpContext.Request;
var sessionId = request.Query.FirstOrDefault().Value;
var session = await _httpService.ValidateSession(sessionId);
if (!string.IsNullOrEmpty(sessionId))
{
var claims = new List<Claim>()
{
new Claim(CustomClaimTypes.UserId, session.UserId),
new Claim(CustomClaimTypes.BuId, session.BuId),
new Claim(CustomClaimTypes.SecurityLevel, session.SecurityLevel)
};
var identity = new ClaimsIdentity(claims, "TNReadyEVP");
var principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal,
new AuthenticationProperties { ExpiresUtc = DateTime.Now.AddMinutes(60), IsPersistent = true, AllowRefresh = false });
var isAuthenticated = principal.Identity.IsAuthenticated;
return File("index.html", "text/html");
}
else
{
return RedirectToPage("Error");
}
}
I suspect the root of the problem is:
public async void OnGetAsync()
You are dispatching an asynchronous operation that is not being awaited, and the context will be disposed - see cannot access a disposed object asp net identitycore and aspnet Mvc issues 7011
FIX: Always use
public async Task OnGetAsync()
I echo above thoughts about need to report errors rather than sticking your head in the sand. In this case the failure of ASP.NET Core 2.2 to report the error means that you'll have lots of weird problems until you change the method signature.

How to get TempData in an integration test

I have a controller with the following actions:
[HttpGet]
public IActionResult Index()
{
return View();
}
[HttpPost]
[Route(
"/MyShop/OrderDetails/CancelOrder",
Name = UrlRouteDefinitions.MyShopOrderDetailsCancelOrder)]
[ValidateAntiForgeryToken]
public IActionResult CancelOrder(MyViewModel viewModel)
{
var isCancelSuccessful = _orderBusinessLogic.CancelOrderById(viewModel.Order.Id);
if (isCancelSuccessful)
{
//to show a success-message after the redirect
this.TempData["SuccessCancelOrder"] = true;
}
return RedirectToRoute(UrlRouteDefinitions.MyShopOrderDetailsIndex, new
{
orderId = viewModel.Order.Id
});
}
Then I also have the following piece of HTML in the View for the Controller mentioned above:
<div class="panel-body">
#if (TempData["SuccessCancelOrder"] != null)
{
//show the message
#TempData["SuccessCancelOrder"].ToString();
}
</div>
Now I'm writing an integration test (code-summary below) to check if the CancelOrder() method works as expected. There I'd like to access the value of the TempData dictionary to check its correctness.
[TestMethod]
public void MyTest()
{
try
{
//Arrange: create an Order with some Products
var orderId = CreateFakeOrder();
//Open the Order Details page for the arranged Order
var httpRequestMessage = base.PrepareRequest(
HttpMethod.Get,
"/MyShop/OrderDetails?orderId=" + orderId,
MediaTypeEnum.Html);
var httpResponse = base.Proxy.SendAsync(httpRequestMessage).Result;
var responseContent = httpResponse.Content.ReadAsStringAsync().Result;
var viewModel = base.GetModel<MyViewModel>(responseContent);
Assert.AreEqual(HttpStatusCode.OK, httpResponse.StatusCode);
//Try to execute a POST to cancel the Order
httpRequestMessage = base.PrepareRequest(
HttpMethod.Post,
"/MyShop/OrderDetails/CancelOrder",
MediaTypeEnum.Html,
httpResponse, content: viewModel);
httpResponse = base.Proxy.SendAsync(httpRequestMessage).Result;
var expectedRedirectLocation = $"{this.RelativeHomeUrl}/MyShop/OrderDetails?orderId=" + orderId;
var receivedRedirectLocation = WebUtility.UrlDecode(httpResponse.Headers.Location.ToString());
//we expect that the Order Details page will be reloaded
//with the ID of the already cancelled Order
Assert.AreEqual(HttpStatusCode.Redirect, httpResponse.StatusCode);
//-----------------------------------------------------------
//here I'm going to re-execute a GET-request
//on the Order Details page.
//
//Then I need to check the content of the message
//that's been written in the TempData
//-----------------------------------------------------------
}
finally
{
//delete the arranged data
}
}
In any case, I don't know how to access it from my integration test. Does anybody know how to do it and if there's a way at all?
Controller.TempData is public so you can easily access it and check if your key/value exists
[TestMethod]
public void TempData_Should_Contain_Message() {
// Arrange
var httpContext = new DefaultHttpContext();
var tempData = new TempDataDictionary(httpContext, Mock.Of<ITempDataProvider>());
var controller = new TestController();
controller.TempData = tempData;
// Act
var result = controller.DoSomething();
//Assert
controller.TempData["Message"]
.Should().NotBeNull()
.And.BeEquivalentTo("Hello World");
}

Unit testing fileupload with Moq .net Core

I have a method in WebApi controller that I want to write unit tests for. This is how my controller method looks:
Controller.cs
public async Task<FileUploadDto> UploadGoalDocument(Guid id)
{
var file = this.Request?.Form?.Files.FirstOrDefault();
FileUploadDto result = null;
if (file == null)
{
return this.CreateResponse(result);
}
//logic to store file in db
return this.CreateResponse(new FileUploadDto() { Id = document.Id, Name = document.Name, Uri = document.Uri});
}
How can I mock the request object in unit testing? I tried following but ran into problems with IFormFileCollection. The following line throws error:
system.argumentexception interface not found
cc.Setup(x => x.HttpContext.Request.Form.Files).Returns(col.Object);
ControllerTest.cs
public async Task Upload_document_should_upload_document_and_return_dto()
{
var fileDto = new FileUploadDto { Id = Guid.NewGuid(), Name = "dummy.txt" };
var fileMock = new Mock<IFormFile>();
//Setup mock file using a memory stream
using (var ms = new MemoryStream())
{
using (var writer = new StreamWriter("dummy.txt"))
{
writer.WriteLine("Hello World from a Fake File");
writer.Flush();
ms.Position = 0;
fileMock.Setup(m => m.OpenReadStream()).Returns(ms);
var file = fileMock.Object;
this.goalService.Setup(m => m.UploadDocument(Guid.NewGuid(), file, ""))
.ReturnsAsync(new Services.DTO.FileUploadDto { Id = fileDto.Id, Name = fileDto.Name });
var cc = new Mock<ControllerContext>();
var col = new Mock<IFormFileCollection>();
col.Setup(x=> x.GetFile("dummy.txt")).Returns(file);
cc.Setup(x => x.HttpContext.Request.Form.Files).Returns(col.Object);
this.controller.ControllerContext = cc.Object;
var result = await this.controller.UploadGoalDocument(Guid.NewGuid());
//Asserts removed for simplicity
}
}
}
Detailed stack trace:
System.RuntimeTypeHandle.VerifyInterfaceIsImplemented(RuntimeTypeHandle handle, RuntimeTypeHandle interfaceHandle)
at System.RuntimeType.GetInterfaceMap(Type ifaceType)
at Moq.Extensions.IsGetObjectDataVirtual(Type typeToMock)
at Moq.Extensions.IsSerializableMockable(Type typeToMock)
at Moq.SerializableTypesValueProvider.ProvideDefault(MethodInfo member)
at Moq.Mock.GetInitialValue(IDefaultValueProvider valueProvider, Stack`1 mockedTypesStack, PropertyInfo property)
at Moq.Mock.SetupAllProperties(Mock mock, Stack`1 mockedTypesStack)
at Moq.Mock.<>c__DisplayClass72_0.<SetupAllProperties>b__0()
at Moq.PexProtector.Invoke(Action action)
at Moq.Mock.SetupAllProperties(Mock mock)
at Moq.QueryableMockExtensions.FluentMock[T,TResult](Mock`1 mock, Expression`1 setup)
at lambda_method(Closure )
at Moq.Mock.GetInterceptor(Expression fluentExpression, Mock mock)
at Moq.Mock.<>c__DisplayClass66_0`2.<SetupGet>b__0()
at Moq.PexProtector.Invoke[T](Func`1 function)
at Moq.Mock.SetupGet[T,TProperty](Mock`1 mock, Expression`1 expression, Condition condition)
at Moq.Mock.<>c__DisplayClass65_0`2.<Setup>b__0()
at Moq.PexProtector.Invoke[T](Func`1 function)
at Moq.Mock.Setup[T,TResult](Mock`1 mock, Expression`1 expression, Condition condition)
at Moq.Mock`1.Setup[TResult](Expression`1 expression)
I am thinking I have not constructed the test properly, but a keen eye can point me in the right direction.
For anyone facing similar problem, here's what I did to get it working -
ControllerTest.cs
[TestMethod]
public async Task Upload_document_should_upload_document_and_return_dto()
{
var goalId = Guid.NewGuid();
var file = new Services.DTO.FileUploadDto { Id = goalId, Name = "dummy.txt", Uri = "path/to/file" };
this.goalService.Setup(m => m.UploadDocument(It.IsAny<Guid>(), It.IsAny<IFormFile>(), It.IsAny<string>())).ReturnsAsync(file);
//**This is the interesting bit**
this.controller.ControllerContext = this.RequestWithFile();
var result = await controller.UploadGoalDocument(goalId);
Assert.IsNotNull(result);
Assert.AreEqual(file.Id, result.Data.Id);
Assert.AreEqual(file.Name, result.Data.Name);
Assert.AreEqual(file.Uri, result.Data.Uri);
}
//Add the file in the underlying request object.
private ControllerContext RequestWithFile()
{
var httpContext = new DefaultHttpContext();
httpContext.Request.Headers.Add("Content-Type", "multipart/form-data");
var file = new FormFile(new MemoryStream(Encoding.UTF8.GetBytes("This is a dummy file")), 0, 0, "Data", "dummy.txt");
httpContext.Request.Form = new FormCollection(new Dictionary<string, StringValues>(), new FormFileCollection { file });
var actx = new ActionContext(httpContext, new RouteData(), new ControllerActionDescriptor());
return new ControllerContext(actx);
}

Can't get IValueProvider values from QueryStringValueProvider(ControllerContext) in Unit Testing?

Here is the Test method Creating a mock for request and context
have added the querystring to the context and while debugging the TestMethod could able to see the values of the querystring collection
[TestMethod]
public void Save_Tester()
{
//Arrange
HomeController controller = new HomeController();
string querystring = "?key1=value1&key2=value2&key3=value3&key4=value4";
Mock<HttpRequestBase> mock_request = MockHelpers.CreateMockRequest(querystring);
Mock<HttpContextBase> mock_context = new Mock<HttpContextBase>();
//Request
NameValueCollection myValues = new NameValueCollection();
FormCollection formcollection = new FormCollection(myValues);
mock_request.SetupGet(mr => mr.Params).Returns(myValues);
mock_request.SetupGet(mr => mr.Form).Returns(myValues);
mock_request.SetupGet(mr => mr.QueryString).Returns(HttpUtility.ParseQueryString(querystring));
//Context
mock_context.Setup(c => c.Request).Returns(mock_request.Object);
controller.ValueProvider = formcollection.ToValueProvider();
// Act
Assert.IsNotNull(controller); //Guard
var result_query = controller.Save() as ViewResult;
// Assert
}
In the save method using QueryStringValueProvider to get the Values but has no namevaluecollection
QueryStringValueProvider takes the ControllerContext.HttpContext.Request.QueryString that to avail in debugging
[HttpPost]
public ActionResult Save()
{
try
{
IValueProvider provider = new QueryStringValueProvider(this.ControllerContext);
//provider has no namevaluecollection values
}
catch
{
throw;
}
}
Solution
This solved it I should have taken some time to analyse before putting a question
Uri uri = new Uri("http://localhost?key1=value1&key2=value2&key3=value3&&key4=value4");
HttpRequest httpRequest = new HttpRequest(string.Empty, uri.ToString(),
uri.Query.TrimStart('?'));
HttpContext httpContext = new HttpContext(httpRequest, new HttpResponse(new StringWriter()));
HttpSessionStateContainer sessionContainer = new HttpSessionStateContainer("id",
new SessionStateItemCollection(),
new HttpStaticObjectsCollection(),
10, true, HttpCookieMode.AutoDetect,
SessionStateMode.InProc, false);
SessionStateUtility.AddHttpSessionStateToContext(
httpContext, sessionContainer);
HttpContext.Current = httpContext;

Categories

Resources