Repository navigation
feat!: Invoke async message scenario factories on request - #536
adamrodger wants to merge 1 commit into
Conversation
We still have to invoke the factory with sync-over-async at request time and this puts a burden on the user not to use this improperly, but if we want to delay invocation of the factory until after provider states have been configured then this is the first step. This is also obviously a breaking API change and requires a major version bump. `WithAsyncContent` makes way more sense because it's the content factory that's async, not the call to the builder as `WithContentAsync` would imply. This also means the return type changes to void instead of Task. See #459
|
One problem with making the messaging provider async is that you end up with this signature on /// <summary>
/// Add a message scenario
/// </summary>
/// <param name="description">Scenario description</param>
/// <param name="factory">Message content factory</param>
IMessageScenarios Add(string description, Func<dynamic> factory);
/// <summary>
/// Add a message scenario
/// </summary>
/// <param name="description">Scenario description</param>
/// <param name="factory">Message content factory</param>
IMessageScenarios Add(string description, Func<Task<dynamic>> factory);When you invoke that then it causes an issue: // sync invocation
Scenarios.Add("foo", () => new Foo());
// async invocations
Scenarios.Add("foo", () => Task.FromResult(new Foo())); // hmmmm
Scenarios.Add("foo", async () => await CreateFooAsync()); // also hmmmmBecause of the use of |
|
My initial version does get all kinds of weird compiler errors in the tests, and those highlight ways in which this would remove some existing useful functionality. For example, this compiles: // anonymous type returned from a factory
Func<dynamic> factory = () => new { Foo = 42 };but this doesn't: // try so use anonymous type in an async factory
Func<Task<dynamic>> factory = () => Task.FromResult(new { Foo = 42 });You aren't allowed to use anonymous types in that scenario, and lots of people use anonymous types when defining their pacts. |
We still have to invoke the factory with sync-over-async at request time and this puts a burden on the user not to use this improperly, but if we want to delay invocation of the factory until after provider states have been configured then this is the first step.
This is also obviously a breaking API change and requires a major version bump.
WithAsyncContentmakes way more sense because it's the content factory that's async, not the call to the builder asWithContentAsyncwould imply. This also means the return type changes to void instead of Task.See #459
We should also investigate making the internal messaging server itself async so that we don't have to do the sync-over-async shenanigans, which could maybe be achieved with
Task.Run(or similar) instead of using a dedicated thread. We'd probably then also change the signature ofScenario.Factoryto beFunc<Task<dynamic>>instead, and the sync versions would wrap inTask.FromResult.We do get really weird behaviour when using
dynamicinsideFunc<>though, so this has to be carefully investigated, and may not be possible.