I'm using FluentValidation to validate request. If the validation fails UseCaseHandler should not be invoked. Within the UseCaseHandler I'm usign IRepository, and this is checked to see if the UseCaseHandler gets invoked.
The Request validator
public class MyValidator: AbstractValidator<Request>
{
public MyValidator()
{
RuleFor(rq=> rq)
.Cascade(CascadeMode.Continue);
RuleFor(rq=> rq)
.Must(property => property.id != default(Guid))
.WithMessage(message => $"invalid id.")
.WithName(member => nameof(member.Id));
}
}
This is the test
[Fact]
public async Task Test()
{
Mock<IUnitOfWork> uowMock = new Mock<IUnitOfWork>();
Mock<IRepository> repositoryMock = new Mock<IRepository>(MockBehavior.Strict);
Mock<IValidator<Request>> validatorMock = new Mock<IValidator<Request>>(MockBehavior.Strict);
var request = new Request
{
Id = Guid.NewGuid()
};
validatorMock
.Setup(validator => validator.Validate(request))
.Returns(new ValidationResult());
repositoryMock
.Setup(repo => repo.SaveAsync(It.IsAny<object>()))
.Returns(Task.CompletedTask);
var sut = new UseCase(uowMock.Object, repositoryMock.Object, validatorMock.Object);
Func<Task> act = () => sut.UseCaseHandler(request);
await act.Should().NotThrowAsync();
repositoryMock.Verify(repo => repo.SaveAsync(It.IsAny<object>()), Times.Once);
}
I'm looking to write a test that will check the flow.
If the validation fails the test should fail and SaveAsync should not be called.
If the validation succeeds that the test should succeed also and SaveAsync should be called one time.
What is the way to write a test ?
UPDATE
This is the use case class definition.
UseCaseHandlerProxy is a base abstract class which acts as a proxy
public class UseCase : UseCaseHandlerProxy<Request, Response>
{
private readonly IRepository _repository;
public UseCase(IUnitOfWork unitOfWork, IRepository repository, IValidator<Request> validator)
: base(unitOfWork, validator)
{
_repository = repository
}
public override async Task<Response> UseCaseHandler(Request request)
{
Order order = new Order();
order.Create();
await _repository.SaveAsync(order);
return new Response(order.Id);
}
}
This is the Request class definition
class Request
{
public Guid Id { get; set; }
}
Te response only returns the same Id
Proxy class
public abstract class UseCaseHandlerProxy<TRequest, TResponse> : IUseCaseHandler<TRequest, TResponse>
where TRequest : IRequest
where TResponse : Response
{
private IValidator<TRequest> Validator { get; }
protected internal IUnitOfWork UnitOfWork { get; }
public UseCaseHandlerProxy(IUnitOfWork unitOfWork, IValidator<TRequest> validator)
{
Validator = validator;
UnitOfWork = unitOfWork;
}
async Task<TResponse> IUseCaseHandler<TRequest, TResponse>.HandleAsync(TRequest request)
{
ValidationResult validationResult = await Validator.ValidateAsync(request);
TResponse response;
if (!validationResult.IsValid)
{
response = (TResponse)System.Activator.CreateInstance(typeof(TResponse));
validationResult.Errors.ToList().ForEach(error => response.AddError(error.PropertyName, error.ErrorMessage));
return response;
}
response = await UseCaseHandler(request);
return response;
}
public abstract Task<TResponse> UseCaseHandler(TRequest request);
}
Given the flow you want to test I would say that you are invoking the wrong member.
Cast the sut to IUseCaseHandler<TRequest, TResponse> to get access to HandleAsync which is what does the desired flow.
For example the following verifies that if no validation error that repository invokes save.
[Fact]
public async Task UseCase_Should_Save() {
//Arrange
Mock<IUnitOfWork> uowMock = new Mock<IUnitOfWork>();
Mock<IRepository> repositoryMock = new Mock<IRepository>(MockBehavior.Strict);
Mock<IValidator<Request>> validatorMock = new Mock<IValidator<Request>>(MockBehavior.Strict);
var request = new Request {
Id = Guid.NewGuid()
};
validatorMock
.Setup(validator => validator.ValidateAsync(request, It.IsAny<CancellationToken>()))
.ReturnsAsync(new ValidationResult());
repositoryMock
.Setup(repo => repo.SaveAsync(It.IsAny<object>()))
.Returns(Task.FromResult((object)null));
var sut = new UseCase(uowMock.Object, repositoryMock.Object, validatorMock.Object) as IUseCaseHandler<Request, Response>;
//Act
Func<Task> act = () => sut.HandleAsync(request);
//Assert
await act.Should().NotThrowAsync();
repositoryMock.Verify(repo => repo.SaveAsync(It.IsAny<object>()), Times.Once);
}
The following verifies that if there are errors, then the repository does not save
[Fact]
public async Task UseCase_Should_Not_Save() {
//Arrange
var uowMock = new Mock<IUnitOfWork>();
var repositoryMock = Mock.Of<IRepository>();
var validatorMock = new Mock<IValidator<Request>>(MockBehavior.Strict);
var request = new Request {
Id = Guid.NewGuid()
};
var result = new ValidationResult();
result.Errors.Add(new ValidationFailure("SomeProperty", "SomeError"));
validatorMock
.Setup(validator => validator.ValidateAsync(request, It.IsAny<CancellationToken>()))
.ReturnsAsync(result);
var sut = new UseCase(uowMock.Object, repositoryMock, validatorMock.Object) as IUseCaseHandler<Request, Response>;
//Act
Func<Task> act = () => sut.HandleAsync(request);
//Assert
await act.Should().NotThrowAsync();
Mock.Get(repositoryMock).Verify(repo => repo.SaveAsync(It.IsAny<object>()), Times.Never);
}
Related
I have this class called Handler, which is a MassTransit IConsumer:
public class Handler : IConsumer<ICommand>
{
private readonly IOrderRepository _orderRepository;
public Handler(IOrderRepository orderRepository)
{
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
}
public async Task Consume(ConsumeContext<ICommand> context)
{
var command = context.Message;
var orderId = new OrderId(command.OrderId);
var order = await _orderRepository.FindOrderAsync(orderId, context.CancellationToken);
if (order is null)
{
await context.RespondAsync(CommandResponse.NotFound);
return;
}
order.Cancel();
await _orderRepository.SaveOrderAsync(order, context.CancellationToken);
await context.RespondAsync(CommandResponse.Submitted);
}
}
I have two unit tests for it. Here's the one that seems to work fine:
[TestMethod]
public async Task Consume_WithExistingOrderId_CancelsOrderAndSavesChangesAndReturnsSubmitted()
{
// Arrange
var mockConsumer = new Mock<IConsumer<ICommand>>();
var mockRepository = new Mock<IOrderRepository>();
var sut = new Handler(mockRepository.Object);
var mockCommand = new Mock<ICommand>();
var mockContext = new Mock<ConsumeContext<ICommand>>();
mockContext.Setup(x => x.Message).Returns(mockCommand.Object);
mockContext.Setup(x => x.RespondAsync(It.IsAny<CommandResponse>())).Returns(Task.CompletedTask);
var existingOrderId = new OrderId(Guid.NewGuid());
mockCommand.Setup(x => x.OrderId).Returns(existingOrderId.Value);
var order = GetTestOrder(existingOrderId);
mockRepository.Setup(x => x.FindOrderAsync(existingOrderId, It.IsAny<CancellationToken>())).ReturnsAsync(order);
// Act
await sut.Consume(mockContext.Object);
// Assert
mockRepository.Verify(x => x.SaveOrderAsync(order, It.IsAny<CancellationToken>()), Times.Once());
mockContext.Verify(x => x.RespondAsync(CommandResponse.Submitted), Times.Once());
order.IsCancelled.Should().BeTrue();
}
And here's the one that isn't doing what I expected:
[TestMethod()]
public async Task Consume_WithNonExistantOrderId_ReturnsNotFoundResponseAndDoesNotSave()
{
// Arrange
var mockRepository = new Mock<IOrderRepository>();
var sut = new Handler(mockRepository.Object);
var mockCommand = new Mock<ICommand>();
var mockContext = new Mock<ConsumeContext<ICommand>>();
mockContext.Setup(x => x.Message).Returns(mockCommand.Object);
mockContext.Setup(x => x.RespondAsync(It.IsAny<CommandResponse>())).Returns(Task.CompletedTask);
var nonExistantOrderId = new OrderId(Guid.NewGuid());
mockCommand.Setup(x => x.OrderId).Returns(nonExistantOrderId.Value);
mockRepository.Setup(x => x.FindOrderAsync(nonExistantOrderId, It.IsAny<CancellationToken>())).ReturnsAsync((Order?)null);
// Act
await sut.Consume(mockContext.Object);
// Assert
mockRepository.Verify(x => x.SaveOrderAsync(It.IsAny<Order>(), It.IsAny<CancellationToken>()), Times.Never());
mockContext.Verify(x => x.RespondAsync(CommandResponse.NotFound), Times.Once());
}
Both unit tests require that the Handler calls the RespondAsync method of the MassTransit context exactly once. However, the second unit test doesn't pass, saying that the method was never called. I don't see why it was never called. When I debug into the method it appears to show the method is called.
I can't tell if my test is wrong or if my system under test is wrong. Can anybody see the problem please?
(Also, if anybody can see how to make my code more testable and my unit tests shorter and simpler that would also be appreciated.)
The problem is with the nonExistantOrderId and using that for the match in expectation.
mockRepository
.Setup(x => x.FindOrderAsync(nonExistantOrderId, It.IsAny<CancellationToken>()))
.ReturnsAsync((Order?)null);
the mock expects to get that specific instance when the subject is being exercised but the subject initialized its own instance which causes the mock to not invoke the async call and exit the subject before that target line can be invoked.
This is why
mockRepository.Verify(x => x.SaveOrderAsync(It.IsAny<Order>(), It.IsAny<CancellationToken>()), Times.Never());
supposedly passed verification, and
mockContext.Verify(x => x.RespondAsync(CommandResponse.NotFound), Times.Once());
failed since the subject code exited before reaching both members that are the targets of your verification.
Loosen the match using It.IsAny<OrderId>()
mockRepository
.Setup(x => x.FindOrderAsync(It.IsAny<OrderId>(), It.IsAny<CancellationToken>()))
.ReturnsAsync((Order?)null);
so that the mocked async call can be invoked and allow the code to flow to completion.
I have accepted Nkosi's answer because it solved the problem stated in the question. Thank you, Nkosi.
However, the resources that were provided by the MassTransit boss-man, #Chris Patterson, were helpful in making a better unit test altogether. That's why I'm posting this alternative answer. This answer rewrites the original unit test to use the MassTransit test harness, and I hope it helps somebody one day.
Chris's links have somehow disappeared from sight. However, I think he posted this:
https://www.youtube.com/watch?v=Cx-Mc0DCpfE&t=545s
And this:
https://masstransit-project.com/usage/testing.html
The rewritten method:
public interface ICommand
{
Guid OrderId { get; }
}
public record Command : ICommand
{
public Command(Guid orderId)
{
OrderId = orderId;
}
public Guid OrderId { get; }
}
public class Handler : IConsumer<ICommand>
{
private readonly IOrderRepository _orderRepository;
public Handler(IOrderRepository orderRepository)
{
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
}
public async Task Consume(ConsumeContext<ICommand> context)
{
var command = context.Message;
OrderId orderId = new OrderId(command.OrderId);
var order = await _orderRepository.FindOrderAsync(orderId, context.CancellationToken);
if (order is null)
{
await context.RespondAsync(CommandResponse.NotFound);
return;
}
order.Cancel();
await _orderRepository.SaveOrderAsync(order, context.CancellationToken);
await context.RespondAsync(CommandResponse.Submitted);
}
}
The rewritten unit test class:
[TestClass()]
public class Handler_Tests
{
private static OrderId ExistingOrderId = new OrderId("d94108e4-1121-401a-a6ef-c7736054041d");
private static OrderId NonExistentOrderId = new OrderId("daaa72a0-8f8c-4a9b-b4ad-ebefbb6b5aa2");
private static CustomerId ExistingCustomerId = new CustomerId("5fbf40d8-c064-4821-8948-a520863e6242");
[TestMethod()]
public async Task Consume_WithExistingOrderId_CancelsOrderAndSavesChangesAndReturnsSubmitted2()
{
// Arrange
var harness = new InMemoryTestHarness();
var mockRepository = GetMockOrderRepository();
var sut = harness.Consumer(() =>
{
return new Handler(mockRepository.Object);
});
await harness.Start();
try
{
var requestClient = await harness.ConnectRequestClient<ICommand>();
var command = new Command(ExistingOrderId.Value);
// Act
var response = await requestClient.GetResponse<CommandResponse>(command, It.IsAny<CancellationToken>());
// Assert
mockRepository.Verify(x => x.SaveOrderAsync(It.IsAny<Order>(), It.IsAny<CancellationToken>()), Times.Once());
response.Message.Result.Should().Be(CommandResponse.Results.Submitted);
}
finally
{
await harness.Stop();
}
}
[TestMethod()]
public async Task Consume_WithNonExistentOrderId_ReturnsNotFoundResponseAndDoesNotSave()
{
// Arrange
var harness = new InMemoryTestHarness();
var mockRepository = GetMockOrderRepository();
var sut = harness.Consumer(() =>
{
return new Handler(mockRepository.Object);
});
await harness.Start();
try
{
var requestClient = await harness.ConnectRequestClient<ICommand>();
var command = new Command(NonExistentOrderId.Value);
var response = await requestClient.GetResponse<CommandResponse>(command, It.IsAny<CancellationToken>());
mockRepository.Verify(x => x.SaveOrderAsync(It.IsAny<Order>(), It.IsAny<CancellationToken>()), Times.Never());
response.Message.Result.Should().Be(CommandResponse.Results.NotFound);
}
finally
{
await harness.Stop();
}
}
private static Order GetTestOrder(OrderId orderId)
{
var orderItem = GetTestOrderItem();
return Order.Place(orderId, ExistingCustomerId, new[] { orderItem });
}
private static OrderItem GetTestOrderItem()
{
var productId = new ProductId(Guid.NewGuid());
var price = new Price(1, PurchaseCurrency.Usd);
var quantity = new Quantity(1, UnitOfMeasure.Each);
return new OrderItem(productId, quantity, price);
}
private static Mock<IOrderRepository> GetMockOrderRepository()
{
var mockRepository = new Mock<IOrderRepository>();
mockRepository.Setup(x => x.FindOrderAsync(It.IsAny<OrderId>(), It.IsAny<CancellationToken>()))
.ThrowsAsync(new InvalidOperationException(
$"This mock is set up to work with {nameof(ExistingOrderId)} and {nameof(NonExistentOrderId)}."));
mockRepository.Setup(x => x.FindOrderAsync(ExistingOrderId, It.IsAny<CancellationToken>()))
.ReturnsAsync(GetTestOrder(ExistingOrderId));
mockRepository.Setup(x => x.FindOrderAsync(NonExistentOrderId, It.IsAny<CancellationToken>()))
.ReturnsAsync((Order?)null);
return mockRepository;
}
}
I try to use AutoFixture in this test:
[Fact]
public void testLogin()
{
var response = new Mock<IServiceSomething2>();
response.Setup(r => r.RedirectToLoginPageWithInvalidLogin());
var sut = new Authenticate(null, response.Object, null, null, null);
sut.LoginGo("", "fake");
response.Verify(a => a.RedirectToLoginPageWithInvalidLogin(), Times.Once());
}
With
Authenticate(IServiceSomething1 i1, IServiceSomething2 response, Func<Obj1> login, Func<Obj2> func2, Action<string> action)
{
_i1 = i1;
_response = response;
_login = login;
_func2 = func2;
_action = action;
}
I try this:
[Fact]
public void testLogin()
{
var fixture = new Fixture().Customize(new AutoMoqCustomization());
var response = fixture.Freeze<Mock<IServiceSomething2>>();
response.Setup(r => r.RedirectToLoginPageWithInvalidLogin());
//fixture.Register(() => response.Object);
fixture.Inject(response.Object);
var sut = fixture.Create<Authenticate>();
sut.Login("", "fake");
response.Verify(a => a.RedirectToLoginPageWithInvalidLogin(), Times.Once());
}
and this:
public class AutoMoqDataAttribute : AutoDataAttribute
{
public AutoMoqDataAttribute()
: base(() => new Fixture().Customize(new AutoMoqCustomization() {}))
{
}
}
[Theory, AutoMoqData]
public void testLogin([Frozen]Mock<IServiceSomething2> response, Authenticate sut)
{
response.Setup(r => r.RedirectToLoginPageWithInvalidLogin());
sut.Login("", "fake");
response.Verify(a => a.RedirectToLoginPageWithInvalidLogin(), Times.Once());
}
But i can´t because the RedirectToLoginPageWithInvalidLogin method doesn´t mocking.
Can you help me?
I resolve this. The problem was that the interface was readonly and for don't change that i need to do this
var ex = fixture.Build().FromFactory(() => new Authenticate(null, response.Object, null, null, null)).Create();
I don't find other answer
i am new to integration tests. I have an xUnit project in my solution which contains one test only.
Here's the definition of my test:
[Fact]
public async Task ShouldCreateUser()
{
// Arrange
var createUserRequest = new CreateUserRequest
{
Login = "testowyLogin",
Password = "testoweHaslo",
FirstName = "testoweImie",
LastName = "testoweNazwisko",
MailAddress = "test#test.pl"
};
var serializedCreateUserRequest = SerializeObject(createUserRequest);
// Act
var response = await HttpClient.PostAsync(ApiRoutes.CreateUserAsyncRoute,
serializedCreateUserRequest);
// Assert
response
.StatusCode
.Should()
.Be(HttpStatusCode.OK);
}
And the BaseIntegrationTest class definition:
public abstract class BaseIntegrationTest
{
private const string TestDatabaseName = "TestDatabase";
protected BaseIntegrationTest()
{
var appFactory = new WebApplicationFactory<Startup>()
.WithWebHostBuilder(builder =>
{
builder.ConfigureServices(services =>
{
RemoveDatabaseContextFromServicesCollectionIfFound<EventStoreContext>(services);
RemoveDatabaseContextFromServicesCollectionIfFound<GrantContext>(services);
services
.AddDbContext<EventStoreContext>(options =>
options.UseInMemoryDatabase(TestDatabaseName))
.AddDbContext<GrantContext>(options =>
options.UseInMemoryDatabase(TestDatabaseName));
});
});
HttpClient = appFactory.CreateClient();
}
protected HttpClient HttpClient { get; }
protected static StringContent SerializeObject(object #object) =>
new StringContent(
JsonConvert.SerializeObject(#object),
Encoding.UTF8,
"application/json");
private static void RemoveDatabaseContextFromServicesCollectionIfFound<T>(IServiceCollection services)
where T : DbContext
{
var descriptor = services.SingleOrDefault(service =>
service.ServiceType == typeof(DbContextOptions<T>));
if (!(descriptor is null))
{
services
.Remove(descriptor);
}
}
}
When i run tests, it takes few seconds, and the test ends successfully. The problem is that Resharper Test Runner still runs, although i've already have collected results. what am i doing wrong here? Do i have to somehow dispose the HttpClient, after performing all tests? If so, how to achieve that? Thanks for any help.
It looks like you're actually booting the application inside the test rather than using the testhost (https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-3.1)
public class BasicTests
: IClassFixture<WebApplicationFactory<RazorPagesProject.Startup>>
{
private readonly WebApplicationFactory<RazorPagesProject.Startup> _factory;
public BasicTests(WebApplicationFactory<RazorPagesProject.Startup> factory)
{
_factory = factory;
}
[Theory]
[InlineData("/")]
[InlineData("/Index")]
[InlineData("/About")]
[InlineData("/Privacy")]
[InlineData("/Contact")]
public async Task Get_EndpointsReturnSuccessAndCorrectContentType(string url)
{
// Arrange
var client = _factory.CreateClient();
// Act
var response = await client.GetAsync(url);
// Assert
response.EnsureSuccessStatusCode(); // Status Code 200-299
Assert.Equal("text/html; charset=utf-8",
response.Content.Headers.ContentType.ToString());
}
}
Notice the IClassFixture stuff.
I am working in a .Net Core API. I wish to unit test the GetArtists method on the ArtistsController.
CODE
Here is my controller code:
[Route("artists")]
public class ArtistsController : Controller
{
private readonly IPermissionsService _permissionsService;
private readonly IArtistsService _artistsService;
private readonly ILogger<ArtistsController> _logger;
public ArtistsController(IPermissionsService permissionsService, IArtistsService artistsService, ILogger<ArtistsController> logger)
{
_permissionsService = permissionsService ?? throw new ArgumentNullException(nameof(permissionsService));
_artistsService = artistsService ?? throw new ArgumentNullException(nameof(artistsService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
[HttpGet]
public async Task<IActionResult> GetArtists()
{
var permissions = await _permissionsService.GetPermissionsAsync(HttpContext);
var artists = _artistsService.GetAllArtists(permissions.UserId, permissions.IsAdministrator);
return Ok( new { artists });
}
}
And here is the test method I am writing:
[TestClass]
public class ArtistsControllerTests
{
private readonly Mock<IPermissionsService> _mockPermissionsService = new Mock<IPermissionsService>();
private readonly Mock<IArtistsService> _mockArtistsService = new Mock<IArtistsService>();
private readonly Mock<ILogger<ArtistsController>> _mockLogger = new Mock<ILogger<ArtistsController>>();
public void Setup()
{
_mockArtistsService.Reset();
_mockPermissionsService
.Setup(service => service.GetPermissionsAsync(It.IsAny<HttpContext>()))
.Returns(Task.FromResult(new Permissions { UserId = "112233", IsAdministrator = false }));
_mockArtistsService.Setup(service => service.GetAllArtists(It.IsAny<string>(), false)).Returns(new ArtistCardDtoCollection());
}
[TestMethod]
public async Task GetArtists_ReturnsOKStatusCode()
{
// arrange
var artistsController = new ArtistsController(_mockPermissionsService.Object, _mockArtistsService.Object, _mockLogger.Object);
// act
var getArtistsResult = await artistsController.GetArtists();
var okResult = getArtistsResult as OkObjectResult;
// assert
Assert.IsInstanceOfType(okResult, typeof(OkObjectResult));
}
}
Here is the IPermissionsService and the Permissions class.
public interface IPermissionsService
{
Task<Permissions> GetPermissionsAsync(HttpContext httpContext);
}
public class Permissions
{
public string UserId { get; set; }
public bool IsAdministrator { get; set; }
}
When I run that, I get the following error:
Project.ArtistsControllerTests.GetArtists_ReturnsOKStatusCode threw exception:
System.NullReferenceException: Object reference not set to an instance of an object.
When debugging, I found out that var permissions = await _permissionsService.GetPermissionsAsync(HttpContext); returns null.
I must have an issue with the way I am mocking that:
_mockPermissionsService
.Setup(service => service.GetPermissionsAsync(It.IsAny<HttpContext>()))
Why wouldn't the above work?
ArtistsControllerTests.Setup() is not being invoked so the mocks are not being setup before the test is exercised.
Therefore when the test is exercised they will return null.
Your setup code is correct, it just is not getting called.
either change that Setup method to a constructor
public ArtistsControllerTests() {
_mockArtistsService.Reset();
_mockPermissionsService
.Setup(service => service.GetPermissionsAsync(It.IsAny<HttpContext>()))
.Returns(Task.FromResult(new Permissions { UserId = "112233", IsAdministrator = false }));
_mockArtistsService.Setup(service => service.GetAllArtists(It.IsAny<string>(), false)).Returns(new ArtistCardDtoCollection());
}
or adorn the method with [TestInitilize] attribute
[TestInitialize]
public void Setup() {
_mockArtistsService.Reset();
_mockPermissionsService
.Setup(service => service.GetPermissionsAsync(It.IsAny<HttpContext>()))
.Returns(Task.FromResult(new Permissions { UserId = "112233", IsAdministrator = false }));
_mockArtistsService.Setup(service => service.GetAllArtists(It.IsAny<string>(), false)).Returns(new ArtistCardDtoCollection());
}
or just move the arrange into the test itself
[TestMethod]
public async Task GetArtists_ReturnsOKStatusCode() {
// arrange
_mockArtistsService.Reset();
_mockPermissionsService
.Setup(service => service.GetPermissionsAsync(It.IsAny<HttpContext>()))
.Returns(Task.FromResult(new Permissions { UserId = "112233", IsAdministrator = false }));
_mockArtistsService.Setup(service => service.GetAllArtists(It.IsAny<string>(), false)).Returns(new ArtistCardDtoCollection());
var artistsController = new ArtistsController(_mockPermissionsService.Object, _mockArtistsService.Object, _mockLogger.Object);
// act
var getArtistsResult = await artistsController.GetArtists();
var okResult = getArtistsResult as OkObjectResult;
// assert
Assert.IsInstanceOfType(okResult, typeof(OkObjectResult));
}
I have ChangePassword method where I have User.Identity.GetUserId() to find UserId.
Problem: It always return null. Don't understand why.
I read in another post that the GetUserById use below line of code to find Id. I am not sure how do I mock ClaimsTypes.NameIdentifier.
return ci.FindFirstValue(ClaimTypes.NameIdentifier);
ChangePassword method (method to be unit testes)
public async Task<IHttpActionResult> ChangePassword(string NewPassword, string OldPassword)
{
_tstService = new TestService();
IdentityResult result = await _tstService.ChangePassword(User.Identity.GetUserId(), OldPassword, NewPassword);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
Unit Test
var mock = new Mock<MyController>();
mock.CallBase = true;
var obj = mock.Object;
obj.ControllerContext = new HttpControllerContext { Request = new HttpRequestMessage() };
obj.Request.SetOwinContext(CommonCodeHelper.mockOwinContext());
IPrincipal user = GetPrincipal();
obj.ControllerContext.RequestContext.Principal = user;
var result = await obj.ChangePassword(dto);
//GetPrincipal
public static IPrincipal GetPrincipal()
{
var user = new Mock<IPrincipal>();
var identity = new Mock<IIdentity>();
identity.Setup(x => x.Name).Returns("User1#Test.com");
identity.Setup(p => p.IsAuthenticated).Returns(true);
user.Setup(x => x.Identity).Returns(identity.Object);
Thread.CurrentPrincipal = user.Object;
return user.Object;
}
IOwinContext mocking code
public static IOwinContext mockOwinContext()
{
var owinMock = new Mock<IOwinContext>();
owinMock.Setup(o => o.Authentication.User).Returns(new ClaimsPrincipal());
owinMock.Setup(o => o.Request).Returns(new Mock<OwinRequest>().Object);
owinMock.Setup(o => o.Response).Returns(new Mock<OwinResponse>().Object);
owinMock.Setup(o => o.Environment).Returns(new Dictionary<string, object> { { "key1", 123 } });
var traceMock = new Mock<TextWriter>();
owinMock.Setup(o => o.TraceOutput).Returns(traceMock.Object);
var userStoreMock = new Mock<IUserStore<IfsUser>>();
userStoreMock.Setup(s => s.FindByIdAsync("User1#ifstoolsuite.com")).ReturnsAsync(new IfsUser
{
Id = "User1#test.com",
FirstName = "Test",
LastName = "User1",
Email = "User1#test.com",
UserName = "User1#test.com",
});
var applicationUserManager = new IfsUserManager(userStoreMock.Object);
owinMock.Setup(o => o.Get<IfsUserManager>(It.IsAny<string>())).Returns(applicationUserManager);
return owinMock.Object;
}
Your GetPrincipal can be updated to use claims.
public static IPrincipal GetPrincipal() {
//use an actual identity fake
var username = "User1#Test.com";
var identity = new GenericIdentity(username, "");
//create claim and add it to indentity
var nameIdentifierClaim = new Claim(ClaimTypes.NameIdentifier, username);
identity.AddClaim(nameIdentifierClaim);
var user = new Mock<IPrincipal>();
user.Setup(x => x.Identity).Returns(identity);
Thread.CurrentPrincipal = user.Object;
return user.Object;
}
Here is an example that shows how the above approach works.
public partial class MiscUnitTests {
[TestClass]
public class IdentityTests : MiscUnitTests {
Mock<IPrincipal> mockPrincipal;
string username = "test#test.com";
[TestInitialize]
public override void Init() {
//Arrange
var identity = new GenericIdentity(username, "");
var nameIdentifierClaim = new Claim(ClaimTypes.NameIdentifier, username);
identity.AddClaim(nameIdentifierClaim);
mockPrincipal = new Mock<IPrincipal>();
mockPrincipal.Setup(x => x.Identity).Returns(identity);
mockPrincipal.Setup(x => x.IsInRole(It.IsAny<string>())).Returns(true);
}
[TestMethod]
public void Should_GetUserId_From_Identity() {
var principal = mockPrincipal.Object;
//Act
var result = principal.Identity.GetUserId();
//Asserts
Assert.AreEqual(username, result);
}
[TestMethod]
public void Identity_Should_Be_Authenticated() {
var principal = mockPrincipal.Object;
//Asserts
Assert.IsTrue(principal.Identity.IsAuthenticated);
}
}
}
You have some design issues. Creating a concrete TestService will cause problems if it as connecting to an actual implementation. That becomes an integration test. Abstract that dependency as well.
public interface ITestService {
Task<IdentityResult> ChangePassword(string userId, string oldPassword, string newPassword);
}
public abstract class MyController : ApiController {
private ITestService service;
protected MyController(ITestService service) {
this.service = service;
}
public async Task<IHttpActionResult> ChangePassword(string NewPassword, string OldPassword) {
IdentityResult result = await service.ChangePassword(User.Identity.GetUserId(), OldPassword, NewPassword);
if (!result.Succeeded) {
return GetErrorResult(result);
}
return Ok();
}
}
Also you should not mock the System under test. You should mock the dependencies of the SUT. Based on your method to be tested and what you indicated in the comments that MyController is an abstract class, the following test should apply
[TestClass]
public class MyControllerTests {
public class FakeController : MyController {
public FakeController(ITestService service) : base(service) { }
}
[TestMethod]
public void TestMyController() {
//Arrange
var mockService = new Mock<ITestService>();
mockService
.Setup(m => m.ChangePassword(....))
.ReturnsAsync(....);
var controller = new FakeController(mockService.Object);
//Set a fake request. If your controller creates responses you will need this
controller.Request = new HttpRequestMessage {
RequestUri = new Uri("http://localhost/api/my")
};
controller.Configuration = new HttpConfiguration();
controller.User = GetPrincipal();
//Act
var result = await controller.ChangePassword("NewPassword", "OldPassword");
//Assert
//...
}
}