Initializing DefaultHttpContext.Response.Body to MemoryStream throws NullReferencePointer - unit-testing

I am working on implementation of global exception middleware and I would like to cover middleware with unit tests. Below you see how far I got.
This is a code of unit test.
[Fact]
public async Task MethodName_StateUnderTest_ExpectedBehavior()
{
//Arrange
IExceptionHandlerFeature exceptionHandlerFeature = new ExceptionHandlerFeature {Error = new NotFoundException()};
IFeatureCollection features = new FeatureCollection();
features.Set(exceptionHandlerFeature);
var context = new DefaultHttpContext(features);
context.Response.Body = new MemoryStream();
//Act
await ExceptionMiddleware.HandleException(context);
//Assert
context.Response.StatusCode.Should().Be((int) HttpStatusCode.NotFound);
}
This is a code of ExceptionMiddleware.Handle method
public static async Task HandleException(HttpContext context)
{
var contextFeature = context.Features.Get<IExceptionHandlerFeature>();
if (contextFeature == null)
{
return;
}
if (contextFeature.Error is AppValidationException validationException)
{
context.Response.StatusCode = (int) HttpStatusCode.BadRequest;
var failures = JsonConvert.SerializeObject(validationException.Failures);
await context.Response.WriteAsync(
new ErrorDetails
{
StatusCode = context.Response.StatusCode,
Message = contextFeature.Error.Message,
StackTrace = contextFeature.Error.StackTrace,
Detail = failures
}.ToString());
return;
}
context.Response.StatusCode = (int) ResolveStatusCodeFromExceptionType(contextFeature.Error);
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(
new ErrorDetails
{
StatusCode = context.Response.StatusCode,
Message = contextFeature.Error.Message,
StackTrace = contextFeature.Error.StackTrace
}.ToString());
}
Test is crushing on line
context.Response.Body = new MemoryStream();
According to this question, everything should be fine but I still can't initialize property Body.
Test Projects target framework is .Net Core 3.0.
ExceptionMiddleware.cs is in project with set target framework .NET Standard 2.1 and it is Class Library.
StackTrace of exception is really short:
at
Microsoft.AspNetCore.Http.Internal.DefaultHttpResponse.set_Body(Stream
value) at
Itixis.Shared.API.Tests.ExceptionMiddlewareTests.d__0.MoveNext()
in
C:\Users\daniel.rusnok\source\repos\Itixis\Itixis.Shared\Tests\Itixis.Shared.API.Tests\ExceptionMiddlewareTests.cs:line
35

By manually setting the features you are removing the ones set by default, which causes your null error.
public DefaultHttpContext()
: this(new FeatureCollection())
{
Features.Set<IHttpRequestFeature>(new HttpRequestFeature());
Features.Set<IHttpResponseFeature>(new HttpResponseFeature());
Features.Set<IHttpResponseBodyFeature>(new StreamResponseBodyFeature(Stream.Null));
}
public DefaultHttpContext(IFeatureCollection features)
{
_features.Initalize(features);
_request = new DefaultHttpRequest(this);
_response = new DefaultHttpResponse(this);
}
Source
Note the request, response and response body features added in the default constructor.
Either try recreating what was done in the default constructor by also adding the required features,
IExceptionHandlerFeature exceptionHandlerFeature = new ExceptionHandlerFeature {Error = new NotFoundException()};
IFeatureCollection features = new FeatureCollection();
features.Set(exceptionHandlerFeature);
features.Set<IHttpRequestFeature>(new HttpRequestFeature());
features.Set<IHttpResponseFeature>(new HttpResponseFeature());
features.Set<IHttpResponseBodyFeature>(new StreamResponseBodyFeature(Stream.Null));
var context = new DefaultHttpContext(features);
//...
or remove the manual feature and use the default constructor.
[Fact]
public async Task MethodName_StateUnderTest_ExpectedBehavior() {
//Arrange
var context = new DefaultHttpContext();
context.Response.Body = new MemoryStream();
//Act
await ExceptionMiddleware.HandleException(context);
//Assert
context.Response.StatusCode.Should().Be((int) HttpStatusCode.NotFound);
}
and it should work as expected.

