I am new to unit testing,
I am working on a minimal API project. and I am testing an endpoint with xunit, moq
endpoint class - ParticipantsGetAll.cs
public class ParticipantsGetAll : IEndpoint<IResult, GetAllParticipantsRequest>
{
const string uri = "api/participants-all";
private ParticipantService? participantService;
public void AddRoute(IEndpointRouteBuilder app)
{
app.MapPost(uri, async ( ParticipantService participantService, [FromBody] GetAllParticipantsRequest query) =>
{
this.participantService = participantService;
return await HandleAsync(query);
})
.Produces<List<ParticipantSummaryModel>>()
.WithTags("Participants")
.WithName("GetAllParticipants");
}
public async Task<IResult> HandleAsync( GetAllParticipantsRequest query)
{
var participants = await participantService!.ListAllAsync(query);
return Results.Ok(participants);
}
I tried to write a unit test test above endpoint class.
CreateParticipantApiTest.cs
[Fact]
public async void ListAllAsyncShouldReturn200Status()
{
var query = new GetAllParticipantsRequest()
{
Name= "",
KeyCordinator="",
RelatedConnection="",
Statuses = null
};
var participant = ParticipantMockData.NewParticipantModel(); // a static class
var sut = new ParticipantsGetAll();
var result = (OkObjectResult)await sut.HandleAsync(query);
result.StatusCode.Should().Be(200);
}
I got below error
Message:
System.NullReferenceException : Object reference not set to an instance of an object.
Stack Trace:
ParticipantsGetAll.HandleAsync(GetAllParticipantsRequest query) line 36
CreateParticipantApiTest.ListAllAsyncShouldReturn200Status() line 67
I have no idea why the object tis null.
Please anyone help me to find the problem.
I am facing this issue for a long time
Thanks
I have no idea why the object tis null.
That is pretty clear, because in your unit test you invoke HandleAsync directly, so the setup which you have moved into MapPost does not happen (compiler was trying to help but was shut down with null-forgiving operator in participantService!.ListAllAsync(query)). Also I'm pretty sure this way of using handlers can lead to some concurrency problems (if ParticipantService is a scoped service). Move ParticipantService participantService to HandleAsync. Something along this lines (not tested):
public class ParticipantsGetAll : IEndpoint<IResult, GetAllParticipantsRequest>
{
const string uri = "api/participants-all";
public void AddRoute(IEndpointRouteBuilder app)
{
app.MapPost(uri, HandleAsync)
.Produces<List<ParticipantSummaryModel>>()
.WithTags("Participants")
.WithName("GetAllParticipants");
}
public async Task<IResult> HandleAsync(ParticipantService participantService, [FromBody] GetAllParticipantsRequest query)
{
var participants = await participantService.ListAllAsync(query);
return Results.Ok(participants);
}
}
And modify the test accrodingly.
Related
tl;dr: I'm having trouble mocking restease**
Also, I realize I may be totally on the wrong track, so any suggestions / nudges in the right direction would be of great help. I am quite new to this.
I'm making a small HTTP Client library, built around RestEase. RestEase is nice and easy to use, but I'm having trouble mocking the calls for the purpose of unit testing.
I want to use moq and NUnit, but I can't properly mock the RestClient. Example (shortened for brevity):
IBrandFolderApi - interface needed by restease to send calls
public interface IBrandFolderApi
{
[Post("services/apilogin")]
Task<LoginResponse> Login([Query] string username, [Query] string password);
}
BrandfolderClient.cs - the main class
public class BrandfolderClient : IBrandfolderClient
{
private IBrandFolderApi _brandFolderApi { get; set; }
public BrandfolderClient(string url)
{
_brandFolderApi = RestClient.For<IBrandFolderApi >(url);
}
public async Task<string> Login(string username, string password)
{
LoginResponse loginResponse = await _brandFolderApi .Login(username, password);
if (loginResponse.LoginSuccess)
{
....
}
....
return loginResponse.LoginSuccess.ToString();
}
}
The unit tests
public class BrandFolderTests
{
BrandfolderClient _brandfolderClient
Mock<IBrandFolderApi> _mockBrandFolderApii;
[SetUp]
public void Setup()
{
//The test will fail here, as I'm passing a real URL and it will try and contact it.
//If I try and send any string, I receive an Invalid URL Format exception.
string url = "https://brandfolder.companyname.io";
_brandfolderClient = new BrandfolderClient (url);
_mockBrandFolderApii= new Mock<IBrandFolderApi>();
}
....
}
So, I don't know how to properly mock the Restclient so it doesn't send an actual request to an actual URL.
The test is failing at the constructor - if I send a valid URL string, then it will send a call to the actual URL. If I send any other string, I get an invalid URL format exception.
I believe I haven't properly implemented something around the rest client, but I'm not sure where. I'm very stuck on this, I've been googling and reading like crazy, but I'm missing something and I don't know what.
So, I don't know how to properly mock the Restclient so it doesn't send an actual request to an actual URL.
You actually should not have any need to mock RestClient.
Refactor your code to depend explicitly on the abstraction you control
public class BrandfolderClient : IBrandfolderClient {
private readonly IBrandFolderApi brandFolderApi;
public BrandfolderClient(IBrandFolderApi brandFolderApi) {
this.brandFolderApi = brandFolderApi; //RestClient.For<IBrandFolderApi >(url);
}
public async Task<string> Login(string username, string password) {
LoginResponse loginResponse = await brandFolderApi.Login(username, password);
if (loginResponse.LoginSuccess) {
//....
}
//....
return loginResponse.LoginSuccess.ToString();
}
}
removing the tight coupling to static 3rd party implementation concerns will allow your subject to be more explicit about what it actually needs to perform its function.
This will also make it easier for the subject to be tested in isolation.
For example:
public class BrandFolderTests {
BrandfolderClient subject;
Mock<IBrandFolderApi> mockBrandFolderApi;
[SetUp]
public void Setup() {
mockBrandFolderApi = new Mock<IBrandFolderApi>();
subject = new BrandfolderClient(mockBrandFolderApi.Object);
}
//....
[Test]
public async Task LoginTest() {
//Arrange
LoginResponse loginResponse = new LoginResponse() {
//...
};
mockBrandFolderApi
.Setup(x => x.Login(It.IsAny<string>(), It.IsAny<string>()))
.ReturnsAsync(loginResponse);
//Act
string response = await subject.Login("username", "password");
//Assert
mockBrandFolderApi.Verify(x => x.Login(It.IsAny<string>(), It.IsAny<string>()), Times.Once);
}
}
In production code, register and configure the IBrandFolderApi abstraction with the container, applying what ever 3rd party dependencies are required
Startup.ConfigureServices
//...
ApiOptions apiOptions = Configuration.GetSection("ApiSettings").Get<ApiOptions>();
services.AddSingleton(apiOptions);
services.AddScoped<IBrandFolderApi>(sp => {
ApiOptions options = sp.GetService<ApiOptions>();
string url = options.Url;
return RestClient.For<IBrandFolderApi>(url);
});
Where ApiOptions is used to store settings
public class ApiOptions {
public string Url {get; set;}
//... any other API specific settings
}
that can be defined in appsetting.json
{
....
"ApiSettings": {
"Url": "https://brandfolder.companyname.io"
}
}
so that they are not hard coded all over you code.
The HttpClient comes from System.Net.Http, which is not easy to mock.
You can, however, create a test HttpClient by passing a fake HttpMessageHandler. Here is an example:
public class FakeHttpMessageHandler : HttpMessageHandler
{
private readonly bool _isSuccessResponse;
public FakeHttpMessageHandler(bool isSuccessResponse = true)
{
_isSuccessResponse = isSuccessResponse;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return Task.FromResult(
new HttpResponseMessage(_isSuccessResponse ? HttpStatusCode.OK : HttpStatusCode.InternalServerError));
}
}
You can create create a test instance of HttpClient as shown below:
var httpClient = new HttpClient(new FakeHttpMessageHandler(true))
{ BaseAddress = new Uri("baseUrl") };
Not sure how you are using verify on _httpClient, its not a mock. but what you are looking for is https://github.com/canton7/RestEase#custom-httpclient. Most people pass in factory for this
//constructor
public httpClientConstructor(string url, IHttpHandlerFactory httpHandler)
{
var httpClient = new HttpClient(httpHandler.GetHandler())
{
BaseAddress = new Uri(url),
};
_exampleApi = RestClient.For<IExampleApi>(url);
}
public interface IHttpHandlerFactory<T>
{
T GetHandler() where T: HttpMessageHandler
}
Thanks Ankit Vijay https://stackoverflow.com/a/68240316/5963888
public class FakeHttpMessageHandler : HttpMessageHandler
{
private readonly bool _isSuccessResponse;
public FakeHttpMessageHandler(bool isSuccessResponse = true)
{
_isSuccessResponse = isSuccessResponse;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return Task.FromResult(
new HttpResponseMessage(_isSuccessResponse ? HttpStatusCode.OK : HttpStatusCode.InternalServerError));
}
}
[SetUp]
public void Setup()
{
var fakeHandler = new Mock<IHttpHandlerFactory>();
fakeHandler.Setup(e => e.GetHandler() ).Returns( new FakeHttpHandler() );
_httpClient = new HttpClient(fakeHandler.Object);
_exampleApi = new Mock<IExampleApi>();
}
I've created a custom library which automatically sets up Polly policies for specific services which depend on HttpClient.
This is done using the IServiceCollection extension methods and the typed client approach. A simplified example:
public static IHttpClientBuilder SetUpFooServiceHttpClient(this IServiceCollection services)
{
return services
.AddHttpClient<FooService>()
.AddPolicyHandler(GetRetryPolicy());
}
public class FooService
{
private readonly HttpClient _client;
public FooService(HttpClient httpClient)
{
_client = httpClient;
}
public void DoJob()
{
var test = _client.GetAsync("http://example.com");
}
}
Note that my real code uses a generic type and an options builder, but I've omitted that part to keep it simple. The purpose of my tests is to confirm that my options builder correctly applies the policies that I want it to apply. For the sake of example here, let's just assume that it's a hardcoded retry policy which I want to test.
I now want to test if this library correctly registers the Polly policies to my injected HttpClient dependencies.
Note
There are many answers to be found online and on StackOverflow where the suggestion is to construct the HttpClient yourself, i.e.: new HttpClient(new MyMockedHandler());, but this defeats my purpose of needing to test if the actual IHttpClientFactory is constructing httpclients with the requested policies.
To that end, I want to test with a real HttpClient which was generated by a real IHttpClientFactory, but I want its handler to be mocked so I can avoid making actual web requests and artificially cause bad responses.
I'm using AddHttpMessageHandler() to inject a mocked handler, but the factory seems to be ignoring that.
Here's my test fixture:
public class BrokenDelegatingHandler : DelegatingHandler
{
public int SendAsyncCount = 0;
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
SendAsyncCount++;
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.InternalServerError));
}
}
private BrokenDelegatingHandler _brokenHandler = new BrokenDelegatingHandler();
private FooService GetService()
{
var services = new ServiceCollection();
services.AddTransient<BrokenDelegatingHandler>();
var httpClientBuilder = services.SetUpFooServiceHttpClient();
httpClientBuilder.AddHttpMessageHandler(() => _brokenHandler);
services.AddSingleton<FooService>();
return services
.BuildServiceProvider()
.GetRequiredService<FooService>();
}
And here's my test:
[Fact]
public void Retries_client_connection()
{
int retryCount = 3;
var service = GetService();
_brokenHandler.SendAsyncCount.Should().Be(0); // PASS
var result = service.DoJob();
_brokenHandler.SendAsyncCount.Should().Be(retryCount); // FAIL: expected 3 but got 0
}
When I debug the test, the handler's breakpoint is never hit, and the response comes back as a 200 (because it actually connected to the URL, instead of hitting the mocked handler).
Why is my mocked handler being ignored by the http client factory?
Note that I will also accept any answer that allows me to test the policies in another valid way.
I'm aware I can just use a broken URL string but I'm going to need to test specific http responses in my tests.
We had a similar problem few months ago. How to test that the injected HttpClient is decorated with the correct Policies. (We have used a Retry > CircuitBreaker > Timeout policy chain).
We ended up to create several integration tests. We have used WireMock.NET to create a server stub. So, the whole point of this was to let the ASP.NET DI do its magic and then scrutinize the stub's logs.
We have created two base classes which wrapped the WireMock setup (we had a POST endpoint).
FlawlessServer
internal abstract class FlawlessServiceMockBase
{
protected readonly WireMockServer server;
private readonly string route;
protected FlawlessServiceMockBase(WireMockServer server, string route)
{
this.server = server;
this.route = route;
}
public virtual void SetupMockForSuccessResponse(IResponseBuilder expectedResponse = null,
HttpStatusCode expectedStatusCode = HttpStatusCode.OK)
{
server.Reset();
var endpointSetup = Request.Create().WithPath(route).UsingPost();
var responseSetup = expectedResponse ?? Response.Create().WithStatusCode(expectedStatusCode);
server.Given(endpointSetup).RespondWith(responseSetup);
}
}
FaultyServer
(We have used scenarios to simulate timeouts)
internal abstract class FaultyServiceMockBase
{
protected readonly WireMockServer server;
protected readonly IRequestBuilder endpointSetup;
protected readonly string scenario;
protected FaultyServiceMockBase(WireMockServer server, string route)
{
this.server = server;
this.endpointSetup = Request.Create().WithPath(route).UsingPost();
this.scenario = $"polly-setup-test_{this.GetType().Name}";
}
public virtual void SetupMockForFailedResponse(IResponseBuilder expectedResponse = null,
HttpStatusCode expectedStatusCode = HttpStatusCode.InternalServerError)
{
server.Reset();
var responseSetup = expectedResponse ?? Response.Create().WithStatusCode(expectedStatusCode);
server.Given(endpointSetup).RespondWith(responseSetup);
}
public virtual void SetupMockForSlowResponse(ResilienceSettings settings, string expectedResponse = null)
{
server.Reset();
int higherDelayThanTimeout = settings.HttpRequestTimeoutInMilliseconds + 500;
server
.Given(endpointSetup)
.InScenario(scenario)
//NOTE: There is no WhenStateIs
.WillSetStateTo(1)
.WithTitle(Common.Constants.Stages.Begin)
.RespondWith(DelayResponse(higherDelayThanTimeout, expectedResponse));
for (var i = 1; i < settings.HttpRequestRetryCount; i++)
{
server
.Given(endpointSetup)
.InScenario(scenario)
.WhenStateIs(i)
.WillSetStateTo(i + 1)
.WithTitle($"{Common.Constants.Stages.RetryAttempt} #{i}")
.RespondWith(DelayResponse(higherDelayThanTimeout, expectedResponse));
}
server
.Given(endpointSetup)
.InScenario(scenario)
.WhenStateIs(settings.HttpRequestRetryCount)
//NOTE: There is no WillSetStateTo
.WithTitle(Common.Constants.Stages.End)
.RespondWith(DelayResponse(1, expectedResponse));
}
private static IResponseBuilder DelayResponse(int delay) => Response.Create()
.WithDelay(delay)
.WithStatusCode(200);
private static IResponseBuilder DelayResponse(int delay, string response) =>
response == null
? DelayResponse(delay)
: DelayResponse(delay).WithBody(response);
}
Simple test for Slow processing
(proxyApiInitializer is a instance of a WebApplicationFactory<Startup> derived class)
[Fact]
public async Task GivenAValidInout_AndAServiceWithSlowProcessing_WhenICallXYZ_ThenItCallsTheServiceSeveralTimes_AndFinallySucceed()
{
//Arrange - Proxy request
HttpClient proxyApiClient = proxyApiInitializer.CreateClient();
var input = new ValidInput();
//Arrange - Service
var xyzSvc = new FaultyXYZServiceMock(xyzServer.Value);
xyzSvc.SetupMockForSlowResponse(resilienceSettings);
//Act
var actualResult = await CallXYZAsync(proxyApiClient, input);
//Assert - Response
const HttpStatusCode expectedStatusCode = HttpStatusCode.OK;
actualResult.StatusCode.ShouldBe(expectedStatusCode);
//Assert - Resilience Policy
var logsEntries = xyzServer.Value.FindLogEntries(
Request.Create().WithPath(Common.Constants.Routes.XYZService).UsingPost());
logsEntries.Last().MappingTitle.ShouldBe(Common.Constants.Stages.End);
}
XYZ Server init:
private static Lazy<WireMockServer> xyzServer;
public ctor()
{
xyzServer = xyzServer ?? InitMockServer(API.Constants.EndpointConstants.XYZServiceApi);
}
private Lazy<WireMockServer> InitMockServer(string lookupKey)
{
string baseUrl = proxyApiInitializer.Configuration.GetValue<string>(lookupKey);
return new Lazy<WireMockServer>(
WireMockServer.Start(new FluentMockServerSettings { Urls = new[] { baseUrl } }));
}
I hope this can help you.
I am testing this method that in the end does mapping to my DTO
public async Task<IActionResult> Get()
{
var currencies = await _repository.GetCurrencies().ToListAsync().ConfigureAwait(false);
if (currencies.Count == 0)
return NoContent();
return Ok(currencies.ToDto());
}
In return of this method I get the following error:
Message: System.InvalidOperationException : Mapper not initialized.
Call Initialize with appropriate configuration. If you are trying to
use mapper instances through a container or otherwise, make sure you
do not have any calls to the static Mapper.Map methods, and if you're
using ProjectTo or UseAsDataSource extension methods, make sure you
pass in the appropriate IConfigurationProvider instance.
I use a static method to do auto mapper
public static List<CurrencyDTO> ToDto(this List<Currency> model)
{
return Mapper.Map<List<Currency>, List<CurrencyDTO>>(model);
}
When I run only this test it passes but when I squeeze everyone it speaks
I tried already make a builder initialize mapper but continue error, test only passes if it is run individually
public class CurrenciesControllerTest
{
public CurrenciesControllerTest()
{
AutoMapperConfig.RegisterMappings();
}
private Mock<IReimbursementRepository> _repository = new Mock<IReimbursementRepository>();
[Fact]
public async Task Should_return_all_currencies()
{
var mock = Currencyfactory().AsQueryable().BuildMock();
_repository.Setup(x => x.GetCurrencies()).Returns(mock.Object);
var controller = new CurrenciesController(_repository.Object);
var response = await controller.Get().ConfigureAwait(false) as OkObjectResult;`enter code here`
Assert.Equal(response.StatusCode, (int)HttpStatusCode.OK);
}
}
my solution, configure AutoMapperConfig
public class AutoMapperConfig
{
public static object thisLock = new object();
public static void Initialize()
{
lock (thisLock)
{
AutoMapper.Mapper.Reset();
AutoMapper.Mapper.Initialize(cfg => { });
}
}
}
}
I have the following constructor with a get () method and I need to test if this method works correctly through NUnit.
public ConsumersController(IProvider<Consumer> provider)
{
this.provider = provider;
}
/// <summary>
/// Get the list of consumers that you are allowed to see and serve
/// </summary>
/// <response code="200">Returns the consumers you are allowed to see and serve</response>
[HttpGet]
[ProducesResponseType(typeof(IEnumerable<Consumer>), 200)]
[Produces(typeof(IEnumerable<Consumer>))]
[SwaggerResponse((int)HttpStatusCode.OK, Type = typeof(IEnumerable<Consumer>))]
public IEnumerable<Consumer> Get()
{
return this.provider.GetAll();
}
I created this code based on this link Testing controller logic in ASP.NET Core but I do not know if it is correct, the test does not pass.
Thanks in advance.
[TestFixture]
public class ConsumerTest
{
private readonly HttpClient consumerHttp;
private readonly ConsumersController consumerControl;
[Test]
public async Task Get_ConsumersController()
{
var result = this.consumerControl.Get();
var response = await consumerHttp.GetAsync("http://localhost:9000/consumers");
response.EnsureSuccessStatusCode();
var consumerList = JsonConvert.DeserializeObject<IEnumerable<Consumer>>(await response.Content.ReadAsStringAsync());
var consumerTest = consumerList.First();
Assert.AreEqual(result.First().Name, consumerTest.Name);
}
}
Thanks for the answers. In fact it lacked to use the MOQ library and there was an error with the use of Async. I got it with this code.
[Test]
public void Get_ReturnsAConsumer_WithAListOfIProvider()
{
var mockRepo = new Mock<IProvider<Consumer>>();
mockRepo.Setup(repo => repo.GetAll()).Returns(GetConsumers());
var controller = new ConsumersController(mockRepo.Object);
var result = controller.Get();
Assert.IsAssignableFrom<List<Consumer>>(result);
Assert.AreEqual(1, result.Count());
}
I want to test WebMethod of some Web Service (asmx). Suppose I have the following code:
public IUsersRepository UsersRepository
{
get { return Session["repository"] as IUsersRepository; }
set { Session["repository"] = value; }
}
[WebMethod(EnableSession = true)]
public int AddUser(string userName, int something)
{
var usersRepository = Session["repository"] as IUsersRepository;
return usersRepository.AddUser(userName, something);
}
and the corresponding unit test (just to test that the repository is called at all):
[Test]
public void add_user_adds_user()
{
// Arrange
var repository = new Mock<IUsersRepository>();
var service = new ProteinTrackingService { UsersRepository = repository.Object };
// Act
var userName = "Tester";
var something = 42;
service.AddUser(userName: userName, something: something);
// Assert
repository.Verify(r => r.AddUser(
It.Is<string>(n => n.Equals(userName)),
It.Is<int>(p => p.Equals(something))));
}
When I run this test, I receive the following error message:
System.InvalidOperationException : HttpContext is not available.
This class can only be used in the context of an ASP.NET request.
What shall I do to make this test working?
Have you had a look at this one? Setting HttpContext.Current.Session in a unit test Apparently should can do that trick to simulate your session.
On regards to your assert, you can directly do:
// Assert
repository.Verify(r => r.AddUser(userName, something));
And that will assert you are calling that method with these parameters.
Hope this helps!