How to Implement TDD in the Development of Automation Solution
As a two-part series, the previous tutorial,Test Driven Development (TDD) - An Approach to Automation, covered the conceptual understanding of using TDD as an approach to developing automation solutions. In this post, we will be focusing on how to turn the business context into a practical implementation.
Prior knowledge of TDD is not a prerequisite for following this post. TDD can be implemented in many different ways and is often adapted to suit the business problem. It is, however, required that a basic understanding of UiPath processes is present. It is important to understand how sequences can be exported or used as workflows which are invoked within the solution.
The following dependencies are used as part of this solution and need to be installed before following the rest of the post:
The information provided in all three test cases, from the tutorial Test Driven Development (TDD) - An Approach to Automation, may be inserted into an Excel document to be used as test data. The Excel spreadsheet would look similar to the file below, named TestData_CalculateBreakeven.xlsx:
The above mentioned test data is stored in a folder named Test Data within the solution folder structure, as seen below:
Unit tests assist in ensuring that reliable code blocks/units are created and adhere to any governance, standards and policies that exist within an automation environment. Read more about unit tests in Test Driven Development (TDD) - An Approach to Automation.
Since this is the first iteration through unit testing, we first need to create the code unit that needs to be tested as well as the test cases that will reference the code unit. Each test will need to have test data associated to it which would need to be imported as well. Start by creating a blank sequence named CalculateBreakeven with the appropriate input and output arguments:
Now that the code unit workflow exists, create a test case for the workflow by navigating to the workflow in the Project pane, right click on the CalculateBreakeven workflow and select Create Test Case:
Three test cases need to be created as per the test case definitions found in this tutorial: Test Driven Development (TDD) - An Approach to Automation. Name the test case appropriately, for example, name the first one TestCase_NegativeValues, the second TestCase_PositiveValues and the third TestCase_ZeroValues:
Next, select the Test Data tab and navigate to the TestData_CalculateBreakeve.xlsx file that was created, containing the test data. Select the data applicable to each test, before clicking Create:
All three tests should now be created and should be visible within the appropriate solution folder structure:
All test data is accessible through the arguments found within the test cases. The data types of the arguments would need to be manipulated in order for the data to be used within the calculations:
The necessary setup activities have been applied and the solution is now adequately prepared for TDD to commence.
Create a Failing Test - Test Case #1: Negative input values
Click Import Arguments under the Invoke CalculateBreakeven workflow activity and assign the variables as applicable. Take note of the two temp variables that have been created, namely TempNumberOfYears and TempNumberOfRuns to store the output of the test and compare it to the expected output provided in the test data file:
Update the CalculateBreakeven.xaml to assign both output variables to -1:
Navigate back to the TestCase_NegativeValues.xaml and add two Verify Expression activities which aim to validate that the output received from the code unit matches what was expected from the test data:
To execute the test case, click Debug File. The two verification expressions are expected to fail, as seen in the Output pane:
Now that the test has been made to fail, the appropriate logic should be added to ensure that the test passes.
Make the Test Pass
The failure indicates that logic needs to be added for the test to pass. From the first test case, it can be seen that the following logic needs to be implemented to accommodate all of the test criteria:
1. Division by 0 exception should be caught and number of years as well as number of runs should be set to 0
2. All other exceptions should be caught with number of years and number of runs set to 0
3. When runs is less than 0, set runs to 0
These can be addressed by adding a try catch into the solution with the calculation encapsulated by the try block and the two exceptions added in the catch block:
The DivideByZeroException block should only contain the logic that sets the number of years and runs to 0:
The Exception block should only contain the logic that sets the number of years and runs to 0:
To make debugging of the test easier, add Write Line activities to show the output received from the code unit:
Execute the test, the following is printed in the Output pane:
This result is expected as NaN is not equal to 0. Logic needs to be added to cater for the NaN. In a case where the number of years is equal to NaN, the number of years and runs should be set to 0:
Executing the test now renders the following results in the Output pane:
UiPath differentiates itself from other tools by allowing developers to customize the verification title and output message format of the activity. This can be done by customizing the following properties:
Change the arguments of the TestCase_NegativeValues.xaml to ensure that all test values will pass.
The next step is to evaluate the code and optimize wherever it may be necessary.
Since the testing of the first test case did not require a lot of logic to be added, there is no need for significant code refactoring. Something that could be improved is the separation of the calculation across multiple variables. Move the calculation logic into one place and assign it to one variable:
Up to this point, the first test case was created with UiPath, the initial test failed, the appropriate logic was added to the code unit to ensure that the test data would successfully return the correct results and once the logic was added, all test data (relating to the negative values test case) were retested.
Next, the second test case needs to be appropriate configured and the same process (from step 4) needs to be followed.
TestCase_PositiveValues.xaml should look as follows:
CalculateBreakeven.xaml should look as follows once the solution has passed the positive values test case successfully:
While testing Testcase_ZeroValues.xaml, one of the sets of test data returns infinity as its output arguments. This renders a new requirement for which logic should be added. Upon further unpacking, it is decided by business that any arithmetic exception should be caught and the output arguments (years and runs) should be set to 0.
TestCase_ZeroValues.xaml should look as follows:
CalculateBreakeven.xaml should look as follows once the solution has passed the zero-values test case successfully:
The final CalculateBreakeven.xaml workflow should look similar to this after any and all refactoring/optimization is complete:
All that is left now is to create a single unit test bringing all of the test cases relating to the Calculate Breakeven code unit together:
To recap, the aim of TDD is to develop units of the solution that can be tested thoroughly and are robust enough to successfully adhere to the requirements outlined by the business problem.
In this tutorial, the business problem was provided at a very high level with specific focus on the functional requirement which aims to solve the business problem. Throughout the testing process, all non-functional requirements were adhered to and applied.
The speed and reliability of the solution were both within the limits posed by business. The requirement was broken down into test cases from which a unit test was created to encapsulate all of the test cases. The benefit of this would be keeping all test case logic pertaining to the singular code unit together. A lean version of this use case is available for download from GitHub.