Related

Unit testing web api controller with role based authorization [duplicate]

I have an ASP.NET MVC Core application that I am writing unit tests for. One of the action methods uses User name for some functionality:
SettingsViewModel svm = _context.MySettings(User.Identity.Name);
which obviously fails in the unit test. I looked around and all suggestions are from .NET 4.5 to mock HttpContext. I am sure there is a better way to do that. I tried to inject IPrincipal, but it threw an error; and I even tried this (out of desperation, I suppose):
public IActionResult Index(IPrincipal principal = null) {
IPrincipal user = principal ?? User;
SettingsViewModel svm = _context.MySettings(user.Identity.Name);
return View(svm);
}
but this threw an error as well.
Couldn't find anything in the docs either...
The controller’s User is accessed through the HttpContext of the controller. The latter is stored within the ControllerContext.
The easiest way to set the user is by assigning a different HttpContext with a constructed user. We can use DefaultHttpContext for this purpose, that way we don’t have to mock everything. Then we just use that HttpContext within a controller context and pass that to the controller instance:
var user = new ClaimsPrincipal(new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, "example name"),
new Claim(ClaimTypes.NameIdentifier, "1"),
new Claim("custom-claim", "example claim value"),
}, "mock"));
var controller = new SomeController(dependencies…);
controller.ControllerContext = new ControllerContext()
{
HttpContext = new DefaultHttpContext() { User = user }
};
When creating your own ClaimsIdentity, make sure to pass an explicit authenticationType to the constructor. This makes sure that IsAuthenticated will work correctly (in case you use that in your code to determine whether a user is authenticated).
In previous versions you could have set User directly on the controller, which made for some very easy unit tests.
If you look at the source code for ControllerBase you will notice that the User is extracted from HttpContext.
/// <summary>
/// Gets the <see cref="ClaimsPrincipal"/> for user associated with the executing action.
/// </summary>
public ClaimsPrincipal User => HttpContext?.User;
and the controller accesses the HttpContext via ControllerContext
/// <summary>
/// Gets the <see cref="Http.HttpContext"/> for the executing action.
/// </summary>
public HttpContext HttpContext => ControllerContext.HttpContext;
You will notice that these two are read only properties. The good news is that ControllerContext property allows for setting it's value so that will be your way in.
So the target is to get at that object. In Core HttpContext is abstract so it is a lot easier to mock.
Assuming a controller like
public class MyController : Controller {
IMyContext _context;
public MyController(IMyContext context) {
_context = context;
}
public IActionResult Index() {
SettingsViewModel svm = _context.MySettings(User.Identity.Name);
return View(svm);
}
//...other code removed for brevity
}
Using Moq, a test could look like this
public void Given_User_Index_Should_Return_ViewResult_With_Model() {
//Arrange
var username = "FakeUserName";
var identity = new GenericIdentity(username, "");
var mockPrincipal = new Mock<ClaimsPrincipal>();
mockPrincipal.Setup(x => x.Identity).Returns(identity);
mockPrincipal.Setup(x => x.IsInRole(It.IsAny<string>())).Returns(true);
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext.Setup(m => m.User).Returns(mockPrincipal.Object);
var model = new SettingsViewModel() {
//...other code removed for brevity
};
var mockContext = new Mock<IMyContext>();
mockContext.Setup(m => m.MySettings(username)).Returns(model);
var controller = new MyController(mockContext.Object) {
ControllerContext = new ControllerContext {
HttpContext = mockHttpContext.Object
}
};
//Act
var viewResult = controller.Index() as ViewResult;
//Assert
Assert.IsNotNull(viewResult);
Assert.IsNotNull(viewResult.Model);
Assert.AreEqual(model, viewResult.Model);
}
There is also the possibility to use the existing classes, and mock only when needed.
var user = new Mock<ClaimsPrincipal>();
_controller.ControllerContext = new ControllerContext
{
HttpContext = new DefaultHttpContext
{
User = user.Object
}
};
In my case, I needed to make use of Request.HttpContext.User.Identity.IsAuthenticated, Request.HttpContext.User.Identity.Name and some business logic sitting outside of the controller. I was able to use a combination of Nkosi's, Calin's and Poke's answer for this:
var identity = new Mock<IIdentity>();
identity.SetupGet(i => i.IsAuthenticated).Returns(true);
identity.SetupGet(i => i.Name).Returns("FakeUserName");
var mockPrincipal = new Mock<ClaimsPrincipal>();
mockPrincipal.Setup(x => x.Identity).Returns(identity.Object);
var mockAuthHandler = new Mock<ICustomAuthorizationHandler>();
mockAuthHandler.Setup(x => x.CustomAuth(It.IsAny<ClaimsPrincipal>(), ...)).Returns(true).Verifiable();
var controller = new MyController(...);
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext.Setup(m => m.User).Returns(mockPrincipal.Object);
controller.ControllerContext = new ControllerContext();
controller.ControllerContext.HttpContext = new DefaultHttpContext()
{
User = mockPrincipal.Object
};
var result = controller.Get() as OkObjectResult;
//Assert results
mockAuthHandler.Verify();
I want to hit my Controllers directly and just use DI like AutoFac. To do this I first registering ContextController.
var identity = new GenericIdentity("Test User");
var httpContext = new DefaultHttpContext()
{
User = new GenericPrincipal(identity, null)
};
var context = new ControllerContext { HttpContext = httpContext};
builder.RegisterInstance(context);
Next I enable property injection when I register the Controllers.
builder.RegisterAssemblyTypes(assembly)
.Where(t => t.Name.EndsWith("Controller")).PropertiesAutowired();
Then User.Identity.Name is populated, and I do not need to do anything special when calling a method on my Controller.
public async Task<ActionResult<IEnumerable<Employee>>> Get()
{
var requestedBy = User.Identity?.Name;
..................
I would look to implement an Abstract Factory Pattern.
Create an interface for a factory specifically for providing user names.
Then provide concrete classes, one which provides User.Identity.Name, and one that provides some other hard coded value that works for your tests.
You can then use the appropriate concrete class depending on production versus test code. Perhaps looking to pass the factory in as a parameter, or switching to the correct factory based on some configuration value.
interface IUserNameFactory
{
string BuildUserName();
}
class ProductionFactory : IUserNameFactory
{
public BuildUserName() { return User.Identity.Name; }
}
class MockFactory : IUserNameFactory
{
public BuildUserName() { return "James"; }
}
IUserNameFactory factory;
if(inProductionMode)
{
factory = new ProductionFactory();
}
else
{
factory = new MockFactory();
}
SettingsViewModel svm = _context.MySettings(factory.BuildUserName());
I got a brownfield .net 4.8 project that I needed to convert to .net 5.0 and I wanted to keep as much of the original code as possible, including the unit-/integration tests. The test for Controllers relied on the Context a lot so I created this Extension method to enable setting tokens, claims and headers:
public static void AddContextMock(
this ControllerBase controller,
IEnumerable<(string key, string value)> claims = null,
IEnumerable<(string key, string value)> tokens = null,
IEnumerable<(string key, string value)> headers = null)
{
HttpContext mockContext = new DefaultHttpContext();
if(claims != null)
{
mockContext.User = SetupClaims(claims);
}
if(tokens != null)
{
mockContext.RequestServices = SetupTokens(tokens);
}
if(headers != null)
{
SetupHeaders(mockContext, headers);
}
controller.ControllerContext = new ControllerContext()
{
HttpContext = mockContext
};
}
private static void SetupHeaders(HttpContext mockContext, IEnumerable<(string key, string value)> headers)
{
foreach(var header in headers)
{
mockContext.Request.Headers.Add(header.key, header.value);
}
}
private static ClaimsPrincipal SetupClaims(IEnumerable<(string key, string value)> claimValues)
{
var claims = claimValues.Select(c => new Claim(c.key, c.value));
return new ClaimsPrincipal(new ClaimsIdentity(claims, "mock"));
}
private static IServiceProvider SetupTokens(IEnumerable<(string key, string value)> tokenValues)
{
var mockServiceProvider = new Mock<IServiceProvider>();
var authenticationServiceMock = new Mock<IAuthenticationService>();
var authResult = AuthenticateResult.Success(
new AuthenticationTicket(new ClaimsPrincipal(), null));
var tokens = tokenValues.Select(t => new AuthenticationToken { Name = t.key, Value = t.value });
authResult.Properties.StoreTokens(tokens);
authenticationServiceMock
.Setup(x => x.AuthenticateAsync(It.IsAny<HttpContext>(), null))
.ReturnsAsync(authResult);
mockServiceProvider.Setup(_ => _.GetService(typeof(IAuthenticationService))).Returns(authenticationServiceMock.Object);
return mockServiceProvider.Object;
}
This uses Moq but can be adapted to other mocking frameworks. The authentication type is hardcoded to "mock" since I rely on default authentication but this could be supplied as well.
It is used as such:
_controllerUnderTest.AddContextMock(
claims: new[]
{
(ClaimTypes.Name, "UserName"),
(ClaimTypes.MobilePhone, "1234"),
},
tokens: new[]
{
("access_token", "accessTokenValue")
},
headers: new[]
{
("header", "headerValue")
});
If you're using Razor pages and want to override the claims:
[SetUp]
public void Setup()
{
var user = new ClaimsPrincipal(new ClaimsIdentity(
new Claim[] {
new("dateofbirth", "2000-10-10"),
new("surname", "Smith") },
"mock"));
_razorModel = new RazorModel()
{
PageContext = new PageContext
{
HttpContext = new DefaultHttpContext() { User = user }
}
};
}

Mocking IDocumentQuery in Unit Test that uses Linq queries

I am writing unit tests for DocumentDBRepository but I got a null reference exception. I use Moq framework and XUnit.
Here's my methods in DocumentDBRepository class.
public class DocumentDBRepository<T> : IRepository<T> where T: class
{
private static string DatabaseId;
private static string CollectionId;
private static IDocumentClient client;
public DocumentDBRepository(IDocumentClient documentClient, string databaseId, string collectionId)
{
DatabaseId = databaseId;
CollectionId = collectionId;
client = documentClient;
CreateDatabaseIfNotExistsAsync().Wait();
CreateCollectionIfNotExistsAsync().Wait();
}
public async Task<IDocumentQuery<T>> GetQuery(Expression<Func<T, bool>> predicate)
{
try
{
IDocumentQuery<T> query = client.CreateDocumentQuery<T>(
UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId),
new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true })
.Where(predicate)
.AsDocumentQuery();
return query;
}
catch (Exception e) {
throw;
}
}
public async Task<IEnumerable<T>> GetEntities(IDocumentQuery<T> query)
{
try
{
List<T> results = new List<T>();
while (query.HasMoreResults)
{
results.AddRange(await query.ExecuteNextAsync<T>());
}
return results;
}
catch (Exception e)
{
throw;
}
}
}
Here's my test code:
public interface IFakeDocumentQuery<T> : IDocumentQuery<T>, IOrderedQueryable<T>
{
}
[Fact]
public async virtual Task Test_GetBooksById()
{
var expected = new List<Book> {
new Book { ID = "123", Description = "HarryPotter"},
new Book { ID = "124", Description = "HarryPotter2"} };
var response = new FeedResponse<Book>(expected);
var mockDocumentQuery = new Mock<IFakeDocumentQuery<Book>>();
mockDocumentQuery.SetupSequence(_ => _.HasMoreResults)
.Returns(true)
.Returns(false);
mockDocumentQuery.Setup(_ => _.ExecuteNextAsync<Book>(It.IsAny<CancellationToken>()))
.ReturnsAsync(response);
var client = new Mock<IDocumentClient>();
client.Setup(_ => _.CreateDocumentQuery<Book>(It.IsAny<Uri>(), It.IsAny<FeedOptions>()))
.Returns(mockDocumentQuery.Object);
var documentsRepository = new DocumentDBRepository<Book>(client.Object, "123", "123");
//Act
var query = await documentsRepository.GetQuery(t => t != null);
var entities = await documentsRepository.GetEntities(query);
//Assert
if (entities != null)
{
entities.Should().BeEquivalentTo(expected);
}
}
Here's the error message after running the test method:
Message: System.NullReferenceException : Object reference not set to
an instance of an object.
When I stepped through the code, the error happens right after the the test code called GetQuery() method:
IDocumentQuery<T> query = client.CreateDocumentQuery<T>(
UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId),
new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true })
.Where(predicate)
.AsDocumentQuery();
Here's my thought process: when I stepped through the entire code, I do not see any null variables. But in the 'response' variable from the second line of the test method, it does show a lot of the properties are null exception but result view shows the 'expected' variable.
My question is, is it because of the response variable that caused the null reference exception? Or somewhere else?
PS: Test code reference from here
I also tried turning on the Mock behavior to strict and saw this error message.
Message: System.AggregateException : One or more errors occurred.
(IDocumentClient.ReadDatabaseAsync(dbs/123, null) invocation failed
with mock behavior Strict. All invocations on the mock must have a
corresponding setup.)
---- Moq.MockException : IDocumentClient.ReadDatabaseAsync(dbs/123, null) invocation failed with mock behavior Strict. All invocations on
the mock must have a corresponding setup.
As suspected the problem is .Where(predicate). I ran a test with the provided example and removed the .Where clause and it executed to completion.
The fake interface inherits from both IOrderedQueryable and IDocumentQuery. The issue is that the Where is converting it back to a plain IEnumerable because of the List data source and the AsDocumentQuery is crapping out as it is expecting an IDocumentQuery
I am not a fan of tightly coupling to APIs I can't control. I would abstract my way around such implementation details for that very reason.
The work around involved having to provide a fake Linq IQueryProvider to bypass any queries and return a type that derives from IDocumentQuery so as to allow AsDocumentQuery to behave as intended.
But first I refactored GetEntities and made GetQuery private to stop the repository from being a leaky abstraction.
private IDocumentQuery<T> getQuery(Expression<Func<T, bool>> predicate) {
var uri = UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId);
var feedOptions = new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true };
var queryable = client.CreateDocumentQuery<T>(uri, feedOptions);
IQueryable<T> filter = queryable.Where(predicate);
IDocumentQuery<T> query = filter.AsDocumentQuery();
return query;
}
public async Task<IEnumerable<T>> GetEntities(Expression<Func<T, bool>> predicate) {
try {
IDocumentQuery<T> query = getQuery(predicate);
var results = new List<T>();
while (query.HasMoreResults) {
results.AddRange(await query.ExecuteNextAsync<T>());
}
return results;
} catch (Exception e) {
throw;
}
}
Note that getQuery is not doing anything async so it should not be returning a Task<> anyway.
Next in the test the mocked IDocumentQuery was set up to allow the test to flow to completion. This was done by providing a mocked IQueryProvider the would return the mocked IDocumentQuery when Linq queries are invoked against it. (which was the cause of the problem to begin with)
public async virtual Task Test_GetBooksById() {
//Arrange
var id = "123";
Expression<Func<Book, bool>> predicate = t => t.ID == id;
var dataSource = new List<Book> {
new Book { ID = id, Description = "HarryPotter"},
new Book { ID = "124", Description = "HarryPotter2"}
}.AsQueryable();
var expected = dataSource.Where(predicate);
var response = new FeedResponse<Book>(expected);
var mockDocumentQuery = new Mock<IFakeDocumentQuery<Book>>();
mockDocumentQuery
.SetupSequence(_ => _.HasMoreResults)
.Returns(true)
.Returns(false);
mockDocumentQuery
.Setup(_ => _.ExecuteNextAsync<Book>(It.IsAny<CancellationToken>()))
.ReturnsAsync(response);
var provider = new Mock<IQueryProvider>();
provider
.Setup(_ => _.CreateQuery<Book>(It.IsAny<System.Linq.Expressions.Expression>()))
.Returns((Expression expression) => {
if (expression != null) {
dataSource = dataSource.Provider.CreateQuery<Book>(expression);
}
mockDocumentQuery.Object;
});
mockDocumentQuery.As<IQueryable<Book>>().Setup(x => x.Provider).Returns(provider.Object);
mockDocumentQuery.As<IQueryable<Book>>().Setup(x => x.Expression).Returns(() => dataSource.Expression);
mockDocumentQuery.As<IQueryable<Book>>().Setup(x => x.ElementType).Returns(() => dataSource.ElementType);
mockDocumentQuery.As<IQueryable<Book>>().Setup(x => x.GetEnumerator()).Returns(() => dataSource.GetEnumerator());
var client = new Mock<IDocumentClient>();
client.Setup(_ => _.CreateDocumentQuery<Book>(It.IsAny<Uri>(), It.IsAny<FeedOptions>()))
.Returns(mockDocumentQuery.Object);
var documentsRepository = new DocumentDBRepository<Book>(client.Object, "123", "123");
//Act
var entities = await documentsRepository.GetEntities(predicate);
//Assert
entities.Should()
.NotBeNullOrEmpty()
.And.BeEquivalentTo(expected);
}
This allowed the test to be exercised to completion, behave as expected, and pass the test.

