The asynchronous support is not parallel support, your test will still execute serially. This feature is to test asynchronous code.
Testing asynchronous code in Silverlight is based upon the same code and design that is used in the Silverlight Unit Test Framework. The concept is that you queue an item of work into the test framework and the framework processes that queue in a thread safe way. This resolves some issue such as when you make a web request in Silverlight and you need to wait for the response the callback will be marshalled to the UI Thread, if you’re waiting the UI thread in any way for the response the callback will not be able to be marshalled to the UI thread effectively creating a deadlock. The async support allows your tests to give control back to the runtime to allow other operations to execute before receiving the callback.
- Add the Silverlight Unit Test project
- Add a reference to the Specflow Silverlight Runtime TechTalk.SpecFlow.Silverlight3.dll
- First we create a class that will perform an action in an asynchronous manor.
Here we emulate a timely process that is performed on another thread. If you were to write a normal test that calls the Calculate method and then asserts that the Result is 120 you would find that the assertion is performed before the result is updated by the workerThread because the MainThread (UI Thread) continues once it starts the workerThread. To correctly test this we need to wait until the workerThread is finished.
The Silverlight Unit Test Framework Solution
To get around this issue in the Silverlight unit test framework we create a test that looks like this.
We inherit the Test class from the SilverlightTest which contains all the necessary information to enqueue an action. We also need to tell the Test method that it should expect an async action by adding the Asyncronous attribute. We first queue up any async actions by using the EnqueCallback method. The runtime will attempt to execute all code up until the Enque methods. The queued actions get added to a queue and are executed after it leaves the test method. Because of this we also need to enqueue any action that should be run after the async call.
The execution path ends up looking like this
As you can see the Silverlight Unit Test Framework first Queues step 1, 2, 3 and 4 first before execution steps 1’s delegate (step 5) it then sets up the new thread and then starts it which executes step 9 and 10. Once this is complete the condition we queued in step 2 returns true so that it continues executing the actions we queued at 3 and 4.
This affectively means that it waits until a condition is met before asserting the results. This condition could be a callback or it could be just a simple check like we are doing to make sure a value has been set. Of course just checking it result is > 0 is not a good condition in case the result of the async process returns 0 but we are just trying to demonstrate the process.
The problem when using Specflow’s approach to testing
The problem with the above solution is to do with the way Specflow separates its Tests into Step Definitions. The solution above requires us to inherit the Test class from SilverlightTest but we can’t do this because the Test class is generated from our feature file (which is wired up in the code behind of your feature)
In addition to this the Silverlight Unit test has no indication of what a StepDefinition is. Executing the EnqueueTestComplete will mean that the entire test has been complete not just the step definition.
The Specflow solution
Specflow builds on top of the async behaviour of the Silverlight Unit Test framework. Each specflow test is run in its own Async Context, or Work Item as known in the Silverlight Unit Test Framework. This gives control back to the Silverlight runtime to be able to process other operations before receiving a callback.
When you want to Enqueue an action you call the Enque methods on the Specflow AsyncContext singleton similar to the way we did it in the Silverlight Unit Test Framework
Add the mstest.silverlight4.async provider to the config file.
The specflow usage is basically the same as the Silverlight Unit Test Framework except we don’t inherit from the SilverlightTest or add the Asynchronous attribute to our step definitions class. Specflow handles this in the generated code behind of your feature file.
As you can see the usage of the Enqueue methods are the same as the Silverlight Unit Test Framework except you do not need to call EnqueTestComplete(). The Important thing to note here is once you Enqueue an action anything that relies on that action must also be enqueued but only within the step definition. This is different from the Silverlight framework where you would need to enqueue all additional actions in the entire test. Note that the Assert in the Then method does not need to be enqueued but it will still wait on the actions of the previous test. The reason for this is because each step in a Specflow feature is enqueued so the next step will not execute until all enqueued actions of the previous step has been dequeued and executed.
Here is a diagram of how Specflow executes a Test so that the step definitions are executed in an asynchronous safe way.