How to write Nunit Parameterized Test
By Gurudatt S A, Community Contributor - October 2, 2022
Nunit is a very popular unit testing framework for all .net languages. Nunit provides parametirization for its tests using which we can reuse the test for various data sets.
Nunit provides four different ways to pass paramertization to the test as seen below:
Let’s deep dive into each of these parameterization methods to make our tests more reusable and powerful.
Parameterization techniques in NUnit
There are Four ways to group the parameter and can be passed to the test in Nunit
- Inline – As Test Case Attribute
- Inline – As Random Attribute, Range Attribute, Values Attribute
- Separate – TestCaseSource Attribute
- Separate – ValueSource Attribute
Inline – As Test Case Attribute
In this technique, Nunit uses the TestCase attribute to pass the various data sets at Test level. This technique is helpful when we want to repeat complete test for various data sets. For the examples we’ll be using BrowserStack’s demo application
Attribute syntax is
[TestCase(“input1”, “input2”)]
For example,
using NUnit.Framework; using OpenQA.Selenium; using OpenQA.Selenium.Chrome; using System.IO; using System; using OpenQA.Selenium.Support.UI; namespace NunitSeleniumDemo { public class Tests { private IWebDriver driver; [SetUp] public void Setup() { String path = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..\\..\\..\\")); driver = new ChromeDriver(path + "\\drivers"); driver.Manage().Window.Maximize(); driver.Url = "https://bstackdemo.com/"; } [TestCase("OnePlus", "6 Product(s) found.")] [TestCase("Google", "3 Product(s) found.")] public void NunitSeleniumTest(String model, String message) { DefaultWait<IWebDriver> fluentWait = new DefaultWait<IWebDriver>(driver); fluentWait.Timeout = TimeSpan.FromSeconds(5); fluentWait.PollingInterval = TimeSpan.FromMilliseconds(250); fluentWait.Until(driver => driver.Title == "StackDemo"); driver.FindElement(By.XPath($"//*[@class='checkmark' and contains(text(),'{model}')]")).Click(); driver.Manage().Timeouts().ImplicitWait = (TimeSpan.FromSeconds(6)); String searchResults = driver.FindElement(By.XPath("//*[@class='products-found']//span")).Text; Assert.AreEqual(searchResults, $"{message}"); driver.FindElement(By.XPath($"//*[@class='checkmark' and contains(text(),'{model}')]")).Click(); } [TearDown] public void tearDown() { driver.Close(); driver.Quit(); } } }
In this example, reusing the same test for validating the search result after clicking a specific phone model.
As observed, first declaring the Attribute with the values you need to pass at test level
[TestCase("OnePlus", "6 Product(s) found.")] [TestCase("Google", "3 Product(s) found.")]
We receive these values as input parameters for our test method when Nunit runs, hence we declare two parameters for our test method
public void NunitSeleniumTest(String model, String message)
And now you can use these parameters in our code where the code clicks on the model and verifies the message
driver.FindElement(By.XPath($"//*[@class='checkmark' and contains(text(),'{model}')]")).Click(); driver.Manage().Timeouts().ImplicitWait = (TimeSpan.FromSeconds(6)); String searchResults = driver.FindElement(By.XPath("//*[@class='products-found']//span")).Text; Assert.AreEqual(searchResults, $"{message}"); driver.FindElement(By.XPath($"//*[@class='checkmark' and contains(text(),'{model}')]")).Click();
In this way, you can reuse our test for various datasets.
Inline – As Random Attribute, Range Attribute, Values Attribute
Range Attribute
In this technique, Nunit allows two array parameters, which can be passed to the test method.
First parameter is the Values and the Second is the Range. Test repeats for all the Values provided for the range provided.
This can be used when you want to test various ranges for a given Value.
In the below example, the value is a selector, and the quantity is the range. With the given selector, test adds the Phone to the cart and validates the cart with the quantity.
using NUnit.Framework; using OpenQA.Selenium; using OpenQA.Selenium.Chrome; using System.IO; using System; using OpenQA.Selenium.Support.UI; namespace NunitSeleniumDemo { public class InlineDemo { private IWebDriver driver; [OneTimeSetUp] public void Setup() { String path = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..\\..\\..\\")); driver = new ChromeDriver(path + "\\drivers"); driver.Manage().Window.Maximize(); driver.Url = "https://bstackdemo.com/"; } [Test] public void checkQuantity([Values(".shelf-container")] String selector, [Range(2,3,1)] int value) { DefaultWait<IWebDriver> fluentWait = new DefaultWait<IWebDriver>(driver); fluentWait.Timeout = TimeSpan.FromSeconds(5); fluentWait.PollingInterval = TimeSpan.FromMilliseconds(250); fluentWait.IgnoreExceptionTypes(typeof(FormatException)); fluentWait.Until(driver => driver.Title == "StackDemo"); driver.FindElement(By.CssSelector($"{selector} .shelf-item:nth-of-type({value}) .shelf-item__buy-btn")).Click(); driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(120); fluentWait.Until(driver => Int32.Parse(driver.FindElement(By.CssSelector(".float-cart__header .bag__quantity")).Text) == (value - 1)); int cartQty = Int32.Parse( driver.FindElement(By.CssSelector(".float-cart__header .bag__quantity")).Text); Assert.AreEqual(cartQty, (value-1)); } [OneTimeTearDown] public void tearDown() { driver.Close(); driver.Quit(); } } }
Values Attribute
In this technique, Nunit allows two Values array parameters to be passed to the test. For every first value parameter, the Values in the Second parameter will be repeated.
Executing the below example
[Test] public void inlineValueTest([Values(1,2)] int value1, [Values(2, 3)] int value2) { Console.WriteLine("Value 1 is : " + value2); Console.WriteLine("Value 2 is : " + value2); }
The tests will be divided into
Random Attribute
In this technique, the Nunit allows you to pass Random Attribute Array with 1 to 3 parameters
Syntax:
Random(int count) Random(int minNumber, int MaxNumber, int count)
In the below example, we are specifying the Random number to be in range from 0 to 10 and repeat the test for every value 3 times
Executing the below example
[Test] public void inlineValueTest([Values(1,2)] int value1, [Random(0,10, 3)] int value2) { Console.WriteLine("Value 1 is : " + value2); Console.WriteLine("Value 2 is : " + value2); }
Nunit generates test with below combinations
Separate – TestCaseSource Attribute
In this technique, Nunit allows passing the data from different sources using Attribute [TestCaseSource]. This will allow us to keep the test data away from the test and can be used to read the data from External sources like a JSON file or Database.
[TestCaseSource(nameof(UserData))] public void testReadingFromDataSource(String userName, String password) { Console.WriteLine("Value 1 is : " + userName); Console.WriteLine("Value 2 is : " + password); } static object[] UserData = { new object[] { "username1", "passsword1" }, new object[] { "username2", "passsword2" } };
As we can see our test data is coming from a method called UserData which returns an array of objects. Nunit will run the test for every returning object array
Separate – ValueSource Attribute
In this technique, Nunit allows us to pass named data source at test method parameter level using attribute [ValueSource].
[Test] public void testReadingFromDataSource([ValueSource("UserData")] Object[] userObject) { Console.WriteLine("userName is : " + userObject[0]); Console.WriteLine("password is : " + userObject[1]); } static object[] UserData = { new object[] { "username1", "passsword1" }, new object[] { "username2", "passsword2" } };
As observed, the userData object now is applied to the test method’s parameter. Nunit will generate tests like below
Conclusion
Parameterization is a powerful way of implementing our tests in a flexible way and can be reused for various data sets if designed properly. This will reduce the repetitive tests and also allows us to Maintain the tests keeping test data tidy. With TestCaseSource we can easily keep the test data outside the test which will make the test reusable against different test environments where test data might change.