I have a step class that has steps required to call async API and assert against it. Currently it looks like that:
[Given(#"a setting from API is correct")]
public async void GetSetting()
{
var setting = (await GetSettingsFromApi).Setting;
Assert.IsTrue(setting);
}
There are couple problems here:
I need this step to be performed at the correct time - after the previous and before the following, unsurprisingly. They can be sync or async, but they don't await each other. Making sure it's executed exactly first would also do. Is there a way to achieve it? Beside hardcoding all steps to explicitly await each other?
Assert failure exception is suppressed - it is shown in the in the debugger, but scenario is not stopped and failed as it should? Can I make it pass through and fail the test when the await result is received? This one is actually more important - if test is failed correctly on assert it's doesn't matter if that happens a bit later than expected.
Related
I am using Assembly Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0
I have a [TestClass] which includes only tests concerned with publishing and subscribing to messages. However it's possible that the message broker client that we use is not available on the current environment or for some other reason, which is controller with a falg in the appsettings:
var messageBrokerConfig = configuration.GetSection("MessageBroker").Get<BrokerConfig>();
if (messageBrokerConfig.Enabled)...
this is the way I know if the message broker is available and I can execute the tests or not. Now, a simple solution, that immediatly comes to mind is just to have some (maybe private method) which would be called at the beginning of each test like:
private bool ShouldExecute()
{
return configuration.GetSection("MessageBroker").GetSection("Enabled") == false.ToString()
}
But then I should put this at the beginning of every test which is pretty far from DRY.
In a perfect scenarion I would be able to able/disable the [TestClass] attribute or have any other attribute or something that will prevent all test from executing. At worst, maybe when I invoke the
[ClassInitialize] method. But I don't know the framework very well so I'm not sure what are my options here.
I'm pretty sure there should be a solution which will allows me to put a code at one place and decide for the whole class but I just don't know what it is.
I am trying to use ActionFilters to capture the result in my api call and change it if needed.
But it appears to change only the object i pass not the result type. (from OK to BadRequest)
return Ok(await _Data.Remove());
The above code in my controller leads to my filter:
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var resultContext = await next();
Guid userId = await _data.GetId();
BaseModel baseModel = (BaseModel)((ObjectResult)resultContext.Result).Value;
if (baseModel.ApiResponse == ApiResponse.AllGood)
{
BaseModel m = _data.GetBaseModel(userId);
m.ApiResponse = baseModel.ApiResponse;
resultContext.Result = new OkObjectResult(m);
}
else if (baseModel.ApiResponse == ApiResponse.NotFound)
resultContext.Result = new NotFoundObjectResult(baseModel);
else
resultContext.Result = new BadRequestObjectResult(baseModel);
}
The model is correctly captured and i can check against its contents. And i can change it and pass it back. But it always goes back to the controller and then returns the "ok" with the new model.
What i want is it returning BadRequest
I have tried putting doing this:
resultContext.Result = new BadRequestObjectResult(baseModel);
As you can see from the above code, but it does not work.
The reason you cannot get it to work, is because this approach isn't possible (in the general case, at least).
The async variant of the ActionFilter combines both the pre and post variants of the synchronous variant, in one callback, by giving you next().
Once you have called and successfully returned from next(), you are in 'executed' land, not 'executing' land anymore.
The trouble is, if next() has started sending the response to the client, it is too late to change the response. Your receiving client may already sit with 'http 200 ok' in his hand!
You can observe this, by trying to write to the response in context.HttpContext.Response (You can set the Http Status Code on it, and use WriteAsync on it),
in the spot where you attempt to modify Result.
If next had already started the response, you'll get a direct exception telling you this.
However, IF next() hadn't begun the response, you may actually be able to control the response.
A last-resort option you have, is to throw at this point, it will (mostly) break your http response into http-500 server error. This may or may not be good for your client.
Lastly, your resultContext actually has a 'Cancelled' attribute which you can flag.
However I don't know if that has any effect if the response has already begun (it might affect the middleware server side).
In my own case for this, I concluded it was the wrong place to solve our issue, so I moved our necessary handling to a wrapper in the controller method return statement.
I.e. you then get
return wrapper(OK(response));
where wrapper will decide whether to pass on OK(..) unharmed.
If you use this approach, be careful if you had
return OK(responseWIthSideEffect(..));
in that case, you must make sure the side effect code is forced to execute,
before your wrapper makes its decision (i.e. assuming the wrapper checks something that might depend on that side effect).
I am trying to implement Google SSO in my C# web application. It seemed to be pretty straightforward. Based on this tutorial, Google does its magic in the web browser to get an Id_Token JWT. I pass the token is passed it to my web service for validation and use it to match it to a user in the application.
Although I am not sure if my approach with the server-side Google API is correct yet, my big hangup is trying to figure out how to work with the async call in the Google API to see what I get back.
My code is pretty simple using the Google.Apis.Auth namespace:
public async Task<GoogleJsonWebSignature.Payload> wfValidateAsync(string pId_Token)
{
GoogleJsonWebSignature.Payload Sig = await GoogleJsonWebSignature.ValidateAsync(pId_Token, null, false);
return Sig;
}
Although brand new to this async/await paradigm, I do have some experience in AngularJS / NodeJS promises / callbacks. My challenge is that it seems async methods can only be called by other async methods all the way back up the call-stack. I think it means ending the service call before the async response finishes and the service can act on the result.
Also, for creating unit tests, putting async into the [TestMethod] method makes it completely disappear from the test explorer. I'm not sure how to test/debug this conundrum.
Thanks in advance to anyone who can help me screw my head back on straight with this.
Although brand new to this async/await paradigm, I do have some experience in AngularJS / NodeJS promises / callbacks.
But not use Typescript, right?
My challenge is that it seems async methods can only be called by other async methods all the way back up the call-stack.
They should. Bad things can happen if you don't.
I think it means ending the service call before the async response finishes and the service can act on the result.
No! The compiler generates a state machine for methods with the async modifier and the await keyword means "go do something else and I'll come back here when I'm done".
Also, for creating unit tests, putting async into the [TestMethod] method makes it completely disappear from the test explorer. I'm not sure how to test/debug this conundrum.
You're probably making your test methods async void. They should be async Task in order for the test engine to know when the test is done.
Have a look at Stephen Cleary's blog. He has lots of content on async-await.
Paulo, Thank you!!
I was able to get this working with your advice on the last part about the test method. I had to change this:
//THIS TEST METHOD DOESN'T SHOW IN THE TEST EXPLORER
[TestMethod]
public async void AuthenticateGoogle()
{
string lToken = "[JWT TOKEN HERE]";
wfUser lUser = new wfUser(_wfContext);
var lAuthenticateResult = await lUser.wfAuthenticateGoogle(lToken);
Assert.IsTrue(lAuthenticateResult, "JWT Token Validated");
}
To this:
//THIS TEST METHOD SHOWS IN THE TEST EXPLORER
[TestMethod]
public async Task AuthenticateGoogle()
{
string lToken = "[JWT TOKEN HERE]";
wfUser lUser = new wfUser(_wfContext);
var lAuthenticateResult = await lUser.wfAuthenticateGoogle(lToken);
Assert.IsTrue(lAuthenticateResult, "JWT Token Validated");
}
NOW -- as an additional gotcha that was hanging me up, this will also cause a unit test to disappear from the test explorer, which I found out through lazy copy/pasting a non-test method's definition and mindlessly just adding a return when the build output told me I needed to return a value.
//THIS TEST METHOD DOESN'T SHOW IN THE TEST EXPLORER DUE TO RETURN VALUE
[TestMethod]
public async Task<bool> AuthenticateGoogle()
{
string lToken = "[JWT TOKEN HERE]";
wfUser lUser = new wfUser(_wfContext);
var lAuthenticateResult = await lUser.wfAuthenticateGoogle(lToken);
Assert.IsTrue(lAuthenticateResult, "JWT Token Validated");
return true;
}
In addition to the excellent blog you shared, this article from MSDN Magazine entitled Async Programming : Unit Testing Asynchronous Code helped me get my brain around it too.
What was hanging me up with all of this was mixing synchronous and async methods which, to your point, was not working well. The code seemed to skip the debug points I had set after the await calls as if it never ran them or, if it did run them, ran them somewhere I could not see and couldn't log to the debugger.
It was one of those terrible moments late on a Friday night where a developer starts to question both competence and sanity! :)
Thanks again for the help!
p.s. I incorrectly typed Angular JS in my question and meant Angular 4...and I am using TypeScript there. Some day I'll stop incorrectly referring the newer versions of Angular as AngularJS.
My challenge is that it seems async methods can only be called by other async methods all the way back up the call-stack.
It's not entirely true. You can use async method in sync methods. Of course you are losing most of the 'async' effect, but sometimes you have to. As async methods returs tasks you have some options.
When Task is returning result, any reference to t.Result will block execution till it's known. Another option is to use Task.Wait(preferably with timeout). For example code to validate google jwt token:
public bool ValidateGoogleToken(string token)
{
try
{
if(GoogleJsonWebSignature.ValidateAsync(token).Wait(1000))
return true; //success
//timeout exceeded
}
catch (Exception e)
{
//tampered token
}
return false;
}
I am trying to load a document out of RavenDb via a WebAPI call. When I open an async IDocumentSession and call LoadAsync, I get no exception or result, and the thread exits instantly with no error code.
I was able to bypass all the structure of my API and reproduce the error.
Here is the code that will not work:
public IHttpActionResult GetMyObject(long id)
{
try
{
var session = RavenDbStoreHolderSingleton.Store.OpenAsyncSession();
var myObject= session.LoadAsync<MyObject>("MyObject/1").Result;
return Ok(myObject);
}
catch (Exception e)
{
return InternalServerError(e);
}
}
I simply hard coded the object's Id to 1 for testing, but calling the function for an object that doesn't exist (such as "MyObject/1") has the same result.
However, this code works:
public async Task<IHttpActionResult> GetMyObject(long id)
{
try
{
var session = RavenDbStoreHolderSingleton.Store.OpenAsyncSession();
var myObject= await session.LoadAsync<MyObject>("MyObject/1");
return Ok(myObject);
}
catch (Exception e)
{
return InternalServerError(e);
}
}
Things I tried/fiddled with:
Changing the exceptions that are caught in debugging
Carefully monitoring Raven Studio to see if I could find any problems (I didn't, but I'm not sure I was looking in the right places)
Running the API without the debugger attached to see if the error occurred or if something showed up in Raven Studio (no changes)
So I guess I have stumbled on a "fix", but can someone explain why one of these would fail in such an odd way while the other one would work perfectly fine?
In the real application, the API call did not have the async/await pair, but the code that was making the call was actually using async/await.
Here is the repository class that was failing which caused me to look into this issue:
public async Task<MyObject> Load(string id)
{
return await _session.LoadAsync<MyObject>(id);
}
The first part that is failing is as per design, for ASP.Net async call, you are blocking the Synchronization context, when you call the Result on a Task returned and same Synchronization context is required for call to return the data. Check out the following link by Stephen Cleary, where the same mechanism is explained in detail.
Second part works since that is correct way of using it and it's not getting into the deadlock anymore. First part can only work if you are using the Console application, which doesn't have a synchronization context to block, even other UI like winforms will have a similar issue and need to use the use the Second part of the code
I have a REST API with an Basic Filter. The Filter checks against the DB (mongodb) before the actual request is made, wether the user is authorized. however this only works for the first time, afterwards it always gets stuck at
var filter = Builders<User>.Filter.Eq("email", email);
return _collection.Find(filter).FirstOrDefaultAsync();
which basically looks up the user in the DB based on the Email. The task doesn't finish and neither does it throw a timeout exception .
My question is somewhat related to this : C# Mongo FirstOrDefaultAsync hangs
however their fix didn't do it for me. I can't just keep passing the async tasks up the call hierarchy because at some point the filter expects a sychronous action based on the results. and an await doesnt cut it either.
Any ideas? I can't be the 1st one building a REST API with Basic Auth and a MongoDB behind it...
Help would be appreciated:)
Update:
a call like this also results in problems:
//...
await fbUserRepo.UpdateAsync(user);
//the method
public async Task<Guid> UpdateAsync(TEntity entity) {
entity.lastUpdatedTimestamp = DateTime.Now;
var filter = Builders<TEntity>.Filter.Eq("_id", entity.Id);
await _collection.ReplaceOneAsync(filter, entity);
return entity.Id;
}
when this method is called, nothing happens, nothing is returned.. its just stuck later at a .Wait() ... I haven't changed anything on my configuration and it happens both locally as well as in the azure cloud. performing queries directly on the mongodb is no problem, there are also no currentOp() pending. Also no timeout is thrown. its just like my application decided not to do anything anymore. the thread just stays still
Update again
The odd part is: I can CRUD all sorts of stuff into the DB. Only when I come from my AuthenticationFilter is where I get this trouble. I crawled over a 100k documents from FB and dropped then into my DB, updated them, etc. But fetching and updating my User Documents through my BasicAuthFilter is giving me so much trouble. I just can't explain it... you might notice my frustration (2am now..)
.Result after the sync method will solve your problem.
like given below.
var filter = Builders<User>.Filter.Eq("email", email);
return _collection.Find(filter).FirstOrDefaultAsync().Result;
However make sure, the Method you're working on isn't marked "async" as an async method should not be blocking. More details here.
AFAIK, MongoDB only provides an asynchronous API, which IMO is a correct design decision.
I can't just keep passing the async tasks up the call hierarchy because at some point the filter expects a sychronous action based on the results.
If this is an ASP.NET WebAPI, you can make it async all the way, since WebAPI supports async filters just fine.
However, if your REST API is ASP.NET MVC, then you're in a hard place. The current version of MVC does not support async filters (or child actions). Note that ASP.NET vNext is merging WebAPI with MVC, and the new merged version does support async filters (and child actions). In the meantime, you'll have to do a hacky solution like replacing MyUserAuthenticationAsync().Wait() with Task.Run(() => MyUserAuthenticationAsync()).Wait(). (Note that MyUserAuthenticationAsync will run on a thread outside the HTTP request context).
Hacks like this should be minimized; they're only necessary in MVC filters and child actions. Regular controller actions should be made async and use await instead of Wait. And if this is WebAPI, then just use await in every scenario; don't use any hacks at all.