How to test in xunit complex method - c#

How can I test in asp.net core 2.0 following method which exists in separate project than my test project? for example like this:
public partial class LoanRequestServiceController : BaseServiceController
{
public ServiceDTO<AP_CBO> AddCBO(AP_CBO cbo)
{
ServiceDTO<AP_CBO> dto = new ServiceDTO<AP_CBO>();
try
{
using (var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.Snapshot }))
{
cbo.ID_CBO = 333;
dto.Data = cbo;
scope.Complete();
}
}
catch (Exception ex)
{
dto.Error = new ServiceError(ex);
Globals.Logger.Error(ex);
}
finally
{
//Globals.CastleComponentsContainer.Release(LoanRequestDAL);
}
return dto;
}
}
I tested some "light" methods such as if service method returns SucessCode and it works.
Here is my test class:
[Theory]
[InlineData("/Sample/AddCBO")]
public async Task Test_AddCBO(string url)
{
//Arrange
var client = _factory.CreateClient();
//Act
var response = await client.GetAsync(url);
//Assert
response.EnsureSuccessStatusCode();
//Compare two dto objects AP_CBO
//object expected = new AP_CBO { properties... }
// object responseObject = response.Content...
//Assert.Equal(expected, responseObject);
}
I don't know how to test an object with muliple properties.
Maybe I need to use Moq? Theoretically, this method would be go to the DAL (DatabaseAccess Layer) and return from database packed object and returns to the api, or in my case back into test.

First off, you have to decide which level of tests you want to write.
If you're writing a Unit test, you should mock any and all external integrations (in your case I can identify HTTP request -> Controller and Controller -> Database). This is the foundation of your functional testing. So if you're writing unit tests, yes, you should use a mocking framework such as NSubstitute or Moq (and only test your method's behavior by calling it).
The test sample you posted looks to me like an integration test since you're including the integration HTTP request -> Controller. In this case I would seed the database with data (if relevant) and actually call your API endpoint (as you're already doing).
To check the content (DTO) of the response in ASP.Net Core you have to do the following:
// ...
var response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
var content = await httpResponseMessage.Content.ReadAsStringAsync();
var serviceDto = JsonConvert.DeserializeObject<ServiceDTO<AP_CBO>>(content); // Only for Json
// Validate serviceDto

It is pretty long topic for detailed explanation here ; i think it will be better if you follow a sample and read the details.
I assume that you are going to write unit test; for unit test i can recommend this tutorial that may help you . check this please

Related

Mocking Authentication in .Net with IRequestExecutor

Trying to write some integration tests for the first time in .NET - specifically for HotChocolate.
I’ve got had tests working with the WebApplicationFactory, the final part left is trying to now mock the authentication. I tried to set it up based on Mock Authentication but I’m querying the data with IRequestExecutor and finding the de-facto way of setting it up doesn't actually fire:
[Fact]
public async Task GetJokes()
{
var query =
#"query Jokes {
jokes(jokeLength: SMALL) {
nodes {
id
body
}
}
}";
var request = QueryRequestBuilder.New().SetQuery(query).Create();
var executor = await _factory.Services.GetRequestExecutorAsync();
var result = await executor.ExecuteAsync(request);
(await result.ToJsonAsync()).MatchSnapshot();
}
When I debug this I never hit HandleAuthenticateAsync in my AuthHandler’s (setup from the Mock Authentication linked above), so the claims are never added - so looks like those only run for the httpClient requests - is there a way to configure the IRequestExecutor to be authenticated too?
You can add the ClaimsPrincipal directly as property to the QueryRequestBuilder.
var request = QueryRequestBuilder
.New()
.SetQuery(query)
.AddProperty(nameof(ClaimsPrincipal), new ClaimsPrincipal(new ClaimsIdentity("test")))
.Create();

ASP .NET Core Integration Tests fails with corrupted body

