I've done a lot of heavy modification to some async code, and I've been tinkering with it long enough that I am in need of a second set of eyes.
In a unit test, I'm calling my async code, and then validating the values it should return. The object that ultimately performs the async operation is mocked.
The Unit Test:
public void GetCharactersAsync_ById_Test()
{
var charDictionary = TestHelper.GetCharactersIdDictionary(1);
var mockReq = new Mock<IRequester>();
mockReq.Setup(x => x.CreateGetRequestAsync<Dictionary<long, Character>>
(It.IsAny<string>())).ReturnsAsync(charDictionary);
var char1 = TestHelper.GetCharacter();
var api = GameObject.GetInstance(mockReq.Object);
var character = api.GetCharacterAsync(TestHelper.GetRegion(), (int)char.Id);
Assert.AreEqual(character.Result.Name, char1.Name);
}
GetCharacterAsync:
public async Task<Character> GetCharacterAsync(Region region, int charId)
{
var res = await requester.CreateGetRequestAsync<Task<Dictionary<long, Character>>>
(bld.BuildUrlForEndpoint("GetCharacter",
region, charId.ToString()));
var obj = res.Result.Values.FirstOrDefault();
return obj;
}
requester.CreateGetRequestAsync (though this should be mocked and not matter):
public async Task<T> CreateGetRequestAsync<T>(string urlString)
{
return await this.GetResultAsyncDeserialized<T>(new HttpRequestMessage(HttpMethod.Get, urlString));
}
and finally, GetResultAsyncDeserialized (though this is mocked and shouldn't matter):
protected async Task<T> GetResultAsyncDeserialized<T>(HttpRequestMessage request)
{
var result = string.Empty;
using (var response = await httpClient.GetAsync(request.RequestUri))
{
if (!response.IsSuccessStatusCode)
{
HandleRequestFailure(response.StatusCode);
}
using (var content = response.Content)
{
result = await content.ReadAsStringAsync();
}
}
return JsonConvert.DeserializeObject<T>(result);
}
I have next to zero experience with async, and this code is very similar to the old working code in structure (I moved a few things around, like the JsonConvert to the bottom layer when it used to be up top). The major changes, in fact, were all at the level that is being mocked, and shouldn't impact the unit test. The unit test is almost identical to the old one (removed some serialization/deserialization).
Exception is a Null Reference Exception on GetCharacterAsync line:
var obj = res.Result.Values.FirstOrDefault();
Your await requester.CreateGetRequestAsync<Task<Dictionary<long, Character>>> is wrong, you should not be passing Task in to that function, the method signature should be await requester.CreateGetRequestAsync<Dictionary<long, Character>>
public async Task<Character> GetCharacterAsync(Region region, int charId)
{
var res = await requester.CreateGetRequestAsync<Dictionary<long, Character>>
(bld.BuildUrlForEndpoint("GetCharacter",
region, charId.ToString()));
var obj = res.Values.FirstOrDefault();
return obj;
}
The reason you are getting null is in GetCharacterAsync you call CreateGetRequestAsync<Task<Dictionary<long, Character>>> but your moq was set up with mockReq.Setup(x => x.CreateGetRequestAsync<Dictionary<long, Character>>
P.S. As a side note, you really should not likely be doing character.Result.Name in your test, make the test return async Task instead of void and await the result of api.GetCharacterAsync. Pretty much every unit test library (including the one built in to visual studio) supports async/await now.
public async Task GetCharactersAsync_ById_Test()
{
var charDictionary = TestHelper.GetCharactersIdDictionary(1);
var mockReq = new Mock<IRequester>();
mockReq.Setup(x => x.CreateGetRequestAsync<Dictionary<long, Character>>
(It.IsAny<string>())).ReturnsAsync(charDictionary);
var char1 = TestHelper.GetCharacter();
var api = GameObject.GetInstance(mockReq.Object);
var character = await api.GetCharacterAsync(TestHelper.GetRegion(), (int)char.Id);
Assert.AreEqual(character.Name, char1.Name);
}
Related
I am new to Moq and Mocks/Unit Testing in general. After watching a video on Mocking which uses Moq, I thought I had enough understanding to start setting up a few simple tests for a project I am working on. However, no amount of fiddling with the code helps my first test pass.
Here is the code I have:
Interface Being Tested
public interface IDataInterface
{
Task<IList<T>> GetData<T>(string url);
// Other stuff
}
Implementation of Interface Method GetData<T>
public async Task<IList<T>> GetData<T>(string url)
{
try
{
var json = await client.GetAsync(url).Result.Content.ReadAsStringAsync();
var data = (JObject)JsonConvert.DeserializeObject(json);
JArray arr = (JArray)data["resource"];
return arr.ToObject<IList<T>>();
}
catch (InvalidCastException icEx)
{
throw new InvalidCastException("An error occurred when retrieving the data", icEx);
}
// Other catches
}
Service Calling Implemented Interface GetData<T>
public async Task<IList<MyObj>> GetActiveObjs()
{
var data = await _myImplementedInterface.GetData<MyObj>(ActiveUrl);
return data;
}
My Test
[Fact]
public async void MyImplementedInterface_GetActive_ReturnsDataOrEmptyList()
{
var _activeUrl = "http://some.url";
using (var mock = AutoMock.GetLoose())
{
mock.Mock<IDataInterface>()
.Setup(x => x.GetData<MyObj>(_activeUrl))
.Returns(Task.FromResult(_SomeStaticDataRepresentation)));
var svc = mock.Create<MyService>();
var expected = _SomeStaticDataRepresentation;
var actual = await svc.GetActiveObjs();
Assert.True(actual != null);
// Other assertions that never matter because of the null value of _actual_ variable
}
}
Early on, I had issues because the project uses Autofac and Moq, but I resolved those specific issues. However, I cannot seem to get past the null value coming back from the service call. When running the project, the method returns data, just as expected, so I am flummoxed as to where the issue lies. Reviewing various posts and the Moq Quickstart did not provide what I needed to solve this myself, so my hope is there is someone here who can tell me what I am doing wrong. I apologize already as I am certain it is a newbie mistake.
Addressing the implementation first. Avoid mixing async-await and blocking calls like Wait() and .Result which can result in deadlocks.
Reference Async/Await - Best Practices in Asynchronous Programming
public async Task<IList<T>> GetData<T>(string url) {
try {
var response = await client.GetAsync(url);
var json = await response.Content.ReadAsStringAsync();
var data = (JObject)JsonConvert.DeserializeObject(json);
JArray arr = (JArray)data["resource"];
return arr.ToObject<IList<T>>();
} catch (InvalidCastException icEx) {
throw new InvalidCastException("An error occurred when retrieving the data", icEx);
}
// Other catches
}
For the subject method under test, if nothing needs to be awaited then there is no need to make the function async, just return the Task
public Task<IList<MyObj>> GetActiveObjs() {
return _myImplementedInterface.GetData<MyObj>(ActiveUrl);
}
Now for the test, since already using tasks, then the test should be async and the subject awaited as well.
[Fact]
public async Task MyImplementedInterface_GetActive_ReturnsDataOrEmptyList() {
using (var mock = AutoMock.GetLoose()) {
//Arrange
IList<MyObj> expected = _SomeStaticDataRepresentation;
mock.Mock<IDataInterface>()
.Setup(x => x.GetData<MyObj>(It.IsAny<string>()))
.ReturnAsync(expected);
var svc = mock.Create<MyService>();
//Act
var actual = await svc.GetActiveObjs();
//Assert
Assert.True(actual != null);
Assert.True(actual == expected);
// ...
}
}
Assuming, based on what was shown it is uncertain what the active URL would have been, it could be ignored in the test using It.IsAny<string>().
I am trying calling the async method SendParkInfo method using the await operator like this
await Task.WhenAny(parkList);and await Task.WhenAny(parkInfo);
parkInfo has SendParkInfo method object
Here is some part of my code.
public async Task<AvailableParksResponse> GetAvailableParks(IEnumerable<string> parkRegionList)
{
//
var parkList = parkRegionList.Select(x => SendParkInfo(x)).ToList();
var parkListTask = await Task.WhenAny(parkList);
response.ParkInfoList = new List<Task<ParkInfo>> { parkListTask };
var parkInfo = SendParkInfo(id);
var parkTask = await Task.WhenAny(parkInfo);
response.ParkInfo = new List<Task<ParkInfo>> { parkTask };
//
}
public virtual async Task<ParkInfo> SendParkInfo(string id)
{
//
var apiResponse = await apiClient.GetAsync(RequestUri + id);
//
}
Is it ok to call SendParkInfo like the way I am calling and then using await operator with Task.WhenAny method. Or is there any better way of calling the Async SendParkInfo method
Any help or suggestion would be appreciated.
Thanks in advance
Task.WhenAny() will return a Task which is considered completed when at least one item in the list of tasks passed into WhenAny() has completed. This usually an appropriate option if you want to return data incrementally, as processing completes.
Alternatively, if your intent is to only return a result when all async tasks have completed, consider Task.WhenAll().
I'm trying to fill my model with data that I get from an asynchronous operation to my database. The problem is that the function returns the View (without the completed model), despite my await call.
I have tried to put a timer (I know that is not the solution), to be sure that the problem come from the asynchronous, I have also tried to put on comment some part of code inside my ForEachAsync, but it doesn't seem to help.
I get a list of project, that I fill with some additional information, finally, I assign my object to my model then return the View
public async Task<IActionResult> newProjetList(int GestionaireId, int VilleId)
{
ProjetListModel model = new ProjetListModel();
ProjetService projetService = new ProjetService(connectionString);
UserServices userServices = new UserServices(connectionString);
AvancementService avancementService = new AvancementService(connectionString);
VilleService villeService = new VilleService(connectionString);
List<Projet> projets = await projetService.NewProjetLine(GestionaireId, VilleId);
await projets.ToAsyncEnumerable().ForEachAsync(async p =>
{
int villeId = await villeService.getVilleIdByProjetId(p.ProjetId);
Ville ville = await villeService.GetById(villeId);
p.Ville = ville.VilleLabel;
p.GestionnaireProjet = await userServices.GetNameById(p.GestionnaireProjetId ?? 0);
await p.SousProjet.ToAsyncEnumerable().ForEachAsync(async sp =>
{
sp.Avancement = await avancementService.GetLabelById(sp.AvancementId);
});
});
model.projets = projets;
//System.Threading.Thread.Sleep(5000);
return View("ProjetList", model);
}
I expected an output with the missing information (here are the 'ville', 'gestionnairesProjet' and 'Avancement'
ForEachAsync only takes an Action<...>, not a Func<..., Task>, so the async lambda your code is passing to ForEachAsync is becoming an async void method. One of the primary reasons async void should be avoided is that it's not easy to determine when the method completes - and in fact, in this case, there is nothing ensuring that it will complete before sending the response.
I recommend doing what Marc suggested and just using foreach:
List<Projet> projets = await projetService.NewProjetLine(GestionaireId, VilleId);
foreach (var p in projects)
{
int villeId = await villeService.getVilleIdByProjetId(p.ProjetId);
Ville ville = await villeService.GetById(villeId);
p.Ville = ville.VilleLabel;
p.GestionnaireProjet = await userServices.GetNameById(p.GestionnaireProjetId ?? 0);
foreach (var sp in p.SousProject)
{
sp.Avancement = await avancementService.GetLabelById(sp.AvancementId);
}
}
model.projets = projets;
Or, if you want to use asynchronous concurrency, you can make use of Task.WhenAll:
List<Projet> projets = await projetService.NewProjetLine(GestionaireId, VilleId);
await Task.WhenAll(projects.Select(async p =>
{
int villeId = await villeService.getVilleIdByProjetId(p.ProjetId);
Ville ville = await villeService.GetById(villeId);
p.Ville = ville.VilleLabel;
p.GestionnaireProjet = await userServices.GetNameById(p.GestionnaireProjetId ?? 0);
await Task.WhenAll(p.SousProject.Select(async sp =>
{
sp.Avancement = await avancementService.GetLabelById(sp.AvancementId);
});
});
model.projets = projets;
Use ForEachAwaitAsync instead of ForEachAsync
Explanation: ForEachAsync can't wait since it is simply a multi-threaded execution of your loop (accepts Action). In fact you might have received a compiler warning by using async for your lambda return, because you intend to assign a Task to a void (unused)
ForEachAwaitAsync will wait because it accepts a Func<Task> and internally also awaits / asyncs accordingly.
For curious minds, you can see the source code here: https://github.com/dotnet/reactive/blob/main/Ix.NET/Source/System.Linq.Async/System/Linq/Operators/ForEach.cs mentioned as ForEachAwaitAsyncCore and ForEachAsync
I am trying to master async method syntax in .NET 4.5. I thought I had understood the examples exactly however no matter what the type of the async method is (ie Task<T>), I always get the same type of error error on the conversion back to T - which I understood was pretty much automatic. The following code produces the error:
Cannot implicitly convert type 'System.Threading.Tasks.Task<System.Collections.Generic.List<int>>' to 'System.Collections.Generic.List<int>'
public List<int> TestGetMethod()
{
return GetIdList(); // compiler error on this line
}
async Task<List<int>> GetIdList()
{
using (HttpClient proxy = new HttpClient())
{
string response = await proxy.GetStringAsync("www.test.com");
List<int> idList = JsonConvert.DeserializeObject<List<int>>();
return idList;
}
}
It fails if I explicitly cast the result as well. This:
public List<int> TestGetMethod()
{
return (List<int>)GetIdList(); // compiler error on this line
}
somewhat predictably results in this error:
Cannot convert type 'System.Threading.Tasks.Task<System.Collections.Generic.List<int>>' to 'System.Collections.Generic.List<int>'
Any help greatly appreciated.
The main issue with your example that you can't implicitly convert Task<T> return types to the base T type. You need to use the Task.Result property. Note that Task.Result will block async code, and should be used carefully.
Try this instead:
public List<int> TestGetMethod()
{
return GetIdList().Result;
}
You need to make TestGetMethod async too and attach await in front of GetIdList(); will unwrap the task to List<int>, So if your helper function is returning Task make sure you have await as you are calling the function async too.
public Task<List<int>> TestGetMethod()
{
return GetIdList();
}
async Task<List<int>> GetIdList()
{
using (HttpClient proxy = new HttpClient())
{
string response = await proxy.GetStringAsync("www.test.com");
List<int> idList = JsonConvert.DeserializeObject<List<int>>();
return idList;
}
}
Another option
public async void TestGetMethod(List<int> results)
{
results = await GetIdList(); // await will unwrap the List<int>
}
Depending on what you're trying to do, you can either block with GetIdList().Result ( generally a bad idea, but it's hard to tell the context) or use a test framework that supports async test methods and have the test method do var results = await GetIdList();
I've just had this issue. I was trying to return a value, but it could not be implicitly cast to a type of Task. The solution was to add the missing async keyword that I forgot to add.
I just had the same issue pop up but the resolution was different than the others. I'm working with two async calls within an async method, this code is what was giving me the error:
var fileContents = reader.ReadToEndAsync();
if (fileContents != null)
{
var profile = await Task.Run(() => JsonConvert.DeserializeObject<T>(fileContents));
return profile;
}
This is the fix. I had forgotten the async on the first line:
var fileContents = await reader.ReadToEndAsync();
if (fileContents != null)
{
var profile = await Task.Run(() => JsonConvert.DeserializeObject<T>(fileContents));
return profile;
}
The error message was showing on "fileContents" on the line var profile = ... So it wasn't immediately obvious of where the error was.
My OperationContract:
public List<MessageDTO> GetMessages()
{
List<MessageDTO> messages = new List<MessageDTO>();
foreach (Message m in _context.Messages.ToList())
{
messages.Add(new MessageDTO()
{
MessageID = m.MessageID,
Content = m.Content,
Date = m.Date,
HasAttachments = m.HasAttachments,
MailingListID = (int)m.MailingListID,
SenderID = (int)m.SenderID,
Subject = m.Subject
});
}
return messages;
}
In Service Reference configuration I checked the option "Generate asynchronous operations". How do I use the generated GetMessagesAsync()? In the net I found examples that use AsyncCallback, however I'm not familiar with that. Is there a way to use it in some friendly way like async and await keywords in .NET 4.5? If not, what should I do to invoke the method asynchronously?
If you select 'Generate asynchrounous operations', you will get the 'old' behavior where you have to use callbacks.
If you want to use the new async/await syntax, you will have to select 'Generate task-based operations' (which is selected by default).
When using the default Wcf template, this will generate the following proxy code:
public System.Threading.Tasks.Task<string> GetDataAsync(int value) {
return base.Channel.GetDataAsync(value);
}
As you can see, there are no more callbacks. Instead a Task<T> is returned.
You can use this proxy in the following way:
public static async Task Foo()
{
using (ServiceReference1.Service1Client client = new ServiceReference1.Service1Client())
{
Task<string> t = client.GetDataAsync(1);
string result = await t;
}
}
You should mark the calling method with async and then use await when calling your service method.
Your Service Reference can (if you are using .Net 4.5) be set to generate task-based async calls. (Configure Service Reference > check Allow generation of asynchronous operations > select Generate task-based operations) These can be used like any async method. Here's an example of how to use it:
using (var proxy = new YourServiceClient())
{
var t1 = proxy.GetMessagesAsync();
var t2 = proxy.GetMessagesAsync();
//they're runnning asynchronously now!
//let's wait for the results:
Task.WaitAll(t1, t2);
var result1 = t1.Result;
var result2 = t2.Result;
Console.WriteLine(result1);
Console.WriteLine(result2);
}
If your client is not using .Net 4.5, you cannot generate service references that use async. You'll have to do it the old fashioned way, using callbacks. Here's an example:
static void m()
{
var proxy = new YourServiceClient();
proxy.GetMessagesCompleted += proxy_GetMessagesCompleted;
proxy.GetMessagesAsync();
}
static void proxy_GetMessagesCompleted(object sender, GetMessagesCompletedEventArgs e)
{
var proxy = (IDisposable)sender;
proxy.Dispose(); //actual code to close properly is more complex
if (e.Error != null)
{
// do something about this
}
var result = e.Result;
Console.WriteLine(result);
}
Note that in actual code for either of these scenarios, you shouldn't use using or IDisposable.Dispose() to clean up the client, see Avoiding Problems with the Using Statement and this code to get you started into the confusing world of closing these things.
If you're on VS2012, then you can use the *Async calls like this:
var proxy = new MyClient();
var result = await proxy.GetMessagesAsync();
How about something like this...
public async Task<string> DoSomething()
{
var someProxy = new ServiceClient();
var t = someProxy.SomeMethodAsync();
await Task.WhenAny(t);
return t.Result;
}