I believe that you need to always play with the latest technologies to be great in what you are doing. As I promised in my last article Enhanced Selenium WebDriver Tests with the New Improved C# 6.0, now I am going to share with you how you can utilize the power of the brand-new C# 7.0. I am not going to cover all of the features because I don't like some of them or I don't see how we can use them in the tests. You can find even more cool tips and tricks about automated testing in my WebDriver Series.
Quick Navigation
Digit Separator
Prior C# 7.0
There are lots of times where we use number literals in our code. Because of the many zeroes for me, it is hard to distinguish what is the value of the timeout.
[TestInitialize]
public void SetupTest()
{
driver = new FirefoxDriver();
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromMilliseconds(30000);
}
Another example when we use digits in our tests is when we pass expected values.
[TestMethod]
public void SearchTextInBing_First()
{
var bingMainPage = new BingMainPage(driver);
bingMainPage.Navigate();
bingMainPage.Search("Automate The Planet");
bingMainPage.AssertResultsCount(236000);
}
C# 7.0 Version
You can put digit separator _
wherever you want between digits, to improve readability. They have no effect on the value.
[TestInitialize]
public void SetupTest()
{
driver = new FirefoxDriver();
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(30_000);
}
[TestMethod]
public void SearchTextInBing_First()
{
var bingMainPage = new BingMainPage(driver);
bingMainPage.Navigate();
bingMainPage.Search("Automate The Planet");
bingMainPage.AssertResultsCount(236_000);
}
Local Functions
Prior C# 7.0
There are many times when we create private
methods that are just called in a single method in our class so that we can improve the readability. However, when the class is too long, you need to jump back and forth between different methods to get the big picture. In the example, we pass the purchase order objects to the private
method so that the expected values are formatted in the proper way.
public partial class ShippingAddressPage
{
public void AssertPurchaseOrders(List<PurchaseOrder> purchaseOrders)
{
for (int i = 0; i < purchaseOrders.Count; i++)
{
Assert.AreEqual(GetFormattedPurchaseOrder(purchaseOrders[i]), PurchaseOrderRows[i].Text);
}
}
private string GetFormattedPurchaseOrder(PurchaseOrder purchaseOrder)
{
return $"Your product: {purchaseOrder.ProductName} costs {purchaseOrder.Price}";
}
}
C# 7.0 Version
Now we can declare methods that can be used only in the current block scope (e.g. current method). The previous private
method now is part of the Assert
. I think this improvement makes the Assert
much more readable.
public partial class ShippingAddressPage
{
public void AssertPurchaseOrders(List<PurchaseOrder> purchaseOrders)
{
string GetFormattedPurchaseOrder(PurchaseOrder purchaseOrder) =>
$"Your product: {purchaseOrder.ProductName} costs {purchaseOrder.Price}";
for (int i = 0; i < purchaseOrders.Count; i++)
{
Assert.AreEqual(GetFormattedPurchaseOrder(purchaseOrders[i]), PurchaseOrderRows[i].Text);
}
}
}
Extended Expression Bodied Constructor
Prior C# 7.0
public partial class ShippingAddressPage
{
private readonly IWebDriver driver;
public ShippingAddressPage(IWebDriver browser)
{
this.driver = browser;
}
}
C# 7.0 Version
If the constructor of the page is so simple, we can convert it to just a single line instead of three!
public partial class ShippingAddressPage
{
private readonly IWebDriver driver;
public ShippingAddressPage(IWebDriver driver) => this.driver = driver;
}
Extended Expression Bodied Properties
Prior C# 7.0
Sometimes, it can make sense not to return the whole element instead to make the element's property of type string
. This way, you can only assign and get values. This can improve the API usability (all other WebDriver's properties and methods are restricted). The drawback here is that you need eight lines of code for each getter and setter.
public partial class ShippingAddressPage
{
public string Country
{
get
{
return new SelectElement
(driver.FindElement(By.Name("enterAddressCountryCode"))).SelectedOption.Text;
}
set
{
new SelectElement(driver.FindElement
(By.Name("enterAddressCountryCode"))).SelectByText(value);
}
}
public string FullName
{
get
{
return driver.FindElement(By.Id("enterAddressFullName")).Text;
}
set
{
driver.FindElement(By.Id("enterAddressFullName")).SendKeys(value);
}
}
}
C# 7.0 Version
Now, you can use extended bodied expressions for both the getters and the setters. We reduced the code from eight lines to just two.
public partial class ShippingAddressPage
{
public string Country
{
get => new SelectElement(driver.FindElement
(By.Name("enterAddressCountryCode"))).SelectedOption.Text;
set => new SelectElement(driver.FindElement
(By.Name("enterAddressCountryCode"))).SelectByText(value);
}
public string FullName
{
get => driver.FindElement(By.Id("enterAddressFullName")).Text;
set => driver.FindElement(By.Id("enterAddressFullName")).SendKeys(value);
}
}
Throw Expressions
Prior C# 7.0
We need to guard our code (methods/constructors). I believe that test code is a production code so it should be treated like such. Here, if the driver is null
, we throw an exception.
public partial class ShippingAddressPage
{
private readonly IWebDriver driver;
public ShippingAddressPage(IWebDriver driver)
{
if (driver == null)
{
throw new ArgumentNullException(nameof(driver));
}
this.driver = driver;
}
}
C# 7.0 Version
Now you can throw an exception inside the constructor's extended bodied expression. With the new syntax, we saved seven lines!
public partial class ShippingAddressPage
{
private readonly IWebDriver driver;
public ShippingAddressPage(IWebDriver driver) =>
this.driver = driver ?? throw new ArgumentNullException(nameof(driver));
}
So Far in the 'Pragmatic Automation with WebDriver' Series
References
The post 5 Reasons You Should Use the Brand-New C# 7.0 in Your WebDriver Tests appeared first on Automate The Planet.
CodeProject