I'm working on a project where we have to develop a web API with ASP .NET Core 3.x. So far, so good, it is running well. Now, I'm writing some integration tests for this web API and I have some trouble to get the tests for everything else than GET request to work.
We're using the Clean Architecture from Jason Taylor. This means we have a core project with all our request handler, a domain project with all database entities and a presentation project for the API controllers. We use MediatR and dependency injection for the communication between these projects.
Now, we have the problem that the body data of the reuqest doesn't reach the controller.
This is how the Update method in the controller looks like:
[ApiController]
[Route("api/[controller]/[action]")]
public abstract class BaseController : ControllerBase
{
private IMediator _mediator;
protected IMediator Mediator => _mediator ??= HttpContext.RequestServices.GetService<IMediator>();
}
public class FavoriteController : BaseController
{
[HttpPut("{id}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> Update(long id, UpdateFavoriteCommand command)
{
if (command == null || id != command.Id)
{
return BadRequest();
}
// Sends the request to the corresponding IRequestHandler
await Mediator.Send(command);
return NoContent();
}
}
We use xUnit.net as test framework.
For the integration tests, we're using an InMemory SQLite database which is setup in a fixture class.
The test looks like the following:
public class UpdateFavoritesTestSqlite : IClassFixture<WebApplicationFactoryWithInMemorySqlite<Startup>>
{
private readonly WebApplicationFactoryWithInMemorySqlite<Startup> _factory;
private readonly string _endpoint;
public UpdateFavoritesTestSqlite(WebApplicationFactoryWithInMemorySqlite<Startup> factory)
{
_factory = factory;
_endpoint = "api/Favorite/Update/";
}
[Fact]
public async Task UpdateFavoriteDetail_WithFullUpdate_ShouldUpdateCorrectly()
{
// Arange
var client = _factory.CreateClient(); // WebApplicationFactory.CreateClient()
var command = new UpdateFavoriteCommand
{
Id = 5,
Caption = "caption new",
FavoriteName = "a new name",
Public = true
};
// Convert to JSON
var jsonString = JsonConvert.SerializeObject(command);
var httpContent = new StringContent(jsonString, Encoding.UTF8, "application/json");
var stringUri = client.BaseAddress + _endpoint + command.Id;
var uri = new Uri(stringUri);
// Act
var response = await client.PutAsync(uri, httpContent);
response.EnsureSuccessStatusCode();
httpContent.Dispose();
// Assert
response.StatusCode.ShouldBe(HttpStatusCode.NoContent);
}
}
If we run the test, we get an 400 Bad Request error.
If we run the test in Debug mode, we can see that the code throws a custom ValidationException because of a model state error. This is configured in the DependencyInjection of the presentation project:
services
.AddControllers()
.ConfigureApiBehaviorOptions(options =>
{
options.InvalidModelStateResponseFactory = context =>
{
var failures = context.ModelState.Keys
.Where(k => ModelValidationState.Invalid.Equals(context.ModelState[k].ValidationState))
.ToDictionary(k => k, k => (IEnumerable<string>)context.ModelState[k].Errors.Select(e => e.ErrorMessage).ToList());
throw new ValidationException(failures);
};
})
.AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<IWebApiDbContext>());
The failures object contains one error which says:
The input does not contain any JSON tokens. Expected the input to start with a valid JSON token, when isFinalBlock is true. Path: $ | LineNumber: 0 | BytePositionInLine: 0.
Here is a screenshot: Visual Studio in Debugging mode with json error.
In one article in stackoverflow I've read, that removing the [ApiController] class attribute can result in a more detailed error description. During debugging again the test and setting a breakpoint int the Update method from the FavoriteController at the line with await Mediator.Send(command);, I was able to see, that the command object arriving the Update method contains only null or default values, except the id, which was 5.
command
Caption null string
FavoriteName null string
Id 5 long
Public false bool
The most confusing (and frustrating) part is, that a manual test with swagger or postman are both successfull. The way I understand it, there has to be a problem during the integration test.
I hope, someone can help me and see what I'm missing. Could it be possible that there is something wrong with the HttpClient of the Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory?
We've found the problem in our LoggingMiddleware of the web api presentation project.
Before writing this question, we already hat a look at another article on stackoverflow:
ASP.NET MVC Core 3.0 - Why API Request from body keeps returning !ModelState.IsValid?
But we already had the request.Body.Seek(0, SeekOrigin.Begin); in our code. So, we thought that's it and it couldn't be the problem.
But now, we've found this article:
.net core 3.0 logging middleware by pipereader
Instead of reading the request body like this:
await request.Body.ReadAsync(buffer, 0, buffer.Length);
... where the stream is closed after read, we're using now the BodyReader as a stream and leave the stream open:
var stream = request.BodyReader.AsStream(true); // AsStream(true) to let stream open
await stream.ReadAsync(buffer, 0, buffer.Length);
request.Body.Seek(0, SeekOrigin.Begin);

How to mock httpclient

I am new in TDD can you please write test case using moq for following code -
public async Task<Model> GetAssetDeliveryRecordForId(string id)
{
var response = await client.GetAsync($"api/getdata?id={id}");
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadAsAsync<Model>();
return result;
}
Thanks in advance.
You can use Moq to mock it.
Using Moq to do that for you
Now, you probably don’t want to create a new class for each response.
You could write a helper class for tests which you can prime with
whatever response you might want, but that is probably not flexible
enough.
Moq is a popular .NET library that helps mocking objects for testing.
In essence it uses reflection and expressions to dynamically generate
the mocks at runtime during your tests, based on specifications you
declare using a fluent API.
Now, there is a slight issue here, too. As you noticed, the SendAsync
method on the abstract HttpMessageHandler class is protected. The
caveat: Moq can’t auto-implement protected methods as easy as it does
with interfaces or public methods. Reason being, that the fluent API
uses expressions on the mocked Type, and this can’t offer private or
protected members, as you access the class from the outside here. So,
we have to use some more advanced features of Moq to mock out our
protected method here.
Moq, therefore, has an API for that. You do use Moq. Protected; in your
using clauses, and then you can go on on your Moq with the
.Protected() method. This gives you some additional methods on the
Moq, where you can access the protected members using their names.
A complete test of a class using a HttpClient using Moq would look
like this:
// ARRANGE
var handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
handlerMock
.Protected()
// Setup the PROTECTED method to mock
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
)
// prepare the expected response of the mocked http call
.ReturnsAsync(new HttpResponseMessage()
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("[{'id':1,'value':'1'}]"),
})
.Verifiable();
// use real http client with mocked handler here
var httpClient = new HttpClient(handlerMock.Object)
{
BaseAddress = new Uri("http://test.com/"),
};
var subjectUnderTest = new MyTestClass(httpClient);
// ACT
var result = await subjectUnderTest
.GetSomethingRemoteAsync('api/test/whatever');
// ASSERT
result.Should().NotBeNull(); // this is fluent assertions here...
result.Id.Should().Be(1);
// also check the 'http' call was like we expected it
var expectedUri = new Uri("http://test.com/api/test/whatever");
handlerMock.Protected().Verify(
"SendAsync",
Times.Exactly(1), // we expected a single external request
ItExpr.Is<HttpRequestMessage>(req =>
req.Method == HttpMethod.Get // we expected a GET request
&& req.RequestUri == expectedUri // to this uri
),
ItExpr.IsAny<CancellationToken>()
);
For unit tests, you don’t mock HttpClient. Instead, you mock
HttpMessageHandler, put that into an HttpClient and have it return
whatever you want that way. If you don’t want to create s specific
derivation of HttpMessageHandler for each test, you can also have Moq
create the mocks for you automatically.
Read the whole article here.

