public async Task<ResModel> GetDetails()
{
ResModel resp = new ResModel();
resp = await MobController.GetAllDeatails();
return resp;
}
In the await this calls for an async task method named GetAllDeatails and returns an object ResModel.
How to properly create an unit test for this?
The unit test I've created is right below:
[TestMethod]
public async Task GetFromGetDetails()
{
var controller = new SevicesController();
ResModel resp = new ResModel();
using(ShimsContext.Create())
{
ProjectAPI.Fakes.ShimMobController.GetAllDeatails = () => {
resp.MessageType = "Get"
resp.Message ="contdetail";
return resp;
};
returnValueOfAsyncTask = await controller.GetDetails();
Assert.IsNotNull(returnValueOfAsyncTask);
}
}
This gives errors in the ShimContext creation because the return type must be async in GetAllDeatails but here it only returns the object.
Following is the main error I'm getting:
Error CS0029 Cannot implicitly convert type
'ProjectAPI.Models.ResModel' to
'System.Threading.Tasks.Task<ProjectAPI.Models.ResModel>'
UnitTestProjectAPI
The error message clearly states that it cannot convert the ResModel to a Task<ResModel> which is the return type of the method being shimmed.
In the shim you are returning the model when the method is suppose to be returning a Task. Use Task.FromResult in order to wrap the model in a Task so that it can be return and awaited.
In the exercising of the method under test you stated that the variable used to hold the result of the await was a Task<ResModel>. That should be changed because when you await a Task that returns a result it will just return the result not the task. So that result variable needs to be changed to from Task<ResModel> to ResModel.
Here is the update unit test based on above changes.
[TestMethod]
public async Task GetFromGetDetails() {
//Arrange
var controller = new SevicesController();
var expected = new ResModel();
using(ShimsContext.Create()) {
ProjectAPI.Fakes.ShimMobController.GetAllDeatails = () => {
expected.MessageType = "Get"
expected.Message ="contdetail";
return Task.FromResult(expected);
};
//Act
var actual = await controller.GetDetails();
//Assert
Assert.IsNotNull(actual);
Assert.AreEqual(expected, actual);
}
}
Change the Method return type
to
(public async Task
<
ResModel> GetFromGetDetails()
Related
I am new to signalr and I set up a server:
services.AddControllers();
services.AddSignalR(o =>
{
o.EnableDetailedErrors = true;
}).AddJsonProtocol(options =>
{
options.PayloadSerializerOptions.WriteIndented = true;
options.PayloadSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
options.PayloadSerializerOptions.Converters.Add(new JsonStringEnumConverter());
});
And in my hub I have a function that returns an object
public async Task<ResultType> FunctionWithTResult(string path)
{
// Do work
return new ResultType { StatusCode = 200 };
}
The problem is that this function is not returning the ResultType. The client only gets an empty object.
I build a client software to test this and it's the same result.
ResultType result = await connection.InvokeAsync<ResultType >("FunctionWithTResult", "test");
Strangely string and int Tasks work just fine. The problem only happens with objects.
Does anyone know what I am doing wrong?
This is my first question in SO, i'm new using DataFlow with BroadcastBlock and ActionBlock, i hope i can get solution in here.
Here's the structure.
Model
class SampleModel
{
public string Id { get; set; } = Guid.NewGuid().ToString();
public bool Success { get; set; } = true;
public object UniqueData { get; set; }
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine($"Id - {Id}");
sb.AppendLine($"Success - {Success}");
sb.AppendLine($"UniqueData - {UniqueData}");
string tmp = sb.ToString();
sb.Clear();
return tmp;
}
}
DataFlow Logic
class CreateDownloadTask
{
public async Task VeryLongProcess()
{
await Task.Run(async () =>
{
Console.WriteLine("Long Process Working..");
await Task.Delay(TimeSpan.FromSeconds(5));
Console.WriteLine("Long Process Done..");
});
}
public async Task CreateSimpleBroadcastX<T>(T data)
{
Action<T> process = async model =>
{
Console.WriteLine("Working..");
await VeryLongProcess();
Console.WriteLine("Done");
};
var broad = new BroadcastBlock<T>(null);
var action = new ActionBlock<T>(process);
var dflo = new DataflowLinkOptions { PropagateCompletion = true };
broad.LinkTo(action, dflo);
await broad.SendAsync(data);
broad.Complete();
await action.Completion.ContinueWith(async tsk =>
{
Console.WriteLine("Continue data");
}).ConfigureAwait(false);
Console.WriteLine("All Done");
}
}
Caller
var task = cdt.CreateSimpleBroadcastX<SampleModel>(new SampleModel
{
UniqueData = cdt.GetHashCode()
});
task.GetAwaiter().GetResult();
Console.WriteLin("Completed");
I expect the result should be
Working..
Long Process Working..
Long Process Done..
Done
Continue data
All Done
Completed
But what i've got is
Working..
Long Process Working..
Continue data
All Done
Completed
Long Process Done..
Done
This is happen when ther is async-await inside of ActionBlock.
Now, the question is, is that possible to make the result as i expected without WaitHandle ?
That mean, ActionBlock.Completion will be wait until the Action or Delegate inside the ActionBlock is complete executed?
Or i'm i doing it wrong with that patter?
Thanks in Advance, and sorry for my bad english.
Your problem is here:
Action<T> process = async model => ...
That code creates an async void method, which should be avoided. One of the reasons you should avoid async void is because it is difficult to know when the method has completed. And this is exactly what is happening: the ActionBlock<T> cannot know when your delegate has completed because it is async void.
The proper delegate type for an asynchronous method without a return value that takes a single argument is Func<T, Task>:
Func<T, Task> process = async model => ...
Now that the asynchronous method returns a Task, the ActionBlock can know when it completes.
We are using Automappers ProjectTo method to build a DTO object that is a subset of a larger database view. It is working as expected when running the actual application but we have had a problem where it doesn't give expected results when unit testing with an EF Core In-Memory database. It seems to just be giving back 0 results no matter what the query. Here is the test I am trying to run.
[Fact]
public async Task GetTemplateAdHocReportList_ReturnsOnlyTemplateReports()
{
await TestHelper.SeedFull(ReportContext); // Calls SeedAdHocReports below along with other seed methods
var results = await _sut.GetTemplateAdHocReports();
results.Where(x => !x.IsTemplate).Count().Should().Be(0);
}
This is the Seed Data:
public static async Task SeedAdHocReports(ReportContext context)
{
var reports = new AdHocReport[]
{
new AdHocReport()
{
Id = 1,
Name = "DevExtreme Example Report",
IsTemplate = true,
AdHocDataSourceId = 1,
Fields = "[{\"caption\":\"Category\",\"dataField\":\"ProductCategoryName\",\"expanded\":true,\"area\":\"row\"},{\"caption\":\"Subcategory\",\"dataField\":\"ProductSubcategoryName\",\"area\":\"row\"},{\"caption\":\"Product\",\"dataField\":\"ProductName\",\"area\":\"row\"},{\"caption\":\"Date\",\"dataField\":\"DateKey\",\"dataType\":\"date\",\"area\":\"column\"},{\"caption\":\"Amount\",\"dataField\":\"SalesAmount\",\"summaryType\":\"sum\",\"format\":{\"type\":\"currency\",\"precision\":2,\"currency\":\"USD\"},\"area\":\"data\"},{\"caption\":\"Store\",\"dataField\":\"StoreName\"},{\"caption\":\"Quantity\",\"dataField\":\"SalesQuantity\",\"summaryType\":\"sum\"},{\"caption\":\"Unit Price\",\"dataField\":\"UnitPrice\",\"format\":\"currency\",\"summaryType\":\"sum\"},{\"dataField\":\"Id\",\"visible\":false}]",
Status = true
}
};
context.AdHocReports.AddRange(reports);
await context.SaveChangesAsync();
}
Here is the GetTemplateAdHocReports method being tested
public async Task<IList<AdHocReportDto>> GetTemplateAdHocReports()
{
//This gives the expected 1 object in the unit tests:
var test = await _reportContext.AdHocReports.Where(x => x.Status && x.IsTemplate).OrderBy(x => x.Name).ToListAsync();
//This always comes back with a count of 0 even though the seed data should return 1 result
var results = await _reportContext.AdHocReports.Where(x => x.Status && x.IsTemplate).OrderBy(x => x.Name).ProjectTo<AdHocReportDto>(_mapper.ConfigurationProvider).ToListAsync();
return results;
}
Finally in case they are useful here are the constructors:
public AdHocServiceTests()
{
TestHelper = new TestHelper();
var reportOptions = TestHelper.GetMockedReportDbOptions();
ReportContext = new ReportContext(reportOptions);
_sut = new AdHocService(ReportContext, TestHelper.Mapper, TestHelper.GetMockedNiceService().Object);
}
public TestHelper()
{
var mappingConfig = new MapperConfiguration(cfg =>
{
cfg.AddProfile<ReportDtoMapperProfile>();
});
Mapper = mappingConfig.CreateMapper(); // public property on TestHelper
}
public AdHocService(ReportContext reportContext, IMapper mapper, INiceService niceService)
{
_niceService = niceService;
_mapper = mapper;
_reportContext = reportContext;
}
So the final question is why does the "var test = " line above work, but the following "var results =" line with the ProjectTo just keeps giving back 0 results?
I have refactored a Web API to rely on async/await in ASP.NET Core 3.1 and I have the following scenario: a statistics method is sequentially computing a list of indicators which are defined in a list.
readonly Dictionary<StatisticItemEnum, Func<Task<SimpleStatisticItemApiModel>>> simpleItemActionMap =
new Dictionary<StatisticItemEnum, Func<Task<SimpleStatisticItemApiModel>>>();
private void InitSimpleStatisticFunctionsMap()
{
simpleItemActionMap.Add(StatisticItemEnum.AllQuestionCount, GetAllQuestionCountApiModel);
simpleItemActionMap.Add(StatisticItemEnum.AllAnswerCount, GetAllAnswerCountApiModel);
simpleItemActionMap.Add(StatisticItemEnum.AverageAnswer, GetAverageAnswer);
// other mappings here
}
private async Task<SimpleStatisticItemApiModel> GetAllQuestionCountApiModel()
{
// await for database operation
}
private async Task<SimpleStatisticItemApiModel> GetAllAnswerCountApiModel()
{
// await for database operation
}
private async Task<SimpleStatisticItemApiModel> GetAverageAnswer()
{
// await for database operation
}
The code sequentially goes through each item and computes it and after the refactoring it is looking like this:
itemIds.ForEach(itemId =>
{
var itemEnumValue = (StatisticItemEnum) itemId;
if (simpleItemActionMap.ContainsKey(itemEnumValue))
{
var result = simpleItemActionMap[itemEnumValue]().Result;
payload.SimpleStatisticItemModels.Add(result);
}
});
I know that Task.Result might lead to deadlocks, but I could not find any other way to make this work.
Question: How to execute a dynamic list of async functions in a sequential way?
You should change the ForEach call to a regular foreach, and then you can use await:
foreach (var itemId in itemIds)
{
var itemEnumValue = (StatisticItemEnum) itemId;
if (simpleItemActionMap.ContainsKey(itemEnumValue))
{
var result = await simpleItemActionMap[itemEnumValue]();
payload.SimpleStatisticItemModels.Add(result);
}
}
Do not make the ForEach lambda async; that will result in an async void method, and you should avoid async void.
I think you can do this:
itemIds.ForEach(async itemId =>
{
var itemEnumValue = (StatisticItemEnum) itemId;
if (simpleItemActionMap.ContainsKey(itemEnumValue))
{
var result = await simpleItemActionMap[itemEnumValue]();
payload.SimpleStatisticItemModels.Add(result);
}
});
I have Controller whit some endpoints Task<IActionResult> MyCustomEndpoint which is returning return Ok(CustomDataType). Returned datas are in JSON fromat.
Now I want to call that Endpoint from other Controller like var resp = myController.MyCustomEndpoint, where resp becomes IActionResult. The problem is that resp now doesn't return only datas anymore, but all of those fields as seen on image.
My question is, how to access and return only Value field, because resp.Value is not working.
Thanks for help.
You have to convert to as ObjectResult
public async Task GetFormSummary_ItShouldReturnSingleForm_NotNull()
{
var formId = new Guid("5EF685E7-1167-4226-7F5E-08D9009544A3");
var mockFormService = new Mock<IFormService>();
mockFormService.Setup(x => x.GetAsync(formId).Result)
.Returns(GetTestForm());
// Inject
var formController = new FormController(_logger, mockFormService.Object);
// Act
var result = (await formController.GetAsync(formId)) as ObjectResult;
// Assert
Assert.IsAssignableFrom<FormViewModel>(result.Value);
}