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);
Related
I have a asp.net WebAPI service for user login that takes an email and password. The api method has the following signature. LoginDto has two fileds, Email and password.
public async Task<IActionResult> Login(LoginDto dto)
Once the user is authenticated, WebAPI returns an object that has token and Id:
return Ok(new { Token = GenerateJwtTokenFromClaims(claims), Id=user.Id });
On the client side (Blazor app), I used nswag command line tool by running nswag run and it "successfully" generated the Service and Contract files. Everything complies. nswag generated code is pasted below.
When I want to use the login nswag Service, I have the following method (I also have an overloaded method with CancellationToken but I only use this method):
public System.Threading.Tasks.Task Login2Async(LoginDto body)
{
return Login2Async(body, System.Threading.CancellationToken.None);
}
The question that I have is that how do I get the response out of the nswag-generated-code that the WebAPI login sent back to the client? When I try to assign a var to the method, I get Cannot assign void to an implicitly-typed variable which makes sense since I don't see a return type. I also don't see any logic in the nswag generated service file to return the response to the caller. How do I get the response back from the nswag generated API call? Is there an option I have to set in nswag run to get a response object back? Thanks in advance.
public async System.Threading.Tasks.Task Login2Async(LoginDto body, System.Threading.CancellationToken cancellationToken)
{
var urlBuilder_ = new System.Text.StringBuilder();
urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Account/Login");
var client_ = _httpClient;
var disposeClient_ = false;
try
{
using (var request_ = new System.Net.Http.HttpRequestMessage())
{
var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value));
content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json");
request_.Content = content_;
request_.Method = new System.Net.Http.HttpMethod("POST");
PrepareRequest(client_, request_, urlBuilder_);
var url_ = urlBuilder_.ToString();
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
PrepareRequest(client_, request_, url_);
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
var disposeResponse_ = true;
try
{
var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
if (response_.Content != null && response_.Content.Headers != null)
{
foreach (var item_ in response_.Content.Headers)
headers_[item_.Key] = item_.Value;
}
ProcessResponse(client_, response_);
var status_ = (int)response_.StatusCode;
if (status_ == 200)
{
return;
}
else
if (status_ == 400)
{
var objectResponse_ = await ReadObjectResponseAsync<ProblemDetails>(response_, headers_).ConfigureAwait(false);
throw new ApiException<ProblemDetails>("Bad Request", status_, objectResponse_.Text, headers_, objectResponse_.Object, null);
}
else
{
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
}
}
finally
{
if (disposeResponse_)
response_.Dispose();
}
}
}
finally
{
if (disposeClient_)
client_.Dispose();
}
}
Big thanks to the NSwag team, the issue is resolved. I was returning anonymous object from the WebAPI method. The correct way to do is the following. Notice that IActionResult was changed to ActionResult passing a concrete object to return to the caller.
public async Task<ActionResult<LoginDtoResponse>> Login(LoginDto dto)
then returning
return Ok(new LoginDtoResponse { Token = GenerateJwtTokenFromClaims(claims), Id=user.Id });
After that I did that, the following code was generated:
if (status_ == 200)
{
var objectResponse_ = await ReadObjectResponseAsync<LoginDtoResponse>(response_, headers_).ConfigureAwait(false);
return objectResponse_.Object;
}
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.
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'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);