I have the following method:
public void SetHttpStatusCode(HttpStatusCode httpStatusCode)
{
Response.StatusCode = (int)httpStatusCode;
}
And the following test:
[TestMethod]
public void SetHttpStatusCode_SetsCorrectStatusCode()
{
//Arrange
//Any url will suffice
var mockHttpContext = TestHelpers.MakeHttpContext("");
mockHttpContext.SetupSet(x => x.Response.StatusCode = It.IsAny<int>());
//creates an instance of an asp.net mvc controller
var controller = new AppController()
{
ControllerContext = new ControllerContext() {
HttpContext = mockHttpContext.Object }
};
// Act
controller.SetHttpStatusCode(HttpStatusCode.OK);
//Assert
mockHttpContext.VerifySet(x => x.Response.StatusCode = It.IsAny<int>());
}
Also, Here is MakeHttpContext
public static Mock<HttpContextBase> MakeHttpContext(string url)
{
var mockHttpContext = new Mock<HttpContextBase>();
var mockRequest = new Mock<HttpRequestBase>();
var mockResponse = new Mock<HttpResponseBase>();
var mockSession = new Mock<HttpSessionStateBase>();
//request
mockRequest.Setup(x => x.AppRelativeCurrentExecutionFilePath).Returns(url);
mockHttpContext.Setup(x => x.Request).Returns(mockRequest.Object);
//response
mockResponse.Setup(x => x.ApplyAppPathModifier(It.IsAny<string>())).Returns<string>(x => x);
mockHttpContext.Setup(x => x.Response).Returns(mockResponse.Object);
//session
mockHttpContext.Setup(x => x.Session).Returns(mockSession.Object);
return mockHttpContext;
}
When I run the test, I get the following exception:
Test method PA.Tests.Controllers.AppControllerTest.SetHttpStatusCode_SetsCorrectStatusCode
threw exception:
Moq.MockException:
Expected invocation on the mock at least once,
but was never performed: x => x.StatusCode = It.IsAny<Int32>()
Configured setups:
x => x.StatusCode = It.IsAny<Int32>(), Times.Never
No invocations performed.
How does Moq expect/require invocations to be called? I've debugged the SetHTTPStatusCode method, the response object is indeed a mocked object, however Moq insists that there was no invocation. Am I missing something?
Thanks!
You haven't shown what your TestHelpers.MakeHttpContext method does so it's a bit difficult to understand what's going on.
Try like this:
// Arrange
var mockHttpContext = new Mock<HttpContextBase>();
var response = new Mock<HttpResponseBase>();
mockHttpContext.SetupGet(x => x.Response).Returns(response.Object);
//creates an instance of an asp.net mvc controller
var controller = new AppController()
{
ControllerContext = new ControllerContext()
{
HttpContext = mockHttpContext.Object
}
};
// Act
controller.SetHttpStatusCode(HttpStatusCode.OK);
//Assert
response.VerifySet(x => x.StatusCode = (int)HttpStatusCode.OK);
Related
I am using polly to handle retry (see below code). How can I unit test polly retry? using xunit and moq
services.AddHttpClient("GitHub", client =>
{
client.BaseAddress = new Uri("https://api.github.com/");
client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
})
.AddTransientHttpErrorPolicy(builder => builder.WaitAndRetryAsync(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(10)
}));
As suggested in the comments I recommend Simmy.
It allows you to inject exceptions, return BadRequests and etc. in order to trigger Polly's fault and resilience policies such as WaitAndRetry.
These are a few samples from the documentation.
Inject (Socket) exception
var chaosPolicy = MonkeyPolicy.InjectException(Action<InjectOutcomeOptions<Exception>>);
For example: it causes the policy to throw SocketException with a probability of 5% if enabled
var fault = new SocketException(errorCode: 10013);
var chaosPolicy = MonkeyPolicy.InjectException(with =>
with.Fault(fault)
.InjectionRate(0.05)
.Enabled()
);
Inject (BadRequest) result
var chaosPolicy = MonkeyPolicy.InjectResult(Action<InjectOutcomeOptions<TResult>>);
For example: it causes the policy to return a bad request HttpResponseMessage with a probability of 5% if enabled
var result = new HttpResponseMessage(HttpStatusCode.BadRequest);
var chaosPolicy = MonkeyPolicy.InjectResult<HttpResponseMessage>(with =>
with.Result(result)
.InjectionRate(0.05)
.Enabled()
);
Simply set the InjectionRate to 1 to guarantee a fault in your unit test.
If you want to use the InjectionRate less than 1 you can use xunit and moq chaining via SetupSequence and Moq.Language.ISetupSequentialResult. Here's an example from an blockchain challenge I had to do, I execute 4 calls in a row, so if the InjectionRate is 0.25 one of the 4 calls would trigger a Polly policy:
[Fact]
public async Task Should_Return_GetEthereumTransactionsAsync()
{
// Arrange
IConfiguration settings = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
IOptions<Settings> optionSettings = Options.Create(new Settings
{
CompanyKeyAPI = settings.GetSection("CompanyKeyAPI").Value,
ProjectId = settings.GetSection("ProjectId").Value
});
var sequenceHttpResponse = new List<Tuple<HttpStatusCode, HttpContent>>
{
new Tuple<HttpStatusCode, HttpContent>(HttpStatusCode.OK, ApiCompanyKeyResponses.EthereumBlockWithTransactionHashs()),
new Tuple<HttpStatusCode, HttpContent>(HttpStatusCode.OK, ApiCompanyKeyResponses.Transaction(1)),
new Tuple<HttpStatusCode, HttpContent>(HttpStatusCode.OK, ApiCompanyKeyResponses.Transaction(2)),
new Tuple<HttpStatusCode, HttpContent>(HttpStatusCode.OK, ApiCompanyKeyResponses.Transaction(3))
};
IHttpClientFactory httpClientFactory = base.GetChainedCompanyKeyHttpClientFactory(new Uri(Path.Combine(optionSettings.Value.CompanyKeyAPI, optionSettings.Value.ProjectId)), sequenceHttpResponse);
CompanyKeyService CompanyKeyService = new CompanyKeyService(httpClientFactory);
// Act
List<EthereumTransaction> ethereumTransactionsResult = CompanyKeyService.GetEthereumTransactionsAsync(blockNumber, address).Result;
// Assert
Assert.IsType<List<EthereumTransaction>>(ethereumTransactionsResult);
Assert.NotNull(ethereumTransactionsResult);
Assert.Equal(ethereumTransactionsResult.Count, 3);
Assert.Equal(ethereumTransactionsResult[0].result.blockHash, blockHash);
}
public IHttpClientFactory GetChainedCompanyKeyHttpClientFactory(Uri uri, List<Tuple<HttpStatusCode, HttpContent>> httpReturns, HttpStatusCode statusCode = HttpStatusCode.OK)
{
Mock<HttpMessageHandler> httpMsgHandler = new Mock<HttpMessageHandler>();
var handlerPart = httpMsgHandler.Protected().SetupSequence<Task<HttpResponseMessage>>("SendAsync", new object[2]
{
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
});
foreach (var httpResult in httpReturns)
{
handlerPart = AddReturnPart(handlerPart, httpResult.Item1, httpResult.Item2);
}
httpMsgHandler.Verify();
HttpClient client = new HttpClient(httpMsgHandler.Object)
{
BaseAddress = uri
};
Mock<IHttpClientFactory> clientFactory = new Mock<IHttpClientFactory>();
clientFactory.Setup((IHttpClientFactory cf) => cf.CreateClient(It.IsAny<string>())).Returns(client);
return clientFactory.Object;
}
private Moq.Language.ISetupSequentialResult<Task<HttpResponseMessage>> AddReturnPart(Moq.Language.ISetupSequentialResult<Task<HttpResponseMessage>> handlerPart,
HttpStatusCode statusCode, HttpContent content)
{
return handlerPart
// prepare the expected response of the mocked http call
.ReturnsAsync(new HttpResponseMessage()
{
StatusCode = statusCode,
Content = content
});
}
....
public class CompanyKeyService : ICompanyKeyService
{
private readonly IHttpClientFactory _clientFactory;
private readonly HttpClient _client;
public CompanyKeyService(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
_client = _clientFactory.CreateClient("GitHub");
}
public async Task<List<EthereumTransaction>> GetEthereumTransactionsAsync(string blockNumber, string address)
{
//Validation removed...
List<string> transactionHashs = await GetEthereumTransactionHashsByBlockNumberAsync(blockNumber);
if (transactionHashs == null) throw new Exception("Invalid entry. Please check the Block Number.");
var tasks = transactionHashs.Select(hash => GetTransactionByHashAsync(hash, address));
EthereumTransaction[] lists = await Task.WhenAll(tasks);
return lists.Where(item => item != null).ToList();
}
}
You can unit test this by mocking out the HttpClient and setting up your own test version of the WaitAndRetryAsync policy.
Example:
var mockHttpClient = new Mock<HttpClient>();
var mockRetryPolicy = new Mock<IAsyncPolicy<HttpResponseMessage>>();
mockRetryPolicy
.Setup(p => p.ExecuteAsync(It.IsAny<Func<Context, CancellationToken, Task<HttpResponseMessage>>>(), It.IsAny<Context>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new HttpResponseMessage());
var services = new ServiceCollection();
services
.AddHttpClient("GitHub", client =>
{
client.BaseAddress = new Uri("https://api.github.com/");
client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
})
.AddTransientHttpErrorPolicy(builder => mockRetryPolicy.Object);
var serviceProvider = services.BuildServiceProvider();
var httpClientFactory = serviceProvider.GetRequiredService<IHttpClientFactory>();
var httpClient = httpClientFactory.CreateClient("GitHub");
Assert.NotNull(httpClient);
I'm working on CRUD unit test cases with having configuration .Net 5, Automapper, xUnit etc.
The issue:
So right now I'm having issue specifically in post call and when I uses Dto.
I tried lots of ways to resole it and I was also able to resolve it if I don't use Dto in post call as input parameter and just use Entity it self. (But I don't want to expose entity so I want to make it working with Dto only)
Below is test which is not working and it's controller side implementation.
Failing Test case:
[Fact]
public void PostTest()
{
try
{
//Arrange
var surveyRequest = new SurveyRequest()
{
Id = 0,
Name = "Survey Request 1",
CreateDate = DateTime.Now,
CreatedBy = 1
};
var addedSurveyRequest = new SurveyRequest()
{
Id = 1,
Name = "Survey Request 1",
CreateDate = DateTime.Now,
CreatedBy = 1
};
//setup mock
Mock<IRepositoryWrapper> mockRepo = new Mock<IRepositoryWrapper>();
mockRepo.Setup(m => m.SurveyRequest.Add(addedSurveyRequest)).Returns(new Response<SurveyRequest>(true, addedSurveyRequest));
//auto mapper
var mockMapper = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new AutoMapperProfile());
});
var mapper = mockMapper.CreateMapper();
SurveyRequestController controller = new SurveyRequestController(repositories: mockRepo.Object, mapper: mapper);
//Act
var model = mapper.Map<SurveyRequest, SurveyRequestDto>(source: addedSurveyRequest);
var result = controller.Post(model); // The issue with this post call here is that response remains null on repository level.
//Assert
var okResult = result as OkObjectResult;
Assert.NotNull(okResult);
//we will make sure that returned object is dto and not actual entity
var response = okResult.Value as SurveyRequestDtoWithId;
Assert.NotNull(response);
Assert.Equal(expected: response.Name, actual: model.Name);
}
catch (Exception ex)
{
//Assert
Assert.False(true, ex.Message);
}
}
Controller side post call:
[HttpPost("Insert")]
public IActionResult Post([FromBody] SurveyRequestDto model)
{
try
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
//If I remove this mapping from here, test case will work. (see next working test case)
var entity = _mapper.Map<SurveyRequestDto, SurveyRequest>(source: model);
entity.CreateDate = System.DateTime.Now;
entity.CreatedBy = 1;
var response = _repositories.SurveyRequest.Add(entity: entity); //Response remains null here
_repositories.Save();
if (response.IsSuccess == true)
return new OkObjectResult(_mapper.Map<SurveyRequest, SurveyRequestDtoWithId>(source: response.Data));
else
return new ObjectResult(response.ErrorMessage) { StatusCode = 500 };
}
catch (Exception ex)
{
return new ObjectResult(ex.Message) { StatusCode = 500 };
}
}
Working Test case:
[Fact]
public void PostTest2()
{
try
{
//Arrange
var surveyRequest = new SurveyRequest()
{
Id = 0,
Name = "Survey Request 1",
CreateDate = DateTime.Now,
CreatedBy = 1
};
var addedSurveyRequest = new SurveyRequest()
{
Id = 1,
Name = "Survey Request 1",
CreateDate = DateTime.Now,
CreatedBy = 1
};
//setup mock
Mock<IRepositoryWrapper> mockRepo = new Mock<IRepositoryWrapper>();
mockRepo.Setup(m => m.SurveyRequest.Add(surveyRequest)).Returns(value: new Response<SurveyRequest>(true, addedSurveyRequest));
//auto mapper
var mockMapper = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new AutoMapperProfile());
});
var mapper = mockMapper.CreateMapper();
//setup controlller
SurveyRequestController controller = new SurveyRequestController(repositories: mockRepo.Object, mapper: mapper);
//Act
//var model = mapper.Map<SurveyRequest, SurveyRequestDto>(source: surveyRequest);
var result = controller.Post2(entity: surveyRequest);
//Assert
var okResult = result as OkObjectResult;
Assert.NotNull(okResult);
///we will make sure that returned object is dto and not actual entity
var response = okResult.Value as SurveyRequestDtoWithId;
Assert.NotNull(response);
Assert.Equal(expected: response.Id, actual: addedSurveyRequest.Id);
Assert.Equal(expected: response.Name, actual: addedSurveyRequest.Name);
}
catch (Exception ex)
{
//Assert
Assert.False(true, ex.Message);
}
}
Controller side Post call for working test case:
[HttpPost("Insert")]
public IActionResult Post2([FromBody] SurveyRequest entity)
{
try
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
//var entity = _mapper.Map<SurveyRequestDto, SurveyRequest>(source: model);
//entity.CreateDate = System.DateTime.Now;
//entity.CreatedBy = 1;
var response = _repositories.SurveyRequest.Add(entity: entity); //This returns proper response with saved data and ID
_repositories.Save();
if (response.IsSuccess == true)
return new OkObjectResult(_mapper.Map<SurveyRequest, SurveyRequestDtoWithId>(source: response.Data));
else
return new ObjectResult(response.ErrorMessage) { StatusCode = 500 };
}
catch (Exception ex)
{
return new ObjectResult(ex.Message) { StatusCode = 500 };
}
}
I'm not sure whether my test case setup for mapper is wrong or any other issue. I also tried lots of ways but no luck so far. So posting here if someone can look and help, will be much appreciated.
If you are using IMapper then you can do something like this:
var mapperMock = new Mock<IMapper>();
mapperMock
.Setup(mapper => mapper.Map<SurveyRequestDto, SurveyRequest>(It.IsAny< SurveyRequestDto>()))
.Returns(surveyRequest);
This solution does not utilize the AutoMapperProfile, but because you have only a single mapping that's why I think it not really a problem.
If you want to call Verify on the mapperMock then I would suggest to extract the Map selector delegate like this:
private static Expression<Func<IMapper, SurveyRequestDto>> MapServiceModelFromRequestModelIsAny =>
mapper => mapper.Map<SurveyRequestDto, SurveyRequest>(It.IsAny< SurveyRequestDto>());
Usage
//Arrange
mapperMock
.Setup(MapServiceModelFromRequestModelIsAny)
.Returns(surveyRequest);
...
//Assert
mapperMock
.Verify(MapServiceModelFromRequestModelIsAny, Times.Once);
UPDATE #1
It might also make sense to be as explicit as possible when you make assertion. If you want to you can do deep equality check to make sure that controller's parameter is not amended before the Map call:
private static Expression<Func<IMapper, SurveyRequestDto>> MapServiceModelFromRequestModel(SurveyRequestDto input) =>
mapper => mapper.Map<SurveyRequestDto, SurveyRequest>(It.Is< SurveyRequestDto>(dto => dto.deepEquals(input)));
//Assert
mapperMock
.Verify(MapServiceModelFromRequestModel(model), Times.Once);
This assumes that deepEquals is available as an extension method.
UPDATE #2
As it turned out the mock repository's Setup code also had some problem. Namely it used the surveyRequest rather than a It.IsAny<SurveyRequest>().
Because surveyRequest was specified as the expected parameter that's why the setupped code path is never called but returned with null.
After changed it to It.IsAny then the whole test started to work :D
repoMock
.Setup(repo => repo.SurveyRequest.Add(It.IsAny<SurveyRequest>()))
.Returns(new Response<SurveyRequest>(true, addedSurveyRequest))
I want to mock the callback parameter of the following method:
Task ExecuteReadOnlyThreadSafe(Func<IUnitOfWork, Task> callback);
The call of the upper method in the method that will be tested looks like this:
await _dbContext.ExecuteReadOnlyThreadSafe(async uow =>
{
mobileDevices = (await uow.PushNotificationDeviceRepository.FindByUser(userId)).ToList();
});
And I want to set mobileDevices parameter to some value.
I've done the following mock:
User testUser = new User()
{
ID = 1
};
IEnumerable<PushNotificationDevice> testMobileDevices = new List<PushNotificationDevice>()
{
new PushNotificationDevice()
{
ID = 1,
RegistrationID = "XXXX-XXXX-XXXX",
NotificationPlatform = Shared.HelperMethods.Enums.NotificationPlatformTypes.Apns,
User = testUser
}
};
Mock<IDbContext> mockDbContext = new Mock<IDbContext>();
Mock<IUnitOfWork> mockUnitOfWork = new Mock<IUnitOfWork>();
Mock<IPushNotificationDeviceRepository> mockPushNotificationDeviceRepository = new Mock<IPushNotificationDeviceRepository>();
mockPushNotificationDeviceRepository
.Setup(x => x.FindByUser(It.IsAny<User>()))
.Returns(System.Threading.Tasks.Task.FromResult(testMobileDevices));
mockUnitOfWork
.SetupGet(x => x.PushNotificationDeviceRepository)
.Returns(mockPushNotificationDeviceRepository.Object);
mockDbContext.SetupProperty(x => x.CurrentUnitOfWork, mockUnitOfWork.Object);
var mockCallback = new Mock<Func<IUnitOfWork, System.Threading.Tasks.Task>>();
mockDbContext.Setup(x => x.ExecuteReadOnlyThreadSafe(It.IsAny<Func<IUnitOfWork,System.Threading.Tasks.Task>>()));
I got stuck on the mocking of ExecuteReadOnlyThreadSafe. How do I mock callback so that test will execute the content of the actual implementation:
await _dbContext.ExecuteReadOnlyThreadSafe(async uow =>
{
mobileDevices = (await uow.PushNotificationDeviceRepository.FindByUser(userId)).ToList();
});
I try to have a unit test for my service, I mocked everything needed, How I can Mock repository methods that Service is calling so that has value and code is not breaking,
This is my unit test:
public async Task Updateuser_ReturnsResponse()
{
// Arrange
var request = new UpdateUserRequest()
{
Guid = new Guid("92296ac1-f8e1-489a-a312-6ea9d31d60f8"),
FirstName = "TestFirst",
LastName = "TestLast",
PhoneWork = "9495467845",
EmailWork = "test123#yahoo.com",
};
var respose = new UpdateUserResponse()
{
Success = true
};
var getGuidRequest = new GetGuidRequest()
{
Guid = request.Guid
};
var getGuidResponse = new GetGuidResponse()
{
Guid = request.Guid
};
var mockUserRepository = new Mock<IUserRepository>();
var mockAwsProxy = new Mock<IAwsProxy>();
mockUserRepository.Setup(s => s.UpdateUserAsync(request)).ReturnsAsync(respose);
mockUserRepository.Setup(i => i.GetGuidAsync(getGuidRequest)).ReturnsAsync(getGuidResponse);
var sut = new FromService.UserService(....);
// Act
var response = await sut.UpdateUserAsync(request);
// Assert
Assert.NotNull(response);
Assert.True(response.Success);
}
My problem is when calling - var response = await sut.UpdateUserAsync(request); It goese to service and this GuidResponse is empty so it break after as shows GuidResponse Null:
public async Task<UpdateUserResponse> UpdateUserAsync(UpdateUserRequest request)
{
if (request.EmailWork.HasValue() || request.Role.HasValue())
{
var GuidResponse = await userRepository.GetGuidAsync(new GetGuidRequest
{
Guid = request.Guid
});
// it breaks here because GuidResponse is Null.
if (GuidResponse.Guid != null && request.EmailWork.HasValue())
{
.......
It fails because the setup does not match what was actually given to the mock when the test was exercised.
Use It.Is<T>() to match the passed argument parameter
//...omitted for brevity
mockUserRepository
.Setup(_ => _.GetGuidAsync(It.Is<GetGuidRequest>(x => x.Guid == request.Guid)))
.ReturnsAsync(getGuidResponse);
//...omitted for brevity
assuming the mocked repository is what was injected into the SUT
Reference Moq Quickstart: Matching Arguments
I am new at Just Mock, facing a problem to pass this method, need help to understand the issue.
I have a following code that I want to test
public ActionResult Create(JournalViewModel journal)
{
if (ModelState.IsValid)
{
var newJournal = _mapper.Map<JournalViewModel, Journal>(journal);// Mapper Updates cause syntax change
newJournal.UserId = (int)_membershipService.GetUser().ProviderUserKey;
var opStatus = _journalService.AddJournal(newJournal);
if (!opStatus.Status)
throw new System.Web.Http.HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError));
return RedirectToAction("Index");
}
else
return View(journal);
}
and following code that I have written for testing
[TestMethod()]
public void Create_return_journal()
{
var userMock = Mock.Create<MembershipUser>();
Mock.Arrange(() => userMock.ProviderUserKey).Returns(1);
Mock.Arrange(() => membershipService.GetUser()).Returns(userMock);
var opStatusMock = Mock.Create<OperationStatus>();
opStatusMock.Status = true;
Mock.Arrange(() => journalService.AddJournal(Mock.Create<Journal>())).Returns(opStatusMock);
//Act
PublisherController controller = new PublisherController(journalService, membershipService, mapper);
ViewResult actionResult = (ViewResult)controller.Create(Mock.Create<JournalViewModel>());
var model = actionResult.Model as JournalViewModel;
//Assert
Assert.IsNotNull(model);
}
over here I am expecting opstatus.Status to be true but getting false everytime, which results it to go in
if (!opStatus.Status)
throw new System.Web.Http.HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError));
TIA
Done. just in case if somebody looking for answers.
I forgot to Mock Mapper.
Below is the code that I have used to solve this issue.
[TestMethod()]
public void Create_return_journal()
{
var journalNew = Mock.Create<JournalViewModel>();
var journal = Mock.Create<Journal>();
Mock.Arrange(() => mapper.Map<JournalViewModel, Journal>(journalNew)).Returns(journal);
var userMock = Mock.Create<MembershipUser>();
Mock.Arrange(() => userMock.ProviderUserKey).Returns(1);
Mock.Arrange(() => membershipService.GetUser()).Returns(userMock);
var opStatusMock = Mock.Create<OperationStatus>();
opStatusMock.Status = true;
Mock.Arrange(() => journalService.AddJournal(journal)).Returns(opStatusMock);
//Act
PublisherController controller = new PublisherController(journalService, membershipService, mapper);
RedirectToRouteResult actionResult = (RedirectToRouteResult)controller.Create(journalNew);
//Assert
Assert.IsNotNull(actionResult);
}