I try to get result of GetAll() function in unit test, but i can't convert this to list. How can i do it correctly ?
Test:
[Fact]
public async Task GetAllHeroes_ShouldReturnAllHeroes()
{
var controller = new HeroesController(_heroes);
var response = await controller.GetHeroes() as List<Hero>;
//here i need response list, but there's error
}
Controller:
// GET: api/v1/heroes
[HttpGet]
[Produces(typeof(List<Hero>))]
public async Task<ActionResult<IEnumerable<Hero>>> GetHeroes()
{
var result = await _heroes.GetAll();
return Ok(result);
}
You need to parse the response to a list of your model
like this
var response = await controller.GetHeroes();
Assert.IsType<OkObjectResult>(result);
var content = ((OkObjectResult)result).Value;
Assert.IsType<List<Hero>>(content);
var Heros = (List<Hero>)content;
I added some other asserts that may be useful
Does something like this work:
// Act
var result = await controller.GetHeroes();
var response = result as OkNegotiatedContentResult<IEnumerable<Hero>>;
// Assert
// Assert.IsNotNull(response);
var heroes = response.Content.Result;
Assert.IsInstanceOfType(heroes, typeof(IEnumerable<Hero>), "Incorrect Types");
var dtos = heroes.ToArray();
// Asserting output
Assert.AreEqual(1, dtos.Length);
Assert.AreEqual(expected[0].id, dtos[0].id);
For get IEnumerable, that is return to Action:
var response = await controller.GetHeroes();
var heroList = (IEnumerable<Hero>)response.Result;
More info here
Related
In the below code for each request we are calling rest client in an Async manner. Rest client is basically a http client which is calling some webapis in a batch. If suppose AsyncTaskCount value is 5 then 5 request will be called asynchronously and then in the while block we are getting the result for each call. If any response out of those 5 request has an exception then the response will be faulted and IsFaulted becomes true for that particular request and in the response we can get the inner exception.
private async Task<List<RequestResponse>> ProcessInvestment(List<Request> Requests, List<Result> Results, ILogger log)
{
var requestResponses = new List<RequestResponse>();
var asyncTaskCount = Convert.ToInt32(Environment.GetEnvironmentVariable("AsyncTaskCount"));
log.LogInformation($"Start processing {Requests.Count} in batches of {asyncTaskCount}");
for (int index = 0; index < Requests.Count; index = index + asyncTaskCount)
{
var requestBatch = Requests.Skip(index).Take(asyncTaskCount).ToList();
var requests = requestBatch.Select(x => _restClient.RequestResponse(x)).ToList();
while (requests.Count > 0)
{
// Identify the first task that completes.
Task<RequestResponse> requestResponseTask = await Task.WhenAny(requests);
var requestResponse = new RequestResponse();
// ***Remove the selected task from the list so that you don't process it more than once
requests.Remove(requestResponseTask);
if (!requestResponseTask.IsFaulted)
{
// Await the completed task.
requestResponse = await requestResponseTask;
requestResponses.Add(requestResponse);
}
else
{
if (requestResponseTask.Exception.InnerException != null && requestResponseTask.Exception.InnerException is Exception)
{
var result = new Result();
result = ResponseTransformComponent.ResponseToResult(((Exception)requestResponseTask.Exception.InnerException).Request, null);
result.SetBadRequestErrorDetails(((Exception)RequestResponseTask.Exception.InnerException).BadRequestResponse);
results.Add(Result);
}
else
{
throw requestResponseTask.Exception;
}
}
}
log.LogInformation($"Number of records processed = {requestResponses.Count}");
}
log.LogInformation($"Total invalid and Bad requests count = {results.Count}");
return RequestResponses;
}
Below is the code for restclient which is called from the above method.
public async Task<Response> RequestResponse(Request request)
{
var response = await GetDataFromService("calculation", "CalculateCapital", request);
return JsonConvert.DeserializeObject<Response>(response);
}
public async Task<string> GetDataFromService(string controller, string method, object request)
{
var client = _httpClientFactory.CreateClient(ServiceEnum.DCS);
string baseAddress = client.BaseAddress.ToString();
var requestUrl = $"api/{controller}/{method}";
var content = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json");
var response = await client.PostAsync(requestUrl, content).ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
var responseResult = response.Content.ReadAsStringAsync().Result;
if (response.StatusCode == HttpStatusCode.BadRequest)
{
throw new CalculatorServiceException("Bad Request", JsonConvert.DeserializeObject<BadRequestResponse>(responseResult), (Request)request);
}
throw new Exception($"Status code: {response.StatusCode}. {responseResult}");
}
return response.Content.ReadAsStringAsync().Result;
}
GetDataFromService method is called from Calculate method. And GetDataFromService method will return a custom exception if the request is a Bad Request.
I am trying to write the unit test case for the above method and try to mock a request so that it will return a faulted task and then IsFaulted should become true. Below is the part of my unit test case.
_restClient
.When(a => a.RequestResponse(Arg.Any<Request>()))
.Do(a => {Task.FromException(new CalculatorServiceException(string.Empty, new BadRequestResponse { Message = string.Empty, ModelState = new Dictionary<string, string[]> { { "CalculationDates.StartDate", new string[] { "0002: Duration too short to execute calculations (CalculationDates.StartDate)" } } } }, Arg.Any<Request>())); });
If i mock my restclient method like above then it is throwing the exception instead of giving the response with IsFaulted to true. So how should i mock the restclient method so that it will return a faulted task which has an exception instead of throwing it. Please suggest.
Thanks in advance.
When..Do is for wiring up callbacks for when a member is called.
Try using Returns instead:
_restClient.RequestResponse(Arg.Any<Request>())
.Returns(x => Task.FromException<RequestResponse>(...));
// (assuming RequestResponse(..) returns a Task<RequestResponse>. Tweak as required)
I have 2 projects. One of them aspnet core webapi and second one is console application which is consuming api.
Api method looks like:
[HttpPost]
public async Task<IActionResult> CreateBillingInfo(BillingSummary
billingSummaryCreateDto)
{
var role = User.FindFirst(ClaimTypes.Role).Value;
if (role != "admin")
{
return BadRequest("Available only for admin");
}
... other properties
billingSummaryCreateDto.Price = icu * roc.Price;
billingSummaryCreateDto.Project =
await _context.Projects.FirstOrDefaultAsync(x => x.Id ==
billingSummaryCreateDto.ProjectId);
await _context.BillingSummaries.AddAsync(billingSummaryCreateDto);
await _context.SaveChangesAsync();
return StatusCode(201);
}
Console application which consuming api:
public static async Task CreateBillingSummary(int projectId)
{
var json = JsonConvert.SerializeObject(new {projectId});
var data = new StringContent(json, Encoding.UTF8, "application/json");
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", await Token.GetToken());
var loginResponse = await client.PostAsync(LibvirtUrls.createBillingSummaryUrl,
data);
WriteLine("Response Status Code: " + (int) loginResponse.StatusCode);
string result = loginResponse.Content.ReadAsStringAsync().Result;
WriteLine(result);
}
Program.cs main method looks like:
static async Task Main(string[] args)
{
if (Environment.GetEnvironmentVariable("TAIKUN_USER") == null ||
Environment.GetEnvironmentVariable("TAIKUN_PASSWORD") == null ||
Environment.GetEnvironmentVariable("TAIKUN_URL") == null)
{
Console.WriteLine("Please specify all credentials");
Environment.Exit(0);
}
Timer timer = new Timer(1000); // show time every second
timer.Elapsed += Timer_Elapsed;
timer.Start();
while (true)
{
Thread.Sleep(1000); // after 1 second begin
await PollerRequests.CreateBillingSummary(60); // auto id
await PollerRequests.CreateBillingSummary(59); // auto id
Thread.Sleep(3600000); // 1hour wait again requests
}
}
Is it possible find all id and paste it automatically instead of 59 and 60? Ids from projects table. _context.Projects
Tried also approach using method which returns ids
public static async Task<IEnumerable<int>> GetProjectIds2()
{
var json = await
Helpers.Transformer(LibvirtUrls.projectsUrl);
List<ProjectListDto> vmList =
JsonConvert.DeserializeObject<List<ProjectListDto>>(json);
return vmList.Select(x => x.Id).AsEnumerable(); // tried
ToList() as well
}
and in main method used:
foreach (var i in await PollerRequests.GetProjectIds2())
new List<int> { i }
.ForEach(async c => await
PollerRequests.CreateBillingSummary(c));
for first 3 ids it worked but does not get other ones,
tested with console writeline method returns all ids
First get all Ids:
var ids = await PollerRequests.GetProjectIds2();
Then create list of task and run all tasks:
var taskList = new List<Task>();
foreach(var id in ids)
taskList.Add(PollerRequests.CreateBillingSummary(id));
await Task.WhenAll(taskList);
I am trying to test for the results of certain exceptions when Nest has a value in IGetResponse.OriginalException property.
I first set up the response:
var response = A.Fake<Nest.IGetResponse<Dictionary<string, object>>>();
A.CallTo(() => response.OriginalException).Returns(new Exception("Status code 404"));
Then the fake elastic client:
var client = A.Fake<Nest.IElasticClient>();
A.CallTo(client)
.WithReturnType<Nest.IGetResponse<Dictionary<string, object>>>()
.Returns(response);
The client gets injected into the class I am testing.
However, when stepping through the code, when the client is called it returns a faked response, but the OriginalException getter has no value. Its not null, but none of the properties has any value. I was expecting the OriginalException.Message to equal Status code 404.
I also tried setting the response object to:
var response = A.Fake<Nest.IGetResponse<Dictionary<string, object>>>();
A.CallTo(() => response.OriginalException.Message).Returns("Status code 404");
... with equally poor results.
How can I set the IGetResponse so I can evaluate OriginalException.Message in the class being tested?
More code was requested. I can show the entire test, and I will show the method being tested. Here is my entire test:
[TestMethod]
[ExpectedException(typeof(NotFoundException))]
public void Get_ClientReturns404_ThrowsNotFoundException()
{
// setup
var request = new DataGetRequest
{
CollectionName = string.Empty,
DocumentType = string.Empty,
DataAccessType = string.Empty
};
var response = A.Fake<Nest.IGetResponse<Dictionary<string, object>>>();
A.CallTo(() => response.OriginalException.Message).Returns("Status code 404");
var client = A.Fake<Nest.IElasticClient>();
A.CallTo(client)
.WithReturnType<Nest.IGetResponse<Dictionary<string, object>>>()
.Returns(response);
var elasticSearch = new ElasticSearch(null, client);
// test
var result = elasticSearch.Get(request);
// assert
Assert.Fail("Should have hit an exception.");
}
}
And here is the method being tested:
public async Task<Dictionary<string, object>> Get(DataGetRequest getRequest)
{
GetRequest request = new GetRequest(getRequest.CollectionName, getRequest.DocumentType, getRequest.Id);
var response = await Client.GetAsync<Dictionary<string, object>>(request);
if (response.OriginalException != null)
{
var message = response.OriginalException.Message;
if (message.Contains("Status code 404"))
throw new NotFoundException(String.Format("Not Found for id {0}", getRequest.Id));
else
throw new Exception(message);
}
return response.Source;
}
The error handling in the IF block is not very robust. Once the unit test works then the code will likely receive more love.
The return type of the mocked client is wrong as the IElasticClient.GetAsync<> returns a Task<IGetResponse<T>>.
Task<IGetResponse<T>> GetAsync<T>(IGetRequest request, CancellationToken cancellationToken = default(CancellationToken)) where T : class;
Source
So the setup needs to return a Task derived result to allow the async code
var response = await Client.GetAsync<Dictionary<string, object>>(request);
to flow as expected.
For example
[TestMethod]
[ExpectedException(typeof(NotFoundException))]
public async Task Get_ClientReturns404_ThrowsNotFoundException() {
//Arrange
var originalException = new Exception("Status code 404");
var response = A.Fake<Nest.IGetResponse<Dictionary<string, object>>>();
A.CallTo(() => response.OriginalException).Returns(originalException);
var client = A.Fake<Nest.IElasticClient>();
A.CallTo(() =>
client.GetAsync<Dictionary<string, object>>(A<IGetRequest>._, A<CancellationToken>._)
).Returns(Task.FromResult(response));
var request = new DataGetRequest {
CollectionName = string.Empty,
DocumentType = string.Empty,
DataAccessType = string.Empty
};
var elasticSearch = new ElasticSearch(null, client);
// Act
var result = await elasticSearch.Get(request);
// Assert
Assert.Fail("Should have hit an exception.");
}
In my Asp.net core 1 app I have controller with following method:
[Microsoft.AspNetCore.Mvc.HttpPost()]
[Microsoft.AspNetCore.Mvc.RequireHttps]
public async System.Threading.Tasks.Task<Microsoft.AspNetCore.Mvc.IActionResult> Save()
{
if (ModelState.IsValid)
{
try
{
var pass = Request.Form["password"].ToString();
var pass1 = Request.Form["password1"].ToString();
if (!pass.Equals(pass1))
{
return View("~/Views/PasswordRecovery.cshtml");
}
}
catch (System.Exception ex)
{
return View("~/Views/Message.cshtml");
}
}
return View("~/Views/Message.cshtml");
}
I want to write a test for this method. So I have written this:
[Xunit.Fact]
public async System.Threading.Tasks.Task SavePassNotEqualTest()
{
var controller = new Controllers.PasswordRecoveryController(_mockRepo.Object);
var dic = new System.Collections.Generic.Dictionary<string, Microsoft.Extensions.Primitives.StringValues>();
dic.Add("password", "test");
dic.Add("password1", "test1");
var collection = new Microsoft.AspNetCore.Http.FormCollection(dic);
controller.Request.Form = collection; //request is null
var result = await controller.Save();
var viewResult = Xunit.Assert.IsType<Microsoft.AspNetCore.Mvc.ViewResult>(result);
Xunit.Assert.Equal("~/Views/Message.cshtml", viewResult.ViewName);
}
The problem is that I need to set some test values to Form, Form is in Request, and Request is NULL. I can not find, how can I create some not NULL request and fill it's Form with values.
EDIT
Answers helped me to finish up with following solution:
I've created a method that will return a FormCollection:
private Microsoft.AspNetCore.Http.FormCollection GetFormCollection()
{
var dic = new System.Collections.Generic.Dictionary<string, Microsoft.Extensions.Primitives.StringValues>();
dic.Add("password", "test");
dic.Add("password1", "test1");
return new Microsoft.AspNetCore.Http.FormCollection(dic);
}
And my test method is:
[Xunit.Fact]
public async System.Threading.Tasks.Task SavePassNotEqualTest()
{
var controller = new Findufix.Controllers.PasswordRecoveryController(_mockRepo.Object);
var httpContext = new Moq.Mock<Microsoft.AspNetCore.Http.HttpContext>();
httpContext.Setup( x => x.Request.Form).Returns(GetFormCollection());
controller.ControllerContext.HttpContext = httpContext.Object;
var result = await controller.Save();
var viewResult = Xunit.Assert.IsType<Microsoft.AspNetCore.Mvc.ViewResult>(result);
Xunit.Assert.Equal("~/Views/PasswordRecovery.cshtml", viewResult.ViewName);
}
If you pass a DefaultHttpContext to your controller, Request won't be null and you can assign the form to Request.Form. No mocking required.
[Xunit.Fact]
public async System.Threading.Tasks.Task SavePassNotEqualTest()
{
var controller = new Controllers.PasswordRecoveryController(_mockRepo.Object);
var dic = new System.Collections.Generic.Dictionary<string, Microsoft.Extensions.Primitives.StringValues>();
dic.Add("password", "test");
dic.Add("password1", "test1");
var collection = new Microsoft.AspNetCore.Http.FormCollection(dic);
// Give the controller an HttpContext.
controller.ControllerContext.HttpContext = new DefaultHttpContext();
// Request is not null anymore.
controller.Request.Form = collection;
var result = await controller.Save();
var viewResult = Xunit.Assert.IsType<Microsoft.AspNetCore.Mvc.ViewResult>(result);
Xunit.Assert.Equal("~/Views/Message.cshtml", viewResult.ViewName);
}
With Moq, you can do it like this:
var httpContext = new Mock<HttpContextBase>();
httpContext.Setup(c => c.Request.Form).Returns(delegate()
{
var formVaues = new NameValueCollection();
formVaues .Add("Id", "123");
formVaues .Add("Name", "Smith");
return formVaues ;
});
In Moq you can try to use Setup() or SetupGet() to teach it to return something that you need
something along the lines of
controller.SetupGet(x => x.Request.Form).Returns(collection);
I need to test the following Patch method in my odata controller from my test project.
[ValidateModel]
[AcceptVerbs("PATCH", "MERGE")]
public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<User> patch)
{
var user = await db.Users.FindAsync(key);
if (user == null)
{
return NotFound();
}
patch.Patch(user);
Validate(user);
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
db.Entry(user).Property(p => p.UserType).IsModified = false;
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!UserExists(key))
{
return NotFound();
}
throw;
}
return Updated(user);
}
The code in the test project is as follows. Could someone tell me how do I pass value to the Delta parameter. Currently I am getting compilation errors at line controller.Patch(1, user);.
[TestMethod]
public void TestPatch()
{
// Arrange
var controller = new UsersController();
var user = new User();
user.Id = 1;
user.Lastname = "Johanson";
// Act
controller.Patch(1, <System.Web.OData.Delta> user);
// Assert
}
You can also declare the delta using the dynamic keyword and set the properties directly:
dynamic delta = new Delta<User>();
delta.Id = 1;
delta.Lastname = "Johanson";
var delta = new Delta<User>(typeof(User));
delta.TrySetPropertyValue("Id", 1);
delta.TrySetPropertyValue("Lastname", "Johanson");
I don't know if there are any helpers to make that easier
#yenta's answer is perfectly fine, but if you can, also consider using the nameof (since C# 6.0)
var delta = new Delta<User>(typeof(User));
delta.TrySetPropertyValue(nameof(User.Id), 1);
delta.TrySetPropertyValue(nameof(User.Lastname), "Johanson");