Exercise 5: Testing Workflows

Up to this point, the application is not very interesting. It is only working in a console and does not receive any input arguments. Most applications that do meaningful work will have to deal with input and output arguments. Additionally, the application is not easily tested in its current form.

In this task, you will modify the SayHello activity to use arguments and prepare it to be useful in other non-console application environments by returning a greeting rather than writing directly to the Console with the WriteLine activity. You will be doing this using the "write the test first" approach. First, you will create a test that fails and then add the necessary code to make it pass.

The resulting application will be the equivalent of the following code.

C#

private static string SayHello(string name) { return "Hello " + name + " from Workflow 4"; }

Visual Basic

Private Shared Function SayHello(ByVal name As String) As String Return "Hello " & name & " from Workflow 4" End Function

Task 0 – Opening the Solution

To begin this exercise you can use the solution you finished from Exercise 4. Alternatively, you can follow the following steps to begin with Exercise 5.

  1. Start Microsoft Visual Studio 2010 from Start | All Programs | Microsoft Visual Studio 2010.
  2. Open the starting solution for Exercise 5 located under the Source\Ex5-Testing\Begin folder and use it as the starting point for this exercise.
  3. Press CTRL+SHIFT+B to build the solution.

Task 1 – Creating the Unit Test Project

  1. To begin with, create a unit test for the workflow to verify the behavior. In Solution Explorer, right-click the HelloWorkflow solution and select Add / New Project and set the project options.

    1. Installed Templates: select either Visual C# or Visual Basic then select Test under the language
    2. Template: Test Project
    3. Name: HelloWorkflow.Tests

    Figure 19

    Adding a new Test Project to the solution (C#)

    Figure 20

    Adding a new Test Project to the solution (Visual Basic)

  2. Note:
    The default target version of the .NET Framework in test projects is the .NET Framework 4. However, Visual Studio 2010 SP1 Beta adds basic support for unit tests that target the .NET Framework 3.5. For more information, see https://msdn.microsoft.com/library/gg442059.aspx

  3. Right-click the HelloWorkflow.Tests project and click Add Reference. Using the Projects tab, add a project reference to the HelloWorkflow project. Repeat these steps, using the .NET tab instead to add a reference to the System.Activities library.
  4. Right-click UnitTest1.cs (C#) or UnitTest1.vb (Visual Basic), click Rename and change its name to SayHelloFixture.cs (C#) or SayHelloFixture.vb (Visual Basic). When prompted to rename the UnitTest1 class select Yes.

Task 2 – Creating the Test

In this task you will create the test before you actually implement the behavior in the activity.

  1. Add the following namespace directive to the SayHelloFixture.cs (C#) file or SayHelloFixture.vb (Visual Basic):

    C#

    using System.Activities; using HelloWorkflow;

    Visual Basic

    Imports System.Activities Imports HelloWorkflow

  2. Create a test to make sure that the workflow properly greets you. To do this, open SayHelloFixture.cs (C#) or SayHelloFixture.vb (Visual Basic), locate the TestMethod1 method and rename it to ShouldReturnGreetingWithName.
  3. The SayHello activity does not accept any arguments yet, but you will write the code to invoke it as though it does. This will allow you to think about what the interface to your activity feels like when consuming it. Do not worry if Visual Studio warns you about the UserName property not being defined yet. Replace the implementation as shown

    (Code Snippet - Introduction to WF4Lab - ShouldReturnGreetingImpl CSharp)

    C#

    [TestMethod] public void ShouldReturnGreetingWithName() { IDictionary<string, object> output; output = WorkflowInvoker.Invoke( new SayHello() { UserName = "Test" }); Assert.AreEqual("Hello Test from Workflow 4", output["Greeting"]); }

    (Code Snippet - Introduction to WF4Lab - ShouldReturnGreetingImpl VB)

    Visual Basic

    <TestMethod()> Public Sub ShouldReturnGreetingWithName() Dim output = WorkflowInvoker.Invoke( New SayHello() With {.UserName = "Test"}) Assert.AreEqual("Hello Test from Workflow 4", output("Greeting")) End Sub

    Note:
    How do I pass arguments to an activity?

    You can create the activity and initialize the arguments (which are public properties) using object initialization or you can pass a Dictionary<string, object> (C#) or Dictionary(Of String, Object) (Visual Basic) of input parameters that map to the names of the input arguments of the activity.

    How do I get data from output?

    The output variable is an IDictionary<string, object> (C#) or IDictionary(Of String, Object) (Visual Basic) that contains a map of output variables using the name of the variable as the key.

Task 3 – Getting the Application to Compile

The interface to your Activity (in terms of input and output arguments) looks good but your application doesn’t compile. Your first goal is to get the app into a state where it will compile.

  1. Press CTRL+SHIFT+B to build the application it should fail with a compile error

    Figure 21

    UserName has not been defined yet (C#)

    Figure 22

    UserName has not been defined yet (VB)

  2. Open SayHello.xaml in the designer
  3. Open the arguments pane by clicking on Arguments

    Figure 23

    Click on Arguments to open the arguments pane

  4. Add the UserName and Greeting arguments as shown

    Figure 24

    Declare the arguments to the activity

    Note:
    Arguments

    In Windows Workflow Foundation (WF), arguments represent the flow of data into and out of an activity. An activity has a set of arguments and they make up the signature of the activity. Each argument has a specified direction: input, output, or input/output.

  5. Press CTRL+SHIFT+B to build the solution, it should now build without errors.

Task 4 – Seeing the Test Fail

It is important to see your test fail because it is possible that you have a bug in your test that would cause it to always succeed. In this task, you will insure that your test does indeed fail.

  1. Press CTRL+R,T to run the unit tests in the current context. The test will run but it will fail, because the activity is not returning anything in the Greeting out argument.

    Figure 25

    ShouldReturnGreetingwithName test

Task 5 – Making the Test Pass

Now that you know your test is working, you need to do the simplest possible thing you can to make the test pass.

  1. Writing text to the console with WriteLine isn’t going to make the test pass so delete the WriteLine activity by right-clicking on it and choosing Delete ().
  2. You need to set the value of the Greeting out argument. To do this, drag an Assign activity from the Primitives group in the Toolbox and drop it onto the designer surface.

    Figure 26

    Drag an Assign activity to the design surface

  3. Set the To property to Greeting

    Figure 27

    Type Greeting in the left box

  4. You could type the greeting expression into the design surface but since the expression is longer, use the properties window. Click the button to the right of the Value property to open the expression editor

    Figure 28

    Click the button to the right of the Value property

  5. In the expression editor, you can enter longer expressions, including expressions that use the VB line continuation character “_”. Set the expression to "Hello " & UserName _& " from Workflow 4"

    Figure 29

    The Assign activity that sets the Greeting argument

    Note:
    If you are a C# developer you may not be familiar with the line oriented nature of Visual Basic syntax. If you want an expression to break across lines in Visual Basic you must use an underscore to continue the expression on the next line.

  6. Press CTRL+SHIFT+B to build the solution, it should compile without errors.

Next Step

Exercise 5: Verification