Testing filters with IDependencyScope in Web API

I have WebApi simple NUnit Test
[Test]
public async Task Test()
{
var attribute = new TestAuthenticationAttribute {ApiVersions = new[] {"v1"}};
System.Web.Http.Controllers.HttpActionContext context = CreateExecutingContext();
var executedContext = new HttpAuthenticationContext(context, null);
const string reasonPhrase = "ReasonPhrase";
const string messagePhrase = "MessagePhrase";
executedContext.ErrorResult = new AuthenticationFailureResult(reasonPhrase, messagePhrase, executedContext.Request);
await attribute.AuthenticateAsync(executedContext, CancellationToken.None);
var errorResult = await executedContext.ErrorResult.ExecuteAsync(new CancellationToken());
Assert.AreEqual(HttpStatusCode.Unauthorized, errorResult.StatusCode);
}
private System.Web.Http.Controllers.HttpActionContext CreateExecutingContext()
{
return new System.Web.Http.Controllers.HttpActionContext { ControllerContext = new HttpControllerContext {Request = new HttpRequestMessage()
{
RequestUri = new Uri("http://TestApi/api/v1/Test")
}}};
}
and in TestAuthenticationAttribute I have
if (context.Request.GetDependencyScope().GetService(typeof(IExternalService)) is IExternalService externalService)
Do some actions;
How to set/resolve IExternalService dependency in test? Do I need e.g. UnityContainer or I can do it without container?
I added HttpConfiguration to my HttpActionContext and now Context.Request.GetDependencyScope() doesn't throw System.NullReferenceException. Of cource ontext.Request.GetDependencyScope().GetService(typeof(IExternalService)) is null, but now It's ok for my tests.
private System.Web.Http.Controllers.HttpActionContext CreateExecutingContext()
{
var config = new HttpConfiguration();
var httpActionContext = new System.Web.Http.Controllers.HttpActionContext
{
ControllerContext = new HttpControllerContext
{
Request = new HttpRequestMessage()
{
RequestUri = new Uri("http://TestApi/api/v1/Test"),
},
Configuration = config
}
};
httpActionContext.ControllerContext.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;
return httpActionContext;
}
If I want to resolve dependency, I will can add DependencyResolver to my config or Mocking framework

