Moq - Setup method with complex parameters - unit-testing

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)

Related

Getting error as 'Expected invocation on the mock at least once, but was never performed' after executing unit test

I have below code which navigates from one page to other using Navigation Service in Xamarin forms.
On clicking on Observation button it executes ObservationsCommand as shown below.
public Command ObservationsCommand => new Command(async () => await OnObservationsCommandAsync());
After clicking on Observation button it navigates to next page passing the selected data to the navigation service as shown
private async Task OnObservationsCommandAsync()
{
ObservationDetailsParameter selectedData = new ObservationDetailsParameter
{
Cage = DisplayedCage,
Dossiers = DossierList.SelectedItems
};
await _navigationService.NavigateToAsync<ObservationDetailsViewModel>(selectedData);
}
Below is the code for unit test
[Fact]
public void TestOnObservationsCommandAsync()
{
var mockNavigationService = new Mock<INavigationService>();
var mockCageDetailsService = new MockCageDetailsService();
var mockObservationDetailsService = new MockObservationDetailsService();
var mockSettingsService = new MockSettingsService();
Cage _displayedCage = new Cage { Id = 11 };
Dossier _dossier1 = new Dossier { Id = 841 };
var _dossierList = new SelectableItemCollection<Dossier>
{
_dossier1
};
_dossierList.SelectAll();
var cageObsViewModel = new CageObsViewModel(mockNavigationService.Object, mockCageDetailsService, mockSettingsService);
var mockObservationDetailsParameter = new
Mock<IObservationDetailsParameter>();
mockObservationDetailsParameter.Setup(x =>
x.Cage).Returns(_displayedCage);
mockObservationDetailsParameter.Setup(x => x.Dossiers
).Returns(_dossierList.SelectedItems );
cageObsViewModel.DisplayedCage = mockObservationDetailsParameter .Object .Cage ;
cageObsViewModel.DossierList = _dossierList;
// Act
cageObsViewModel.ObservationsCommand.Execute(null);
mockNavigationService.Verify((s) => s.NavigateToAsync<ObservationDetailsViewModel>(mockObservationDetailsParameter.Object));
}
However after executing this test i get error as 'Expected invocation on the mock at least once, but was never performed.'
Will you please help?
This is an example of a unit test I have to verify navigation after successful login.
[Fact]
public async Task ValidCredentials()
{
//Mock login -> false
var authenticationServiceMock = new Mock<IAuthenticationService>();
authenticationServiceMock
.Setup(s => s.Login(It.IsAny<string>(), It.IsAny<string>()))
.Returns(Task.FromResult(true));
LoginViewModel viewModel = new LoginViewModel(authenticationServiceMock.Object);
await viewModel.InitializeAsync(null);
viewModel.UserName.Value = "johann#mail.com";
viewModel.Password.Value = "1234!Maaaa";
viewModel.LoginCommand.Execute(null);
navigationServiceMock.Verify((s) => s.NavigateToAsync<HomeViewModel>());
}
As I am using AutoFac and my ViewModelBase gets the NavigationService instance by itself this is my test setup:
public LoginViewModelTests()
{
ContainerBuilder builder = new ContainerBuilder();
navigationServiceMock = new Mock<INavigationService>();
navigationServiceMock.SetReturnsDefault<Task>(Task.FromResult(0));
builder.RegisterInstance<INavigationService>(navigationServiceMock.Object);
ViewModelLocator.RegisterDependencies(builder);
}
If it is not clear enough let me know and we can review it to better adapt to your case.

How to create mock for httpclient getasync method?

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);

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.

Owin.TestServer To Test a Middleware that Requires Authentication

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);
}

Unit test anonymous user against controller with authorize attribute

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);