How do you access the data returned by a controller when run in unit tests?

I have a Asp .net core 2.2 web api. I am trying to set up unit testing on a number of the controllers. I have it working to he point where it does call the controller
Controller
[HttpGet("list")]
public ActionResult Get([FromQuery] string q = null)
{
if (q != null)
return Ok(new ClientListResponse(ClientHelper.FindClientByClientIdOrClientName(_configurationDbContext.Clients, q)));
return Ok(new ClientListResponse(_configurationDbContext.Clients.ToList()));
}
Test calling it
var allClients = controller.Get();
Issue
Now when i look at it in debug everything looks fine. I can see that value contains the correct object being returned.
However when it try to access the variables they don't exist.
I am suspecting that this has something to do with the response being an ActionResult. I have tried to convert it to everything i can think of.
How do you access the data returned by a controler when run in unit tests?
Cast it to an OkObjectResult.
In ours:
var controller = SetupAdminTestsForPassword();
var result = controller.ValidateTokenForUserCreation(new ValidateUserTokenRequest() { token = "VALIDCREATEUSERTOKEN"});
var okResult = result as OkObjectResult;
// assert
Assert.IsNotNull(okResult);
Assert.IsNotNull(okResult.Value);
Assert.IsNotNull(okResult.Value as ValidateUserTokenResponse);
Assert.IsTrue((okResult.Value as ValidateUserTokenResponse).IsValid);
We're also using dotnet core 2.2 and Moq for tests.

Boundary test with Raven Db

I am building a system using Web API and Raven DB.
I am writing integration tests against the external boundary of this system.
public void GetAfterPostingPollReturnsPoll()
{
using (var client = HttpClientFactory.Create())
{
var poll = new
{
Question = "What is the answer?",
Options = new[] { "Yes", "No", "Maybe" }
};
var postResponse = client.PostAsJsonAsync("", poll).Result;
var pollLocation = postResponse.Headers.Location;
var getResponse = client.GetAsync(pollLocation).Result;
var actual = JsonConvert.DeserializeObject<Poll>(
getResponse.Content
.ReadAsStringAsync()
.Result);
Assert.Equal(poll.Question, actual.Question);
Assert.Equal(poll.Options, actual.Options);
}
}
When I submit an entry, the Controller interacts with a DocumentStore because that is how it works in production.
The trouble I am having is that the data produced in the test is never cleaned up.
Based on what I have been reading, I should use the EmbeddableDocumentStore for the purposes of my acceptance tests.
How might I use the DocumentStore normally but the EmbeddableDocumentStore when executing boundary tests like this one?
How do you "interact with DocumentStore" in your controller? The controller really only need to "interact" with the IDocumentSession that could be injected by the WebAPI infrastructure and in your integration test you register IDocumentStore to be implemented by EmbeddableDocumentStore (providing you use some kind of IoC container).

Categories

Resources