Moq - Setup method with complex parameters

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)

Unit testing post controller .NET Web Api

I don't have much experience with .NET Web Api, but i've been working with it a while now, following John Papa's SPA application tutorial on Pluralsight. The application works fine, but the thing i'm struggling with now, is unit testing POST-controllers.
I have followed this incredible guide on how to unit test web api controllers. The only problem for me is when it comes to test the POST method.
My controller looks like this:
[ActionName("course")]
public HttpResponseMessage Post(Course course)
{
if (course == null)
throw new HttpResponseException(HttpStatusCode.NotAcceptable);
try
{
Uow.Courses.Add(course);
Uow.commit();
}
catch (Exception)
{
throw new HttpResponseException(HttpStatusCode.InternalServerError);
}
var response = Request.CreateResponse(HttpStatusCode.Created, course);
string uri = Url.Link(routeName: "ControllerActionAndId",
routeValues: new { id = course.Id });
response.Headers.Location = new Uri(uri);
return response;
}
And my unit test looks like this:
[Test]
public void PostShouldReturnHttpResponse()
{
var populatedPostController = new CoursesController(new TestUOW());
SetupPostControllerForTest(populatedPostController);
var course = new Course
{
Id = 12,
Author = new UserProfile()
{
Firstname = "John",
Lastname = "Johnson",
},
Description = "Testcourse",
Title = "Test Title"
};
var responses = populatedPostController.Post(course);
ObjectContent content = responses.Content as ObjectContent;
Course result = (Course)content.Value;
Assert.AreSame(result, course);
}
With the help function:
public static void SetupPostControllerForTest(ApiController controller)
{
var config = new HttpConfiguration();
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/courses/course");
var route = config.Routes.MapHttpRoute(
name: "ControllerActionAndId",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: null,
constraints: new { id = #"^\d+$" }
);
var routeData = new HttpRouteData(route, new HttpRouteValueDictionary { { "controller", "courses" }, { "action", "course" } });
controller.ControllerContext = new HttpControllerContext(config, routeData, request);
controller.Request = request;
controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;
}
When i debug the unit test, it seems to fail at:
string uri = Url.Link(routeName: "ControllerActionAndId",
routeValues: new { id = course.Id });
response.Headers.Location = new Uri(uri); //Exception because uri = null
It seems like the Url.Link can't find the route.
I tried this guide aswell, but i really want the example i have above to work.
Am i missing something really basic here?
Yes, you are missing the one line in the configuration as Nemesv mentioned.
controller.Request.Properties[HttpPropertyKeys.HttpRouteDataKey] = routeData
As you can see, configuring a controller just for using the UrlHelper is extremely complex. I tend to avoid the use of UrlHelper in the controller classes for that reason. I usually introduce an external dependency to make testing easier like an IUrlHelper, which allows me to mock the behavior in an unit test.
public interface IUrlHelper
{
string Link(string routeName, object routeValues);
string Route(string routeName, object routeValues);
}
public class UrlHelperWrapper : IUrlHelper
{
UrlHelper helper;
public UrlHelperWrapper(UrlHelper helper)
{
this.helper = helper;
}
public string Link(string routeName, object routeValues)
{
return this.helper.Link(routeName, routeValues);
}
public string Route(string routeName, object routeValues)
{
return this.helper.Route(routeName, routeValues);
}
}
I inject this UrlHelperWraper in the real Web API, and a mock of the IUrlHelper interface in the tests. By doing that, you don't need all that complex configuration with the routes.
Regards,
Pablo.