New Release Microsoft.Activities.UnitTesting 1.71
Just posted a new release of Microsoft.Activities.UnitTesting
This release incorporates the Task Parallel Library to greatly simplify test code.
Episodes
You can now create a task that will run an episode of work in the workflow. An episode is simply a pulse of work that the workflow runs.
For example an episode might look like the following
Thread |
Action |
host |
Run the workflow |
host |
Wait for idle |
Workflow |
Activity 1 |
Workflow |
Activity 2 (creates a bookmark) |
Workflow |
Invoke Idle delegate - host sets event |
host |
episode complete |
Episodes can end in the following ways as defined by the new EpisodeEndedWith enum
Enum |
Description |
Timeout |
The episode did not end within the timeout |
Aborted |
The episode aborted due to an unhandled exception |
Completed |
The episode ended when the workflow completed |
Idle |
The episode ended when the workflow became idle |
Should Idle end an episode?
Ending an episode on idle is tricky because there are some idle events that you might not want to end a workflow. For example async activities such as the Delay activity will cause the workflow to idle. If you are testing a workflow and you want test what happens after the delay you don't want the idle to end the episode. In other cases you might want to wait the second or third idle or for an idle where there is at least one bookmark.
The Async Episode methods offer overloads that allow you to control how an episode ends.
Option |
Description |
Default |
An episode ends when completed or aborted |
Idle Count |
An episode ends when the specified idle count occurs |
Func<WorkflowApplicationTest, bool> |
You provide a function that will be invoked when idle is detected allowing you to determine if the episode should end. The function receives the active WorkflowApplicationTest object which has captured all the event arguments and tracking data as well as the last known bookmark count to help you make your decision |
Async Methods
Method |
Description |
WorkflowApplicationTest.ResumeBookmarkAsync |
returns a Task that will resume a bookmark to run an episode of work. |
WorkflowApplicationTest.RunAsync |
returns a Task that will run an episode of work. |
WorkflowApplicationTest.TestActivityAsync |
returns a Task that will run an episode of work. |
Examples
Use the default to run until complete or abort
/// <summary>
/// Verifies that an episode ended with abort
/// </summary>
[TestMethod]
public void EpisodeShouldEndInAbort()
{
// Arrange
var host = WorkflowApplicationTest.Create(GetSequenceThatAbortsAfterAsync());
// Act
try
{
// Run the activity until it aborts, the activity will go idle once
// because of the TestAsync activity.
Assert.AreEqual(EpisodeEndedWith.Aborted, host.TestActivityAsync().Result.EpisodeResult);
}
finally
{
// Track the tracking records to the test results
host.Tracking.Trace();
}
}
Use the idleCount to run until n number of idle events
/// <summary>
/// Verifies that an episode of work ended with an idle event
/// </summary>
[TestMethod]
public void EpisodeShouldRunToIdleAndThenToCompletedAfterResumeBookmark()
{
// Arrange
var host =
WorkflowApplicationTest.Create(
new Sequence
{
Activities =
{
new WriteLine(), new TestBookmark<int> { BookmarkName = "Bookmark1" }
}
});
try
{
// Act
// Run the activity to the first idle
Assert.AreEqual(EpisodeEndedWith.Idle, host.TestActivityAsync(1).Result.EpisodeResult);
// Resume the bookmark and run the activity to completion
Assert.AreEqual(EpisodeEndedWith.Completed, host.ResumeBookmarkAsync("Bookmark1").Result.EpisodeResult);
}
finally
{
host.Tracking.Trace();
}
}
Use the Func<WorkflowApplicationTes, bool> method to run a workflow until an idle with at least 1 bookmark
/// <summary>
/// Verifies that an episode of work idles, then resumes to completion
/// </summary>
[TestMethod]
public void EpisodeShouldRunToIdleAndThenToCompletedAfterResumeBookmark()
{
// Arrange
var host =
WorkflowApplicationTest.Create(
new Sequence
{
Activities =
{
new WriteLine(), new TestBookmark<int> { BookmarkName = "Bookmark1" }
}
});
try
{
// Act
// Run the activity to the first idle
Assert.AreEqual(EpisodeEndedWith.Idle, host.TestActivityAsync(1).Result.EpisodeResult);
// Resume the bookmark and run the activity to completion
Assert.AreEqual(EpisodeEndedWith.Completed, host.ResumeBookmarkAsync("Bookmark1").Result.EpisodeResult);
}
finally
{
host.Tracking.Trace();
}
}
Comments
Anonymous
January 10, 2011
With Microsoft.Activities.UnitTesting, is there any way to autogenerate the IServiceWithCorrelation (equivalent) interface for a particular xamlx, or does it always have to be recoded up manually every time the service changes?Anonymous
January 11, 2011
IServiceWithCorrelation is just an interface I made up to test a specific workflow service. I used a technique to create a workflow service that implements a contract (similar to the Contract First extensions we showed at PDC) So yes you must update your interface if the associated workflow breaks the contract in some way.Anonymous
January 11, 2011
Thanks Ron. Actually I subsequently realised that I can just add a service reference to my unit test project, and then just use the auto-generated interface within the reference.cs. This is great, as not only does it autogenerate via a single click when the workflow changes, but it avoids me having to write it manually in the first place!