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
Wait for idle
Workflow
Activity 1
Activity 2 (creates a bookmark)
Invoke Idle delegate - host sets event
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
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
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
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(); } }