Creating your First Test
In this blog post, I will be using the Calculator application that comes with Windows 10 and create a simple test to add two numbers together and then verify (assert) that the displayed answer is correct.
Add Two Numbers Together
Creating the SpecFlow Scenario
When creating our BDD scenarios, we want to keep them simple and write them in a declarative style, sticking simply to the business logic, rather than writing them in an imperative style. A great article on this can be found at http://itsadeliverything.com/declarative-vs-imperative-gherkin-scenarios-for-cucumber.
- Right-click on the ‘Features’ folder and select ‘Add a New Item’
- In the window that appears, select ‘SpecFlow Feature File’ and create a file called ‘CalculatorScenarios.feature’ or something similarly appropriate
- Open up the ‘CalculatorScenarios.feature’ file and replace any existing text with the following test scenario. We can also add the “@App” SpecFlow tag either above the ‘Scenario’ (to apply to the specific scenario) or above the ‘Feature’ (to apply to all scenarios within the feature, we will apply it to the top of the Feature file in this example)…
@App Feature: Calculator Scenarios As a user of the Calculator application, I want to be able to solve simple Math problems Scenario: 01. Add two numbers together Given the calculator app has been cleared When I add two numbers together Then the displayed result is correct
All these steps we have added to the scenario will be highlighted as still being undefined
Creating the Undefined Step Definitions
- Right-click on any of the undefined steps in your scenario (e.g. the ‘Given the calculator app has been cleared’ step) and select ‘Generate Step Definitions’
- Make sure all the steps are checked and then click on ‘Copy methods to clipboard’
- Open up ‘CalculatorSteps.cs’ class file and paste in all the undefined step definitions, renaming the ‘int’ parameters to something more suitable, so it looks like below…
using TechTalk.SpecFlow; namespace CalculatorWinAutomation.Steps { [Binding] public sealed class CalculatorSteps { [Given(@"the calculator app has been cleared")] public void GivenTheCalculatorAppHasBeenCleared() { ScenarioContext.Current.Pending(); } [When(@"I add two numbers together")] public void WhenIAddTwoNumbersTogether() { ScenarioContext.Current.Pending(); } [Then(@"the displayed result is correct")] public void ThenTheDisplayedResultIsCorrect() { ScenarioContext.Current.Pending(); } } }
Recording the Basic Actions
We will now create test methods in C# for each of our steps (as well as additional ones which may be needed). We will use the CodedUI Test Recorder for all of them, excluding the methods of actually entering the numbers (which we can do very easily after, through basic code)
- Right-click on ‘BaseUIMap.uitest’ in the Solution Explorer and select ‘Edit With Coded UI Test Builder’
- Open up the Calculator application
Clicking the Del button (Test Method)
- Press the ‘Record’ button in the Coded UI Test Builder
- Click the Del button
- Stop recording
- Click the ‘Generate Code’ button on the right side of the test recorder box to generate the code for your test action
- Name your test method something suitable (e.g. ‘ClickDelButton’) and click ‘Add and Generate’
Clicking the Add button (Test Method)
- Enter in a number in the calculator (e.g. 35)
- Press the ‘Record’ button in the Coded UI Test Builder
- Click the Plus (+) button
- Click the stairs icon next to the record button to see the action that was recorded (optional)
- Stop recording
- Click the ‘Generate Code’ button on the right side of the test recorder box to generate the code for your test action
- Name your test method something suitable (e.g. ‘ClickAddButton’) and click ‘Add and Generate’
- Click the Calculator textfield to focus on it again
- Enter in another number (e.g. 15)
Clicking the Equals button (Test Method)
- Press the ‘Record’ button in the Coded UI Test Builder
- Click the Equals (=) button
- Stop recording
- Click the ‘Generate Code’ button on the right side of the test recorder box to generate the code for your test action
- Name your test method something suitable (e.g. ‘ClickEqualsButton’) and click ‘Add and Generate’
Asserting the Result (Assertion Test Method)
- Click and hold on the Assert icon in the Coded UI Test Builder (the one that looks like a circle with a dot in it)
- Whilst still holding the left mouse button down, drag the mouse cursor over the textfield where you have the result of your addition (e.g. 50), and then release the left click
- In the ‘Add Assertions’ window, right-click on the ‘Text’ specific control (see image below) and select ‘Add Assertion’…
- Set the ‘Comparator’ as ‘Contains’ and change the Comparison Value to only contain the result of the addition (see image below)…
- Click ‘OK’
- Click the ‘Generate Code’ button on the right side of the test recorder box to generate the code for your test action
- Name your test method something suitable (e.g. ‘AssertResult’) and click ‘Add and Generate’
- Click the ‘X’ icon to close the test recorder and return to your project
Moving the Test methods into the code behind the UI Map
We will now view all our recorded methods that we added to our UI Map, and move them to the code behind the UI Map. This allows us to edit the code of the test methods as well as allow it to be data driven (Alternatively, you can leave the methods in the UI Map and use the Params property to pass the value to the method, but I will not be covering this as I prefer to use the first method I mentioned of moving the code behind and then editing it with my programming knowledge)
- Right-click on ‘BaseUIMap.uitest’ in the Solution Explorer and select ‘Open’
- Here, you can see two panes, ‘UI Actions’ on the left and ‘UI Control Map’ on the right. The ‘UI Actions’ contains all the methods you recorded as well as any assertions. If you expand a method in the ‘UI Actions’ pane and click on a recorded action in one fo your methods, it will highlight the corresponding UI control in the ‘UI Control Map’ in the right pane
- Right-click on the ‘ClickDelButton’ test method and select ‘Move code to BaseUIMap.cs’
- Click ‘Yes’ to confirm in the dialog that pops up
- Open up ‘BaseUIMap.cs’ and you can see that the test method has been added successfully (see example below)…
namespace ProductAutomation.UI.BaseUIMapClasses //replace ProductAutomation with name of your CodedUI test project { using Microsoft.VisualStudio.TestTools.UITesting.WpfControls; using Mouse = Microsoft.VisualStudio.TestTools.UITesting.Mouse; using System.Drawing; public partial class BaseUIMap { /// <summary> /// ClickDelButton /// </summary> public void ClickDelButton() { // the below three lines are your Variable declarations, which may need to be expanded to be seen #region Variable Declarations WpfButton uIDelButton = this.UIMyCalculatorv1Window.UIDelButton; #endregion // Click 'Del' button Mouse.Click(uIDelButton, new Point(48, 34)); } } }
- Right-click on the ‘ClickAddButton’ test method and select ‘Move code to BaseUIMap.cs’
- Right-click on the ‘ClickEqualsButton’ test method and select ‘Move code to BaseUIMap.cs’
- Right-click on the ‘AssertResult’ test method and select ‘Move code to BaseUIMap.cs’
Coding our Test Methods & Tidying up the Assertion
We will now write code and add a generic method which inputs any number we pass from our SpecFlow scenario step into the textfield. Our method will first have to focus on the textfield (which we already have a method for), so we will simply add a ‘Keyboard.SendKeys’ which will input a text string that we pass to the UI Control that we specify.
If we look back in our UI Map, we can see the ID and Friendly name for the textfield UI control from when we used it in our Assert method…
EnterNumber (Test Method)
- Open up ‘BaseUIMap.cs’ and the following method for entering a number that we pass, into the calculator…
public void EnterNumber(int numberToEnter) { WpfEdit calcTextField = UIMyCalculatorv1Window.UITBEdit; Keyboard.SendKeys(calcTextField, numberToEnter.ToString()); }
In this method, we create a variable for the textField element and then input the value of the ‘numberToEnter’ variable, by converting it to a string
AssertResult (Test Method)
- Carefully remove all the extra ExpectedValues and Params methods associated with the ‘AssertResult()’ method
- You will now have errors in the ‘AssertResult()’ method, but we will fix that next with a bit of refactoring
- Edit the ‘AssertResult()’ method so that it asserts that the text value of the textfield contains the result that we pass to it from our SpecFlow step. Again, we will have to convert the ‘result’ variable from an int to a string (it should look similar to below)…
public void AssertResult(int result) { #region Variable Declarations WpfEdit uITBEdit = this.UIMyCalculatorv1Window.UITBEdit; #endregion // Verify that the 'Text' property of 'tb' text box contains '50' StringAssert.Contains(uITBEdit.Text, result.ToString()); }
Creating Methods our Step Definitions can connect to
AddTwoNumbers() method
- Open up the ‘BaseUIMap.cs’ class file in the UIMap and add the following method which will enter in a number, press the add button, enter in another number and then press the equals button. Because we already have methods for all these individual functions, we can simply make these methods private to the class and call them in our ‘AddTwoNumbers()’ method, like below…
using System.Drawing; using Microsoft.VisualStudio.TestTools.UITesting.WpfControls; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace ProductAutomation.UI { using Keyboard = Microsoft.VisualStudio.TestTools.UITesting.Keyboard; using Mouse = Microsoft.VisualStudio.TestTools.UITesting.Mouse; public partial class BaseUiMap { /// <summary> /// ClickDelButton /// </summary> public void ClickDelButton() { #region Variable Declarations WpfButton uIDelButton = this.UIMyCalculatorv1Window.UIDelButton; #endregion // Click 'Del' button Mouse.Click(uIDelButton, new Point(52, 24)); } public void AddTwoNumbers(int numOne, int numTwo) { EnterNumber(numOne); ClickAddButton(); EnterNumber(numTwo); ClickEqualsButton(); } private void EnterNumber(int numberToEnter) { var calcTextField = UIMyCalculatorv1Window.UITBEdit; Keyboard.SendKeys(calcTextField, "^a" + "{RIGHT}" + numberToEnter.ToString()); } /// <summary> /// ClickAddButton /// </summary> private void ClickAddButton() { #region Variable Declarations WpfButton uIItemButton = this.UIMyCalculatorv1Window.UIItemButton; #endregion // Click '+' button Mouse.Click(uIItemButton, new Point(44, 34)); } /// <summary> /// ClickEqualsButton /// </summary> private void ClickEqualsButton() { #region Variable Declarations WpfButton uIItemButton1 = this.UIMyCalculatorv1Window.UIItemButton1; #endregion // Click '=' button Mouse.Click(uIItemButton1, new Point(78, 40)); } /// <summary> /// AssertResult - Use 'AssertResultExpectedValues' to pass parameters into this method. /// </summary> public void AssertResult(int result) { #region Variable Declarations WpfEdit uITBEdit = this.UIMyCalculatorv1Window.UITBEdit; #endregion // Verify that the 'Text' property of 'tb' text box contains '50' StringAssert.Contains(uITBEdit.Text, result.ToString()); } } }
Defining our Step Definitions
- Open up the ‘CalculatorSteps.cs’ class file again and edit the ‘Given the calculator app has been cleared’ step so it calls the ‘ClickDelButton()’ method in our ‘BaseUIMap.cs’ class, like below…
[Given(@"the calculator app has been cleared")] public void GivenTheCalculatorAppHasBeenCleared() { CalculatorApp.Base.ClickDelButton(); }
- Next, edit the ‘When I add two numbers together’ step so it calls the ‘AddTwoNumbers()’ method in our ‘BaseUIMap.cs’ class, like below…
[When(@"I add two numbers together")] public void WhenIAddTwoNumbersTogether() { CalculatorApp.Base.AddTwoNumbers(35, 15); }
- Finally, edit the ‘Then the displayed result is correct’ step so it calls the ‘AssertResult()’ method in our ‘BaseUIMap.cs’ class, like below…
[Then(@"the displayed result is correct")] public void ThenTheDisplayedResultShouldIsCorrect() { CalculatorApp.Base.AssertResult(50); }
The whole thing should look like below…
using ProductAutomation.UI.UIMapLoader; using TechTalk.SpecFlow; namespace ProductAutomation.Steps { [Binding] public sealed class CalculatorSteps { [Given(@"the calculator app has been cleared")] public void GivenTheCalculatorAppHasBeenCleared() { CalculatorApp.Base.ClickDelButton(); } [When(@"I add two numbers together")] public void WhenIAddTwoNumbersTogether() { CalculatorApp.Base.AddTwoNumbers(35, 15); } [Then(@"the displayed result is correct")] public void ThenTheDisplayedResultShouldIsCorrect() { CalculatorApp.Base.AssertResult(50); } } }
Tidy Up
If you now build the project and try to run the test (by right clicking on it in the Test Explorer in the left pane), you will notice that the test fails on the assertion. This is because when we enter the 2nd number to add, it is getting input at the beginning of the textfield, in front of the existing text, so the textField ends up with a string of “5035+” which is not correct.
One way to fix this would be to do a mouse click of the text field using specific co-ordinates so it adds the 2nd number to the right of the existing string. One problem with this though is that there is always a possibility the axis could change on different desktop computers etc, and also if we wanted to add really large numbers together, our chosen coordinates may not be far right enough.
Instead, a simple solution is to first highlight all existing text in the textField, then press the {RIGHT} arrow to move the text cursor to the right of all existing text 🙂
- Open up the ‘BaseUIMap.cs’ class file and edit the ‘EnterNumber()’ method so it looks like below…
public void EnterNumber(int numberToEnter) { var calcTextField = UIMyCalculatorv1Window.UITBEdit; Keyboard.SendKeys(calcTextField, "^a" + "{RIGHT}" + numberToEnter.ToString()); }
Run the test again in the Test Explorer and it should now pass 😀