UnitTest HttpResponse WriteAsync and CopyToAsync - c#

I would like to unit test the next method:
public static async Task SetResponseBody(HttpResponse response, string message)
{
var originalResponseBody = response.Body;
var responseBody = new MemoryStream();
response.Body = responseBody;
response.ContentType = "application/json";
dynamic body = new { Message = message };
string json = JsonSerializer.Serialize(body);
await response.WriteAsync(json);
response.Body.Seek(0, SeekOrigin.Begin);
await responseBody.CopyToAsync(originalResponseBody);
}
The last two lines are used from this post.
The current unit test implementation is:
[TestMethod]
public async Task SetResponseBody_TestMessageAsync()
{
var expected = "TestMessage";
string actual = null;
var responseMock = new Mock<HttpResponse>();
responseMock
.Setup(_ => _.Body.WriteAsync(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
.Callback((byte[] data, int offset, int length, CancellationToken token) =>
{
if (length > 0)
actual = Encoding.UTF8.GetString(data);
})
.Returns(Task.CompletedTask);
await ResponseRewriter.SetResponseBody(responseMock.Object, expected);
}
The unit tests fails due to a NullReferenceException which is raised once the test hits the 'await response.WriteAsync(json);' line of code. Could you point me in the right direction in order to fix this exception, so the test will pass?
Summarized: The unit tests needs to check if the given 'TestMessage' is actually written to the Body of the response.
Background information:
I'm calling the SetResponseBody method in order to modify the response body as soon as the 'OnRedirectToIdentityProvider' event is raised from AddOpenIdConnect.
OnRedirectToIdentityProvider = async e =>
{
// e is of type RedirectContext
if (e.Request.Path.StartsWithSegments("/api")))
{
if (e.Response.StatusCode == (int)HttpStatusCode.OK)
{
e.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
// TestMessage is a const
// e.Response is readonly (get) so it's not possible to set it directly.
await ResponseRewriter.SetResponseBody(e.Response, TestMessage);
}
e.HandleResponse();
}
await Task.CompletedTask;
}
.NET Core 3.1, WebApi, OpenId

There are too many internals that need to be configured for the abstract HttpResponse to work as intended when mocking it.
I would suggest using DefaultHttpContext and extracting the default response created within that context.
[TestMethod]
public async Task SetResponseBody_TestMessageAsync() {
//Arrange
string expected = "TestMessage";
string actual = null;
HttpContext httpContext = new DefaultHttpContext();
HttpResponse response = httpContext.Response
//Act
await ResponseRewriter.SetResponseBody(response, expected);
//Assert
//...
}
for the assertion, extract the content of the response body and assert its expected behavior.

We can't write unit test for all method especially system library.
so solution is make virtual function and use below method instead of direct await response.WriteAsync(json);
public virtual async Task WriteAsync(string json)
{
await response.WriteAsync(json);
}
and then
yourClassObject_Where_SetResponseBody_Exist.Setup(m => m.WriteAsync
(It.IsAny<string>()));

Related

Using Xunit in .Net core web API - How to test/ pass 500 status code

We are trying the Test Driven Development for our .Net core 3.1 Web API and using XUnit for the same.
In my controller, I have test cases or [Fact] written for the status codes 200,404 and other scenarios.
When it comes to unit testing 500 internal server error - I am not sure how to pass the code, or how to assert it.
It always fails for me by using the below code.
How to successfully test the 500 - Internal server error in my unit test?
I am very new to TDD any comment is helpful, below is the code :
[Fact] // 404 Sample unit test method
public async Task Get_OnNotFound_Returns404()
{
// Arrange
var mockService = new Mock<IService>();
mockService.Setup(service => service.GetTypes(It.IsAny<string>(),
It.IsAny<CancellationToken>())).ReturnsAsync(new List<TypeResponse>());
var sut = new HomeController(mockService.Object);
//Act
var result = await sut.GetSampleMethod("foo") as ObjectResult;
//Assert
Assert.Equal(StatusCodes.Status404NotFound, result.StatusCode);
}
// Below is my always failing 500 code unit test
[Fact]
public async Task Returns_500_When_Returns_Error()
{
// Arrange
var mockService = new Mock<IService>();
mockService.Setup(service => service.GetTypes(It.IsAny<string>(),
It.IsAny<CancellationToken>())).ReturnsAsync(new List<TypeResponse>());
var sut = new HomeController(mockService.Object);
//Act
var result = await GetSampleMethod("foo") as ObjectResult;
//Assert
Assert.Equal(StatusCodes.Status500InternalServerError, result.StatusCode);
}
Below is my controller method :
[HttpGet]
public async Task<IActionResult> GetSampleMethod(string id,
CancellationToken cancellationToken)
{
try
{
var result = await _service.GetSampleMethod(id, cancellationToken);
if (result.Any())
{
return Ok(result);
}
return StatusCode(StatusCodes.Status404NotFound,
new Error { Message = "No Records ." });
}
catch (Exception ex)
{
return StatusCode(StatusCodes.Status500InternalServerError,
new Error
{
Message = !string.IsNullOrWhiteSpace(ex.Message)
? ex.Message : "An unhandled error occurred."
});
}
}
It's clear that your method returns Status500InternalServerError in case of an exception. So you have to mock an exception for it:
mockService.Setup(service => service.GetTypes(It.IsAny<string>(),
It.IsAny<CancellationToken>()))
.Throws(new Exception());

Unit test that checks Web API 2 Controller takes forever to Request.Content.ReadAsStringAsync()

EDIT: I've added async/await keywords to no avail.
When running the following unit test,
private const string jsonRequest = #"
[
{
""productId"": ""279"",
""price"": ""20.00"",
}
]";
[TestMethod]
public async Task GivenAPostedJsonPayload_ThenCheckIfDataIsBeingSavedOnDatabase()
{
var controller = new MyBelovedController();
var message = new HttpRequestMessage();
var content = new StringContent(jsonRequest);
message.Content = content;
message.Method = HttpMethod.Post;
controller.Request = message;
var response = await controller.PostIncrementalChange();
}
with the following MyBelovedController,
[HttpPost]
public async Task<IHttpActionResult> PostIncrementalChange()
{
string jsonRequest = await Request.Content.ReadAsStringAsync(); // Debugger gets stuck here.
/* JSON deserialization and database processing take place here. */
return Ok();
}
the debugger gets stuck at the aforementioned line and takes forever to step to the next line.
Question: is there any way to make it run faster?
Specs:
My Visual Studio 2019 Professional build version is 16.8.5.
Microsoft .NET Framework version 4.8.04084.
Microsoft Windows 10 Version 2004 build version 19041.867.
EDIT: my laptop is hitting 100% CPU usage while I debug the solution, might it be the cause?
Try it this way to use async pattern. Mark your methods as async and return Task. Await the async call. Remove .Result so it is non-blocking.
[TestMethod]
public async Task GivenAPostedJsonPayload_ThenCheckIfDataIsBeingSavedOnDatabase()
{
blah, blah, blah...
var response = await controller.PostIncrementalChange();
Assert.AreEqual(System.Net.HttpStatusCode.OK, response.StatusCode);
}
[HttpPost]
public async Task PostIncrementalChange()
{
string jsonRequest = await Request.Content.ReadAsStringAsync();
}

Moq fails to throw exception in Unit Test

I have an Azure function that stores applications settings in Azure Blob Storage. To unit test the class that gets and adds the settings, I use moq to have the blob storage abstraction class (blobStorageRepository) throw an exception. It mostly works. however, I have two tests that fail.
I Have other unit tests that mocks the _blobStorageRepository. All work just fine including the test against the "Get" method correctly throws the exception but the "Add" exception tests fail. I've included the actual test below
Fact(DisplayName = "AddUserSettingsAsync - InvalidOperationException")]
[Trait("Category", "Unit Test")]
public async Task SettingsStoreAddUserSettingsTestWithException()
{
string userObject = Guid.NewGuid().ToString();
string correlationId = Guid.NewGuid().ToString();
string body = File.ReadAllText("TestData/userSettings.json");
UserSettingsObject userSettingsObject = JsonConvert.DeserializeObject<UserSettingsObject>(body);
var iFunctionEnvironment = TestHelpers.GetEnvironmentVariable("Test");
Uri.TryCreate("http://localhost", UriKind.Absolute, out Uri uri);
var iblobStorageRepositoryMoq = new Mock<IBlobStorageRepository>();
iblobStorageRepositoryMoq
.Setup(mock => mock.Add(logger, correlationId, body, userObject))
.ThrowsAsync(new Exception("Function Add threw an exception"));
var iblobStorageRepository = iblobStorageRepositoryMoq.Object;
SettingsStore settingsStore = new SettingsStore(iFunctionEnvironment, iblobStorageRepository);
Exception exception = await Assert.ThrowsAsync<InvalidOperationException>(async () => await settingsStore.AddUserSettingsAsync(logger, correlationId, userSettingsObject, userObject));
Assert.Equal("Function Add threw an exception", exception.Message);
Assert.Null(exception.InnerException);
}
Here's the interface for the blogStoreRepository:
Task<bool> Add(ILogger logger, string correlationId, string settingsObject, string settingsObjectName);
Any help would be greatly appreciated!
If an invoked mock does not behave as expected, most times it is because the setup did not match what was actually invoked.
Consider loosening the expectation using It.IsAny<T>()
Fact(DisplayName = "AddUserSettingsAsync - InvalidOperationException")]
[Trait("Category", "Unit Test")]
public async Task SettingsStoreAddUserSettingsTestWithException() {
//Arrange
string userObject = Guid.NewGuid().ToString();
string correlationId = Guid.NewGuid().ToString();
string body = File.ReadAllText("TestData/userSettings.json");
UserSettingsObject userSettingsObject = JsonConvert.DeserializeObject<UserSettingsObject>(body);
var iFunctionEnvironment = TestHelpers.GetEnvironmentVariable("Test");
Uri.TryCreate("http://localhost", UriKind.Absolute, out Uri uri);
var iblobStorageRepositoryMoq = new Mock<IBlobStorageRepository>();
iblobStorageRepositoryMoq
.Setup(mock => mock.Add(It.IsAny<ILogger>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
.ThrowsAsync(new InvalidOperationException("Function Add threw an exception"));
//The SUT
var subjectUnderTest = new SettingsStore(iFunctionEnvironment, iblobStorageRepositoryMoq.Object);
//Act
InvalidOperationException exception = await Assert.ThrowsAsync<InvalidOperationException>(() => subjectUnderTest.AddUserSettingsAsync(logger, correlationId, userSettingsObject, userObject));
//Assert
Assert.Equal("Function Add threw an exception", exception.Message);
Assert.Null(exception.InnerException);
}
Note the change to the setup and also if asserting that InvalidOperationException was thrown then mock should actually throw an InvalidOperationException
//...
var iblobStorageRepositoryMoq = new Mock<IBlobStorageRepository>();
iblobStorageRepositoryMoq
.Setup(mock => mock.Add(It.IsAny<ILogger>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
.ThrowsAsync(new InvalidOperationException("Function Add threw an exception"));
//...

Unit Testing: How to return value asynchronously from mocked interface

I'm trying to unit test a method and assert that the result is of a specific type when calling a mocked 'request provider' which handles HttpClient logic, though I have setup the mocked interface it always returns null.
Previously when using HttpClient I've mocked the HttpMessageHandler and handled the business logic in the method at the other side, however the third party API we are using requires multiple calls to their rest api using GET requests, so I wanted a solution that kept the solution more 'DRY'
The following is the setup I am currently trying to use
_requestProvider.Setup(
svc => svc.GetAsync<PlayerBalance>(It.IsAny<string>(), It.IsAny<string>()))
.Returns(() => Task.FromResult(new PlayerBalance
{
Balance = 0
}));
_playerService = new PlayerService(_playerRepository.Object,
_secretsService.Object,
_awsConfig,
_requestProvider.Object);
My act/assertion
var result = await _playerService.GetPlayerBalanceAsync(request);
result.Should().BeOfType<PlayerBalance>();
The method under test
public async Task<PlayerBalance> GetPlayerBalanceAsync(PlayerBalanceRequest playerBalanceRequest)
{
if (string.IsNullOrEmpty(playerBalanceRequest.Login)) throw new Exception("Login is a required parameter.");
string url = $#"myrestendpoint";
var result = await _requestProvider.GetAsync<List<PlayerBalance>>(url);
return result.FirstOrDefault();
}
And where it's failing invocation on the mock
public async Task<TResult> GetAsync<TResult>(string uri, string token = "")
{
HttpClient httpClient = CreateHttpClient(token);
HttpResponseMessage response = await httpClient.GetAsync(uri);
await HandleResponse(response);
string serialized = await response.Content.ReadAsStringAsync();
TResult result = await Task.Run(() =>
JsonConvert.DeserializeObject<TResult>(serialized, _serializerSettings));
return result;
}
When running in 'Strict' it is telling me that it expected invocation but no setup was found. I'm not really sure what else needs setting up here.
the result returned from the last slice of code is always null, but I want it to be PlayerBalance { Balance = 0 }
Any help would be appreciated.
Just to clarify I have also tried my setup in the following ways
.Returns(Task.FromResult(new PlayerBalance
{
Balance = 0
}));
.ReturnsAsync(new PlayerBalance {
Balance = 0
});
You are mocking:
_requestProvider.GetAsync<PlayerBalance>(url)
When you should be mocking:
_requestProvider.GetAsync<List<PlayerBalance>>(url)
Setup the mock to expect the desired behavior:
_requestProvider.Setup(
svc => svc.GetAsync<List<PlayerBalance>>(It.IsAny<string>(), It.IsAny<string>())
)
.ReturnsAsync(() => new List<PlayerBalance>() { new PlayerBalance
{
Balance = 0
}});

HttpClient PostAsync not working in CRM plugin

I am trying to send json to a web API using HttpClient.PostAsync. It works from a console application but not from my CRM plugin. Doing some research I noted that it is probably something to do with the context the plugin runs in and threading. Anyway here is my calling code:
public async void Execute(IServiceProvider serviceProvider)
{
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
if (context.InputParameters.Contains("Target"))
{
if (context.InputParameters["Target"] is Entity)
{
Entity entity = (Entity)context.InputParameters["Target"];
if (entity.LogicalName == "new_product")
{
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
try
{
if (entity.Contains("new_begindate") && entity.Contains("new_expirationdate"))
{
await OnlineSignUp(entity, service);
}
}
catch (InvalidPluginExecutionException)
{
throw;
}
catch (Exception e)
{
throw new InvalidPluginExecutionException(OperationStatus.Failed, "Error signing up: " + e.Message);
}
}
}
}
}
And here is the relevant code for sending the json:
private async Task<HttpResponseMessage> OnlineSignUp(Entity license, IOrganizationService service)
{
...
var json = JsonConvert.Serialize(invitation);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", "token=7d20f3f09ef24067ae64f4323bc95163");
Uri uri = new Uri("http://signup.com/api/v1/user_invitations");
var response = await httpClient.PostAsync(uri, content).ConfigureAwait(false);
int n = 1;
return response;
}
}
The exception is thrown with a message "Thread was being aborted". Can anyone tell me what I am doing wrong?
I would guess this is going to fail somewhat randomly based on use of async/await. I wouldn't think CRM really supports plugins returning control before they are complete. When it fails, it looks like the thread was in the process of being cleaned up behind the scenes.
CRM is already handling multi-threading of plugins and supports registering plugin steps as asynchronous if they are long running (or don't need to be run in the synchronous pipeline). It would make more sense to use a synchronous HTTP call here like:
var response = httpClient.PostAsync(uri, content).Result;
EDIT: To illustrate, this is an overly-trivialized example of what is most likely happening when CRM goes to kickoff your plugin and you're using async/await (from LinqPad).
static async void CrmInternalFunctionThatKicksOffPlugins()
{
var plugin = new YourPlugin();
//NOTE Crm is not going to "await" your plugin execute method here
plugin.Execute();
"CRM is Done".Dump();
}
public class YourPlugin
{
public async void Execute()
{
await OnlineSignUp();
}
private async Task<HttpResponseMessage> OnlineSignUp()
{
var httpClient = new HttpClient();
var r = await httpClient.PostAsync("http://www.example.com", null);
"My Async Finished".Dump();
return r;
}
}
Which will print:
CRM is Done My Async Finished
looks like you are using Json.NET, when you use external assemblies there are some things to take care of (merging) and not always the result works.
Try to serialize using DataContractJsonSerializer
example: http://www.crmanswers.net/2015/02/json-and-crm-sandbox-plugins.html

Categories