I have a test method that is failing on sso.verify notice the CheckUsername method has two await calls in a async method ? but because of that the sso verify never returns and thus is not verified. But the code is called. What would be the proper way to test this ?
public void Setup()
{
nav = new Mock<INavService>();
sso = new Mock<ISSOApiService>();
_vm_Successs = new ForgotPasswordViewModel(nav.Object, sso.Object);
sso.Setup(x => x.SendEmailCodeRequestAsync(It.IsAny<PasswordTokenRequest>())).ReturnsAsync(new StandardResponse() { ErrorCode = null }).Verifiable();
nav.Setup(x => x.NavigateTo<ForgotPasswordEnterCodeModel, string>(It.IsAny<string>())).Verifiable();
}
[Test]
public void CheckUserName_Success()
{
_vm_Successs.UserName = "Timmy";
var response = _vm_Successs.CheckUsername();
sso.Verify(e => e.SendEmailCodeRequest(It.IsAny<PasswordTokenRequest>()), Times.Once);
nav.Verify(mock => mock.NavigateTo<ForgotPasswordEnterCodeModel, string>(It.IsAny<string>()), Times.Once);
}
This is the checkusername method
public async Task CheckUsername()
{
PasswordTokenRequest r = new PasswordTokenRequest();
await SSOAPIService.SendEmailCodeRequestAsync(r);
await NavService.NavigateTo<ForgotPasswordEnterCodeModel, string>(UserName);
}
you should await test method therefore you need to make your test 'async Task' type
also need to setup SendEmailCodeRequestAsync with ReturnsAsync
[Test]
public async Task ShouldDeleteAzureBlobById()
{
sso.Setup(x => x.SendEmailCodeRequestAsync(It.IsAny<PasswordTokenRequest>()))
.ReturnsAsync(new StandardResponse() { ErrorCode = null })
.Verifiable();
_vm_Successs.UserName = "Timmy";
var response = await _vm_Successs.CheckUsername();
sso.Verify(e => e.SendEmailCodeRequestAsync(It.IsAny<PasswordTokenRequest>()), Times.Once);
nav.Verify(mock => mock.NavigateTo<ForgotPasswordEnterCodeModel, string>(It.IsAny<string>()), Times.Once);
}
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'm trying to write a test for a service that uses IStringLocalizerFactory to translate strings. All of the translations are in a single Resource file. I cannot seem to get the Mock for it to work, as it always throws a NullReferenceException. When debugging, it shows that _localizer is null. When I remove the localizer logic completely, the test succeeds.
Code I'm trying to test:
private readonly IStringLocalizer _localizer;
public EventService(IEventRepository eventRepository, IMemberEventRepository memberEventRepository, IStringLocalizerFactory factory)
{
this._eventRepository = eventRepository;
this._memberEventRepository = memberEventRepository;
this._localizer = factory.Create(typeof(Resource));
}
public async Task CreateEventRegistrationAsync(MemberEvent entity)
{
if (await this._memberEventRepository.GetMemberEvent(entity.EventId, entity.MemberId) != null)
{
throw new ArgumentException(_localizer["This member already participates in this event."].Value);
}
await this._memberEventRepository.CreateAsync(entity);
}
My tests:
private Mock<IStringLocalizerFactory> _stringLocalizerFactoryMock = new Mock<IStringLocalizerFactory>();
public EventServiceTests()
{
_service = new EventService(_eventRepoMock.Object, _memberEventRepoMock.Object, _stringLocalizerFactoryMock.Object);
}
[Fact]
public async Task CreateEventRegistrationAsync_ShouldThrowArgumentException_WhenMemberAlreadyRegisteredForEvent()
{
int eventId = 456;
int memberId = 123;
_stringLocalizerFactoryMock.Setup(x => x.Create(typeof(Resource)))
.Returns(() => new StringLocalizer<Resource>(_stringLocalizerFactoryMock.Object));
MemberEvent registration = new MemberEvent
{
EventId = eventId,
MemberId = memberId
};
_memberEventRepoMock.Setup(x => x.GetMemberEvent(eventId, memberId))
.ReturnsAsync(registration);
await Assert.ThrowsAsync<ArgumentException>(async () => await _service.CreateEventRegistrationAsync(registration));
}
From examining the subject under test I see that
_localizer["This member already participates in this event."].Value
will throw a null exception because _localizer[...] was not setup and thus fail when .Value is invoked.
Consider mocking a IStringLocalizer<T> so that it can be setup to behave as expected when the test is invoked.
//...
var localizer = new Mock<IStringLocalizer<Resource>>();
localizer
.Setup(_ => _[It.IsAny<string>()])
.Returns(string key => new LocalizedString(key, key));
_stringLocalizerFactoryMock
.Setup(_ => _.Create(It.IsAny<Type>()))
.Returns(() => localizer.Object));
//...
I have a method:
public class CreateNewAccountUseCase
{
private readonly IAccountRepository _accountRepository;
public CreateNewAccountUseCase(IAccountRepository accountRepository)
{
_accountRepository = accountRepository;
}
public virtual async Task Execute(Account account)
{
await _accountRepository.Create(account);
}
}
I use it in the controller:
public async Task<ActionResult<AccountModel>> CreateAccount([FromBody]CreatingAccount creatingAccount)
{
var account = creatingAccount.ConvertToAccount();
await _createNewAccountUseCase.Execute(account);
return Created($"/accounts/{account.Id}", account);
}
I want to be sure that the UseCase method will be exactly called.
I wrote this controller test:
[Fact]
public async Task Create()
{
// arrange
var account = new CreatingAccount();
var accountRepository = new Mock<IAccountRepository>();
var createNewAccountUseCase = new Mock<CreateNewAccountUseCase>(accountRepository.Object);
createNewAccountUseCase.Setup(m => m.Execute(account)).Returns(Task.CompletedTask);
// act
await controller.CreateAccount(account);
// assert
createNewAccountUseCase.Verify(m => m.Execute(account), Times.Once);
}
I expect createNewAccountUseCase.Verify() to check if the method has been method but I get failed test with a message:
Moq.MockException :
Expected invocation on the mock once, but was 0 times: m => m.Execute(account)
Performed invocations:
Mock<CreateNewAccountUseCase:1> (m):
No invocations performed.
How do I make sure the method (createNewAccountUseCase.Verify()) is called?
You are mocking the subject under test. Instead mock the dependency and invoked the method under test
[Fact]
public async Task Create() {
// arrange
var account = new CreatingAccount();
var accountRepository = new Mock<IAccountRepository>();
accountRepository.Setup(m => m.Create(It.IsAny<Account>())).Returns(Task.CompletedTask);
var createNewAccountUseCase = new CreateNewAccountUseCase(accountRepository.Object);
// act
await createNewAccountUseCase.Execute(account.ConvertToAccount());
// assert
accountRepository.Verify(m => m.Create(It.IsAny<Account>()), Times.Once);
}
The assertion can then be done on the repository mock to verify that the method behaves as expected.
In the controller test of your question you could have used the mock use case but your also need to make sure that it was injected into the controller.
But your example showed no such injection.
The following example creates the controller and explicitly injects the dependency
[Fact]
public async Task Create() {
// arrange
var account = new CreatingAccount();
var accountRepository = new Mock<IAccountRepository>();
accountRepository.Setup(m => m.Create(It.IsAny<Account>())).Returns(Task.CompletedTask);
var createNewAccountUseCase = new CreateNewAccountUseCase(accountRepository.Object);
var controller = new MyController(createNewAccountUseCase);
// act
await controller.CreateAccount(account);
// assert
accountRepository.Verify(m => m.Create(It.IsAny<Account>()), Times.Once);
}
I am trying to trace a chain of redirects (for an online ad pixel) programmatically, but with a timeout of 2 seconds (in other words, if the redirect chain takes more than 2 seconds to resolve, I want it to abort and return null).
My code is (more or less) running synchronously, so I had to do some acrobatics to do what I wanted, but functionally speaking, it seems to work... except for the timeout part.
I have some asynchronous helpers like so:
public static async Task<TResult> TimeoutAfter<TResult>(this Task<TResult> task, TimeSpan timeout)
{
using (var timeoutCancellationTokenSource = new CancellationTokenSource())
{
var completedTask = await Task.WhenAny(task, Task.Delay(timeout, timeoutCancellationTokenSource.Token));
if (completedTask != task)
{
throw new TimeoutException();
}
timeoutCancellationTokenSource.Cancel();
return await task;
}
}
public static T ToSynchronousResult<T>(this Task<T> task)
{
return Task.Run(async () => await task).Result;
}
The TimeoutAfter() helper method was adapted from the SO article that can be found here. In my service I have a method that resembles this:
public string GetFinalUrl(string url)
{
string finalUrl;
try
{
finalUrl = FollowDestinationUrl(url).TimeoutAfter(TimeSpan.FromSeconds(2)).ToSynchronousResult();
}
catch (TimeoutException)
{
finalUrl = null;
}
return finalUrl;
}
private async Task<string> FollowDestinationUrl(string url)
{
var request = _webRequestFactory.CreateGet(url);
var payload = await request.GetResponseAsync();
return payload.ResponseUri.ToString();
}
The _webRequestFactory here returns an HttpWebRequest abstraction that was written as an IHttpRequest.
In my success case unit test (response under 2 seconds), I get back the result I expect:
private class TestWebResponse : WebResponse
{
public override Uri ResponseUri => new Uri("https://www.mytest.com/responseIsGood");
}
[TestMethod]
public void RedirectUriUnderTimeout()
{
//arrange
var service = GetService();
A.CallTo(() => _httpRequest.GetResponseAsync()).ReturnsLazily(() => new TestWebResponse());
A.CallTo(() => _httpRequest.GetResponseString())
.ReturnsLazily(() => VALID_REQUEST_PAYLOAD);
//act
var url = service.GetFinalUrl("https://someplace.com/testurl");
//assert
Assert.IsNotNull(url);
}
...however, when I try to implement a delay to verify the timeout is working correctly, it's not aborting as I would expect:
[TestMethod]
public void RedirectUriUnderTimeout()
{
//arrange
var service = GetService();
A.CallTo(() => _httpRequest.GetResponseAsync()).ReturnsLazily(() => {
Thread.Sleep(TimeSpan.FromSeconds(3));
return new TestWebResponse();
});
A.CallTo(() => _httpRequest.GetResponseString())
.ReturnsLazily(() => VALID_REQUEST_PAYLOAD);
//act
var url = service.GetFinalUrl("https://someplace.com/testurl");
//assert
Assert.IsNull(url);
}
It seems like it waits for the full three seconds, before returning the TestWebResponse that has a non-null ResponseUri.
I don't know if there's something fundamentally wrong with my implementation, or wrong with my test, but obviously I'm blocking an async call in a way I'm not expecting to.
Can someone help me identify what I've done wrong?
public static T ToSynchronousResult<T>(this Task<T> task)
{
return Task.Run(async () => await task).Result;
}
This part causes to get thread blocked.As you mentioned the method ToSynchronousResult, it will block the thread until task result returned. You should follow "async all the way" rule and you should use await. It is only way to apply async efficiently.
public async Task<string> GetFinalUrl(string url)
{
string finalUrl;
try
{
finalUrl = await FollowDestinationUrl(url).TimeoutAfter(TimeSpan.FromSeconds(2));
}
catch (TimeoutException)
{
finalUrl = null;
}
return finalUrl;
}
OK, it looks like I was way overthinking it. #Stormcloak clued me in that what I was doing wasn't going to work, so I started looking at alternatives, and I realized that while the async/ await pattern weren't appropriate here, the TPL library still came in handy.
I changed my FinalDestinationUrl method to synchronous like so:
private string FollowDestinationUrl(string url)
{
var request = _webRequestFactory.CreateGet(url);
var payload = request.GetResponse();
return payload.ResponseUri.ToString();
}
then I called it like so:
var task = Task.Run(() => FollowDestinationUrl(destinationUrl));
finalUrl = task.Wait(TimeSpan.FromSeconds(2)) ? task.Result : null;
Then I changed my unit test to resemble:
[TestMethod]
public void RedirectUriUnderTimeout()
{
//arrange
var service = GetService();
A.CallTo(() => _httpRequest.GetResponse()).ReturnsLazily(() => {
Thread.Sleep(TimeSpan.FromSeconds(3));
return new TestWebResponse();
});
A.CallTo(() => _httpRequest.GetResponseString())
.ReturnsLazily(() => VALID_REQUEST_PAYLOAD);
//act
var url = service.GetFinalUrl("https://someplace.com/testurl");
//assert
Assert.IsNull(url);
}
The test passed. All is well in the world. Thanks!
I'm new to testing with Moq, so I'm confused a bit regarding ReturnsAsync.
Here is my test method, where I'm expecting ReturnsAsync should receive type that will be returned by method defined in Setup. Am I right?
But seemed ReturnsAsync should have the other signature - it's looking for Func delegate.
[TestClass]
public class TourObjectControllerTest
{
[TestMethod]
public async Task GetTourObjects()
{
var mockService = new Mock<ITourObjectService>(MockBehavior.Default);
mockService.Setup(x => x.GetAll()).ReturnsAsync(new Task<IEnumerable<TourObjectDTO>>);
var controller = new TourObjectController(mockService.Object);
var result = await controller.Get();
Assert.IsNotNull(result);
Assert.AreSame(typeof(TourObjectViewModel), result);
}
}
Method under test:
public async Task<IEnumerable<TourObjectViewModel>> Get()
{
IEnumerable<TourObjectViewModel> viewmodel = null;
try
{
var tourobjects = await _tos.GetAll();
viewmodel = Mapper.Map<IEnumerable<TourObjectViewModel>>(tourobjects);
}
catch (Exception ex)
{
Log.ErrorFormat("Method:{0} <br/> Error: {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, ex);
}
return viewmodel;
}
Pass an actual result.
Assuming ITourObjectService.GetAll() returns Task<IEnumerable<TourObjectDTO>>
eg
[TestClass]
public class TourObjectControllerTest
{
[TestMethod]
public async Task GetTourObjects()
{
var fakeData = new List<TourObjectDTO>() {
new TourObjectDTO { ... }
};
var mockService = new Mock<ITourObjectService>(MockBehavior.Default);
mockService.Setup(x => x.GetAll()).ReturnsAsync(fakeData);
var controller = new TourObjectController(mockService.Object);
var result = await controller.Get();
Assert.IsNotNull(result);
Assert.IsTry(typeof(IEnumerable<TourObjectViewModel>).IsAssignableFrom(result.GetType());
}
}
ReturnsAsync() should take a return value as the paramter instead of Task<TReurnValue>. For example, next 2 lines of code are equal (by behavior):
mockService.Setup(x => x.GetAll()).ReturnsAsync(new List<TourObjectDTO>());
mockService.Setup(x => x.GetAll()).Returns(Task.FromResult(new List<TourObjectDTO>()));
You need to replace new List<TourObjectDTO>() with your test data which you want to retrieve in the test from the mock. For example, you can create just few test values:
var testData = new [] { new TourObjectDTO(1, "test1"), new TourObjectDTO(2, "test2") };
mockService.Setup(x => x.GetAll()).ReturnsAsync(testData);
or create fake data generator if needed.
Note that ReturnsAsync() is only available for methods that return a Task<T>. For methods that return only a Task, .Returns(Task.FromResult(default(object))) can be used.