How to mock Url.Action during testing controller action?
I'm trying to unit test my asp.net core controller action.
Logic of action has Url.Action and I need to mock it to complete test but I can't find right solution.
Thank you for your help!
UPDATE
this is my method in controller that I need to test.
public async Task<IActionResult> Index(EmailConfirmationViewModel model)
{
if (ModelState.IsValid)
{
var user = await _userManager.FindByNameAsync(model.Email);
if (user == null) return RedirectToAction("UserNotFound");
if (await _userManager.IsEmailConfirmedAsync(user)) return RedirectToAction("IsAlreadyConfirmed");
var token = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.Action("Confirm", "EmailConfirmation", new { userId = user.Id, token }, HttpContext.Request.Scheme);
await _emailService.SendEmailConfirmationTokenAsync(user, callbackUrl);
return RedirectToAction("EmailSent");
}
return View(model);
}
I have problem with mocking this part:
var callbackUrl = Url.Action("Confirm", "EmailConfirmation", new { userId = user.Id, token }, HttpContext.Request.Scheme);
Finally I found solution!
When you are mocking UrlHelper you need to mock only base method Url.Action(UrlActionContext context) because all helper methods actually use it.
var mockUrlHelper = new Mock<IUrlHelper>(MockBehavior.Strict);
mockUrlHelper
.Setup(
x => x.Action(
It.IsAny<UrlActionContext>()
)
)
.Returns("callbackUrl")
.Verifiable();
_controller.Url = mockUrlHelper.Object;
Also! I have problem because of null in HttpContext.Request.Scheme. You need to mock HttpContext
_controller.ControllerContext.HttpContext = new DefaultHttpContext();
I added
var urlHelperMock = new Mock<IUrlHelper>();
urlHelperMock
.Setup(x => x.Action(It.IsAny<UrlActionContext>()))
.Returns((UrlActionContext uac) =>
$"{uac.Controller}/{uac.Action}#{uac.Fragment}?"
+ string.Join("&", new RouteValueDictionary(uac.Values).Select(p => p.Key + "=" + p.Value)));
controller.Url = urlHelperMock.Object;
To my generic Controller setup. Which is a bit roughnready but means I can test any controller logic that generates links.
Related
I am using Moq to create mocks for my unit tests but I am stuck when I have to create mock for getasync method of httpclient. Previously I was using SendAsync method and for that I could use the below code:
var mockResponse =
new HttpResponseMessage(HttpStatusCode.OK) {Content = new StringContent(expectedResponse)};
mockResponse.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var mockHandler = new Mock<DelegatingHandler>();
mockHandler
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.Is<HttpRequestMessage>(
message => message.Headers.Contains("Authorization")
&& message.Headers.Authorization.Parameter.Equals(accessToken)
&& message.Headers.Authorization.Scheme.Equals("Bearer")
&& message.RequestUri.AbsoluteUri.Contains(baseUrl)
),
ItExpr.IsAny<CancellationToken>())
.Returns(Task.FromResult(mockResponse));
Now I have a method:
private async Task<List<Model>> GetData()
{
string url = url;
_httpClient.BaseAddress = new Uri(url);
_httpClient.DefaultRequestHeaders.Add(Headers.AuthorizationHeader, "Bearer" + "token");
var response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsAsync<List<Model>>();
}
Now can I create mock for this method (getasync)? Any help?
Internally GetAsync will eventually call SendAsync.
public Task<HttpResponseMessage> GetAsync(Uri requestUri, HttpCompletionOption completionOption,
CancellationToken cancellationToken)
{
return SendAsync(new HttpRequestMessage(HttpMethod.Get, requestUri), completionOption, cancellationToken);
}
Source code
Loosen the ItExpr expectation and you should be able to get it to behave as expected.
Using the originally provided example
mockHandler
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
)
.ReturnsAsync(mockResponse);
I have a middleware method that requires context.Authentication.User.Identity.Name to be resolved for proper execution. However, when writing a unit test these properties are obviously null as no sign-in has occurred. I am not using Oauth or anything authentication related in this middleware (beyond the obvious name property), as it should be handled elsewhere in another middleware (to promote re-use/flexibility of the component I am developing). Is there a way to mock/fake this value so I can run my test? I have tried everything I can think of to fake a sign-on and I am just stuck at this point. To be clear the middleware needs the value not a webapi call or the like.
//Arrange
var resolver = A.Fake<IDependencyResolver>();
A.CallTo(() => resolver.GetService(typeof(ISomeService))).Returns(new TestService());
using (var server = TestServer.Create(app =>
{
app.UseMyMiddleware(new MyMiddlewareOptions()
{
DependencyResolver = resolver
});
app.Run(async ctx =>
{
await ctx.Response.WriteAsync(ctx.Request.Path.Value);
});
}))
{
//Act
var response = await server.CreateRequest("/").GetAsync();
//Assert
A.CallTo(() => resolver.GetService(typeof(ISomeService)))
.MustHaveHappened(Repeated.Exactly.Once);
Assert.AreEqual(response.StatusCode, HttpStatusCode.OK);
//Etc.
}
So here is one way I suppose not thrilled with it but it does the job. I will wait to accept as I imagine there should be a better way.
public class TestFakeLoginMiddleware : OwinMiddleware
{
public TestFakeLoginMiddleware(OwinMiddleware next) : base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
var identity = A.Fake<IIdentity>();
A.CallTo(() => identity.Name).Returns("TEST#domain.local");
var user = new ClaimsPrincipal(identity);
context.Request.Context.Authentication.User = user;
await Next.Invoke(context);
}
}
A bit late, I know, but could you not just create a new ClaimsIdentity?
public override async Task Invoke(IOwinContext context)
{
var identity= new ClaimsIdentity(new List<Claim> {
new Claim(ClaimTypes.Name, "TEST#domain.local")
});
var user = new ClaimsPrincipal(identity);
context.Request.Context.Authentication.User = user;
await Next.Invoke(context);
}
I am trying to call a webservice which is as follows,
#RequestMapping(value = { "/persons" },method = RequestMethod.GET,headers="Accept=application/json")
public List<Person> getDummyData(#RequestParam(value="search", defaultValue="a") String search,HttpServletRequest req){
List<Person> listOfMatchedPersons=listOfPersons.stream().filter(person->person.getName().contains(search)).collect(Collectors.toList());
req.getParameterMap().forEach((k,v)->System.out.println(k+" : "+v));
return listOfMatchedPersons;
}
I want to call this service with some parameter from my UI, but it always executes this method with default value of search i.e. a.
Following is my angularjs 2's service that is consuming this service,
search(term: string) {
var params = new URLSearchParams();
params.set('search', term);
let aopsServices = 'http://localhost:8080/dummy/persons';//?search='+term;
this.ot = this.http
.get(aopsServices,params)
.map(response => response.json())
;
return this.ot;
}
however if i change the url to http://localhost:8080/dummy/persons'?search='+term; it works.
And also what should be the ideal approach to access the restful services if they are secured ?
I see two ways to do that:
Leveraging the URLSearchParams class:
search(term: string) {
var params = new URLSearchParams();
params.set('search', term);
let aopsServices = 'http://localhost:8080/dummy/persons';
this.ot = this.http
.get(aopsServices, { search: params })
.map(response => response.json());
return this.ot;
}
Use of ` (backticks) instead of single quotes '
search(term: string) {
let aopsServices = `http://localhost:8080/dummy/persons?search=${term}`;
this.ot = this.http
.get(aopsServices, { search: params })
.map(response => response.json());
return this.ot;
}
The second approach is more concise but doesn't urlencode the parameter.
I changed my code to
var params = new URLSearchParams();
params.set('search', term);
let aopsServices = 'http://localhost:8080/dummy/persons';
this.ot = this.http
.get(aopsServices,new RequestOptions({search:params}))
.map(response => response.json());
and it worked.
I misread the documentation of get.
I'm trying to set up a mock of this interface:
public interface IAuthenticatedRequestService
{
HttpClient CreateHttpClientForJwt(Func<HttpResponseMessage, bool> isUnauthenticated, int timeoutSeconds);
HttpClient CreateHttpClientForAccessToken(Func<HttpResponseMessage, bool> isUnauthenticated, int timeoutSeconds);
}
This is one implementation of the method to setup that is in use and working:
public HttpClient CreateHttpClientForAccessToken(Func<HttpResponseMessage, bool> isUnauthenticated, int timeoutSeconds)
{
var client = Mvx.Resolve<IPlatformOperationProvider>().CreateHttpClient(timeoutSeconds);
return new HttpClient(new AuthenticatedHttpMessageHandler(this, client, AuthenticationUtils.AddAccessTokenToRequest, isUnauthenticated,_loggingService));
}
This is one usage of the implemented method that is working:
var client = service.CreateHttpClientForAccessToken(x => x.StatusCode == HttpStatusCode.Unauthorized, CoreConstants.TimeoutMyDetails);
This is my unit test which sets up the mock:
[Test]
public async void TestIsLoggedInIsTrue()
{
//Arrange
var authenticatedRequestService = new Mock<IAuthenticatedRequestService>();
authenticatedRequestService.Setup(foo => foo.CreateHttpClientForAccessToken((It.IsAny<Func <HttpResponseMessage, bool>>())
, 0
)).Returns(new HttpClient());
var platformOperationProvider = new Mock<IPlatformOperationProvider>();
platformOperationProvider.Setup(foo => foo.CreateHttpClient(1)).Returns(new HttpClient());
Mvx.RegisterSingleton<IPlatformOperationProvider>(platformOperationProvider.Object);
Mvx.RegisterSingleton<IAuthenticatedRequestService>(authenticatedRequestService.Object);
var loggedInProvider = new LoggedInProvider(
new Mock<ISecuredSettings>().Object,
new Mock<ILoggingService>().Object
);
//Act
await loggedInProvider.SetUserAndToken(
new User(),
new ApiAccessInfo("refresh token", "access token", "jwt")
);
//Assert
Assert.IsTrue(loggedInProvider.IsLoggedIn);
}
This unit test has no errors, but the test fails (I think it is because I am passing it any HttpResponseMessage? And I need to somehow pass it HttpStatusCode.Accepted? How would I do that?
Take note of the usage of the method, how it passes HttpStatusCode.Unauthorized, then can I do something like that with HttpStatusCode.Accepted?:
var client = service.CreateHttpClientForAccessToken(x => x.StatusCode == HttpStatusCode.Unauthorized, CoreConstants.TimeoutMyDetails);
EDIT: To be clear, It is this line of code that I need to correct:
authenticatedRequestService.Setup(foo => foo.CreateHttpClientForAccessToken((It.IsAny<Func <HttpResponseMessage, bool>>())
, 0
)).Returns(new HttpClient());
EDIT: Whilst debugging the problem starts here (check the code comment after the client is created):
async Task<ServiceResponse> UpdateUserDetails()
{
// Have to late-resolve this otherwise we end up with a dependency loop
var service = Mvx.Resolve<IAuthenticatedRequestService>();
try
{
var client = service.CreateHttpClientForAccessToken(x => x.StatusCode == HttpStatusCode.Unauthorized, CoreConstants.TimeoutMyDetails);
// here is the problem, the client is null after this line of code.
var user = _user;
I have since found that it is not a Moq issue. MvvmCross is not registering the object to resolve correctly.
This line is not working:
Mvx.RegisterSingleton<IAuthenticatedRequestService>(authenticatedRequestService.Object);
as this line creates an AuthenticatedRequestService but it is not the mock one that I made:
var service = Mvx.Resolve<IAuthenticatedRequestService>();
Here is some context of resolving the AuthenticatedRequestService
async Task<ServiceResponse> UpdateUserDetails()
{
// Have to late-resolve this otherwise we end up with a dependency loop
var service = Mvx.Resolve<IAuthenticatedRequestService>();
try
{
var client = service.CreateHttpClientForAccessToken(x => x.StatusCode == HttpStatusCode.Unauthorized, CoreConstants.TimeoutMyDetails);
var user = _user;
var str = await client.GetStringAsync(new Uri(user.IdUrl));
var newUser = JsonConvert.DeserializeObject<User.Json>(str);
var token = _token;
if (token != null)
I am trying to build a unit test to make sure an unauthenticated user is unable to reach a controller. when i run the test, the users is being found as authenticated. how do i mock things up so that the test finds the mocked user as unauthenticated.
i am using mvc5 with indentity 2.0
controller
[Authorize]
public class ProfileController : Controller
{
private ICompanyServiceLayer _service;
public ProfileController(ICompanyServiceLayer service)
{
_service = service;
}
public ActionResult Index(int id)
{
/* cool stuff happens here */
return View();
}
}
test
[Test]
public void Index_As_Annonymous_User()
{
// arrange
Mock<ICompanyServiceLayer> service = new Mock<ICompanyServiceLayer>();
GenericIdentity id = new GenericIdentity("");
Mock<IPrincipal> princ = new Mock<IPrincipal>();
princ.Setup(x => x.Identity).Returns(id);
Mock<HttpContextBase> contextBase = new Mock<HttpContextBase>();
contextBase.Setup(x => x.User).Returns(princ.Object);
Mock<ControllerContext> controllerContext = new Mock<ControllerContext>();
controllerContext.Setup(x => x.HttpContext).Returns(contextBase.Object);
// controller
ProfileController controller = new ProfileController(service.Object);
controller.ControllerContext = controllerContext.Object;
// act
var result = controller.Index(1);
// assert
Assert.IsInstanceOf(typeof(HttpStatusCodeResult), result);
}
update based on blorkfish suggestion
[Test]
public void Index_As_Annonymous_User()
{
// arrange
Mock<ICompanyServiceLayer> service = new Mock<ICompanyServiceLayer>();
Mock<HttpRequestBase> request = new Mock<HttpRequestBase>();
request.Setup(x => x.IsAuthenticated).Returns(false);
Mock<HttpContextBase> contextBase = new Mock<HttpContextBase>();
contextBase.Setup(x => x.Request).Returns(request.Object);
// controller
ProfileController controller = new ProfileController(service.Object);
controller.ControllerContext = new ControllerContext(contextBase.Object, new RouteData(), controller);
// act
var result = controller.Index(1);
// assert
Assert.IsInstanceOf(typeof(HttpStatusCodeResult), result);
}
Using Moq, you need to mock the HttpContextBase and ensure its IsAuthenticated property returns false.
var mockHttpContext = new Mock<HttpContextBase>();
mockHttpContext.SetupGet(c => c.User.Identity.IsAuthenticated).Returns(false);
var mockControllerContext = new Mock<ControllerContext>();
mockControllerContext.Setup(c => c.HttpContext).Returns(mockHttpContext.Object);
controller.ControllerContext = mockControllerContext.Object;
Then, running the following in your controller action should return false:
User.Identity.IsAuthenticated
The mvc framework checks the HttpRequest.IsAuthenticated flag. To mock this, you will need to mock the httpContext and the httpRequest:
var httpContext = MockRepository.GenerateMock<HttpContextBase>();
var httpRequest = MockRepository.GenerateMock<HttpRequestBase>();
httpContext.Stub(x => x.Request).Return(httpRequest);
httpRequest.Stub(x => x.IsAuthenticated).Return(false);
UserController controller = new UserController();
controller.ControllerContext
= new ControllerContext(httpContext, new RouteData(), controller);