Data-Driven Testing Examples
Data-driven testing is a powerful approach that allows you to execute the same test logic with multiple sets of input data. This section provides comprehensive examples for implementing data-driven testing patterns with the Ranorex API.
Overview
Data-driven testing separates test logic from test data, enabling:
- Scalability: Test the same functionality with hundreds of data sets
- Maintainability: Update test data without modifying code
- Coverage: Test edge cases and boundary conditions systematically
- Efficiency: Reduce code duplication and maintenance overhead
Table of Contents
Database-Driven
Connect to databases to retrieve test data dynamically.
SQL Server Integration
using System;
using System.Data;
using System.Data.SqlClient;
using Ranorex;
using Ranorex.Core;
public class DatabaseDrivenTest
{
private string connectionString = "Server=localhost;Database=TestData;Integrated Security=true;";
public void RunDatabaseDrivenTest()
{
Host.Initialize();
try
{
var testData = GetTestDataFromDatabase();
foreach (DataRow row in testData.Rows)
{
ExecuteTestCase(
username: row["Username"].ToString(),
password: row["Password"].ToString(),
expectedResult: row["ExpectedResult"].ToString()
);
}
}
catch (Exception ex)
{
Report.Failure("Database Test", $"Failed: {ex.Message}");
throw;
}
finally
{
Host.Shutdown();
}
}
private DataTable GetTestDataFromDatabase()
{
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
var command = new SqlCommand(@"
SELECT Username, Password, ExpectedResult
FROM LoginTestData
WHERE IsActive = 1", connection);
var adapter = new SqlDataAdapter(command);
var dataTable = new DataTable();
adapter.Fill(dataTable);
Report.Info("Database", $"Retrieved {dataTable.Rows.Count} test cases");
return dataTable;
}
}
private void ExecuteTestCase(string username, string password, string expectedResult)
{
try
{
// Perform login action
var repo = MyAppRepository.Instance;
repo.LoginDialog.UsernameField.PressKeys(username);
repo.LoginDialog.PasswordField.PressKeys(password);
repo.LoginDialog.LoginButton.Click();
// Validate result
if (expectedResult == "Success")
{
Validate.IsTrue(repo.Dashboard.WelcomeMessage.Visible,
$"Login should succeed for user: {username}");
Report.Success("Login Test", $"User {username} logged in successfully");
}
else
{
Validate.IsTrue(repo.LoginDialog.ErrorMessage.Visible,
$"Login should fail for user: {username}");
Report.Success("Login Test", $"Login correctly failed for user: {username}");
}
}
catch (Exception ex)
{
Report.Failure("Login Test", $"Test failed for user {username}: {ex.Message}");
}
}
}
MySQL Integration
using MySql.Data.MySqlClient;
using System.Collections.Generic;
public class MySqlDataProvider
{
private string connectionString = "Server=localhost;Database=testdb;Uid=testuser;Pwd=password;";
public List<TestCase> GetTestCases(string testSuite)
{
var testCases = new List<TestCase>();
using (var connection = new MySqlConnection(connectionString))
{
connection.Open();
var command = new MySqlCommand(@"
SELECT tc.TestCaseId, tc.TestName, tc.InputData, tc.ExpectedOutput
FROM TestCases tc
INNER JOIN TestSuites ts ON tc.TestSuiteId = ts.TestSuiteId
WHERE ts.SuiteName = @suiteName AND tc.IsEnabled = 1
ORDER BY tc.ExecutionOrder", connection);
command.Parameters.AddWithValue("@suiteName", testSuite);
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
testCases.Add(new TestCase
{
Id = reader.GetInt32("TestCaseId"),
Name = reader.GetString("TestName"),
InputData = reader.GetString("InputData"),
ExpectedOutput = reader.GetString("ExpectedOutput")
});
}
}
}
Report.Info("MySQL Data", $"Loaded {testCases.Count} test cases for suite: {testSuite}");
return testCases;
}
}
public class TestCase
{
public int Id { get; set; }
public string Name { get; set; }
public string InputData { get; set; }
public string ExpectedOutput { get; set; }
}
File Processing
Process various file formats for test data input.
Excel Data Integration
using System;
using System.Data;
using System.IO;
using ExcelDataReader;
using Ranorex;
public class ExcelDataDrivenTest
{
public void RunExcelDrivenTest(string excelFilePath)
{
Host.Initialize();
try
{
var testData = ReadExcelData(excelFilePath);
foreach (DataRow row in testData.Rows)
{
var testCase = new
{
TestName = row["TestName"].ToString(),
ProductName = row["ProductName"].ToString(),
Quantity = Convert.ToInt32(row["Quantity"]),
Price = Convert.ToDecimal(row["Price"]),
ExpectedTotal = Convert.ToDecimal(row["ExpectedTotal"])
};
ExecuteShoppingCartTest(testCase.ProductName, testCase.Quantity,
testCase.Price, testCase.ExpectedTotal);
}
}
catch (Exception ex)
{
Report.Failure("Excel Test", $"Failed: {ex.Message}");
throw;
}
finally
{
Host.Shutdown();
}
}
private DataTable ReadExcelData(string filePath)
{
using (var stream = File.Open(filePath, FileMode.Open, FileAccess.Read))
{
using (var reader = ExcelReaderFactory.CreateReader(stream))
{
var result = reader.AsDataSet(new ExcelDataSetConfiguration()
{
ConfigureDataTable = (_) => new ExcelDataTableConfiguration()
{
UseHeaderRow = true
}
});
return result.Tables[0];
}
}
}
private void ExecuteShoppingCartTest(string productName, int quantity,
decimal price, decimal expectedTotal)
{
try
{
var repo = ECommerceRepository.Instance;
// Add product to cart
repo.ProductPage.SearchField.PressKeys(productName);
repo.ProductPage.SearchButton.Click();
repo.ProductPage.ProductItem.Click();
// Set quantity
repo.ProductDetails.QuantityField.PressKeys(quantity.ToString());
repo.ProductDetails.AddToCartButton.Click();
// Verify total
var actualTotal = decimal.Parse(repo.Cart.TotalAmount.Text.Replace("$", ""));
Validate.AreEqual(expectedTotal, actualTotal,
$"Total should be {expectedTotal:C} for {quantity} x {productName}");
Report.Success("Shopping Cart",
$"Product {productName} test passed: {quantity} x {price:C} = {actualTotal:C}");
}
catch (Exception ex)
{
Report.Failure("Shopping Cart", $"Test failed for {productName}: {ex.Message}");
}
}
}
CSV Data Processing
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Ranorex;
public class CsvDataProvider
{
public IEnumerable<UserTestData> ReadUserData(string csvFilePath)
{
var users = new List<UserTestData>();
using (var reader = new StreamReader(csvFilePath))
{
// Skip header row
reader.ReadLine();
string line;
while ((line = reader.ReadLine()) != null)
{
var values = ParseCsvLine(line);
if (values.Length >= 6)
{
users.Add(new UserTestData
{
FirstName = values[0],
LastName = values[1],
Email = values[2],
Phone = values[3],
UserType = values[4],
ExpectedValidation = bool.Parse(values[5])
});
}
}
}
Report.Info("CSV Data", $"Loaded {users.Count} user records from {csvFilePath}");
return users;
}
private string[] ParseCsvLine(string line)
{
var result = new List<string>();
bool inQuotes = false;
string currentField = "";
for (int i = 0; i < line.Length; i++)
{
char c = line[i];
if (c == '"')
{
inQuotes = !inQuotes;
}
else if (c == ',' && !inQuotes)
{
result.Add(currentField);
currentField = "";
}
else
{
currentField += c;
}
}
result.Add(currentField);
return result.ToArray();
}
}
public class UserTestData
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string UserType { get; set; }
public bool ExpectedValidation { get; set; }
}
JSON Data Integration
using System;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
using Ranorex;
public class JsonDataDrivenTest
{
public void RunJsonDrivenTest(string jsonFilePath)
{
Host.Initialize();
try
{
var testScenarios = LoadTestScenarios(jsonFilePath);
foreach (var scenario in testScenarios)
{
Report.Info("Test Scenario", $"Executing: {scenario.Name}");
foreach (var step in scenario.Steps)
{
ExecuteTestStep(step);
}
ValidateScenarioResult(scenario);
}
}
catch (Exception ex)
{
Report.Failure("JSON Test", $"Failed: {ex.Message}");
throw;
}
finally
{
Host.Shutdown();
}
}
private List<TestScenario> LoadTestScenarios(string filePath)
{
var json = File.ReadAllText(filePath);
var data = JsonConvert.DeserializeObject<TestData>(json);
Report.Info("JSON Data", $"Loaded {data.Scenarios.Count} test scenarios");
return data.Scenarios;
}
private void ExecuteTestStep(TestStep step)
{
switch (step.Action.ToLower())
{
case "click":
ExecuteClickAction(step);
break;
case "type":
ExecuteTypeAction(step);
break;
case "wait":
ExecuteWaitAction(step);
break;
case "validate":
ExecuteValidateAction(step);
break;
default:
throw new InvalidOperationException($"Unknown action: {step.Action}");
}
}
private void ExecuteClickAction(TestStep step)
{
var element = Host.Local.FindSingle(step.Target);
element.Click();
Report.Info("Action", $"Clicked: {step.Target}");
}
private void ExecuteTypeAction(TestStep step)
{
var element = Host.Local.FindSingle(step.Target);
element.PressKeys(step.Data);
Report.Info("Action", $"Typed '{step.Data}' into: {step.Target}");
}
private void ExecuteWaitAction(TestStep step)
{
var timeout = TimeSpan.FromMilliseconds(int.Parse(step.Data));
System.Threading.Thread.Sleep(timeout);
Report.Info("Action", $"Waited: {step.Data}ms");
}
private void ExecuteValidateAction(TestStep step)
{
var element = Host.Local.FindSingle(step.Target);
var actualValue = element.GetAttributeValueText(step.Attribute ?? "Text");
Validate.AreEqual(step.ExpectedValue, actualValue,
$"Validation failed for {step.Target}");
Report.Success("Validation",
$"Validated {step.Target}: Expected='{step.ExpectedValue}', Actual='{actualValue}'");
}
private void ValidateScenarioResult(TestScenario scenario)
{
foreach (var validation in scenario.Validations)
{
ExecuteValidateAction(validation);
}
}
}
public class TestData
{
public List<TestScenario> Scenarios { get; set; }
}
public class TestScenario
{
public string Name { get; set; }
public string Description { get; set; }
public List<TestStep> Steps { get; set; }
public List<TestStep> Validations { get; set; }
}
public class TestStep
{
public string Action { get; set; }
public string Target { get; set; }
public string Data { get; set; }
public string Attribute { get; set; }
public string ExpectedValue { get; set; }
}
Configuration-Based Testing
Use configuration files to drive test execution parameters.
XML Configuration
using System;
using System.Xml.Linq;
using System.Linq;
using Ranorex;
public class XmlConfigDrivenTest
{
public void RunConfigDrivenTest(string configPath)
{
Host.Initialize();
try
{
var config = LoadConfiguration(configPath);
foreach (var testGroup in config.TestGroups)
{
Report.Info("Test Group", $"Executing: {testGroup.Name}");
foreach (var test in testGroup.Tests)
{
ExecuteConfiguredTest(test, testGroup.Settings);
}
}
}
catch (Exception ex)
{
Report.Failure("Config Test", $"Failed: {ex.Message}");
throw;
}
finally
{
Host.Shutdown();
}
}
private TestConfiguration LoadConfiguration(string configPath)
{
var doc = XDocument.Load(configPath);
var config = new TestConfiguration();
config.TestGroups = doc.Root.Elements("TestGroup")
.Select(group => new TestGroup
{
Name = group.Attribute("name").Value,
Settings = group.Elements("Setting").ToDictionary(
s => s.Attribute("key").Value,
s => s.Attribute("value").Value
),
Tests = group.Elements("Test").Select(test => new ConfiguredTest
{
Name = test.Attribute("name").Value,
Type = test.Attribute("type").Value,
Parameters = test.Elements("Parameter").ToDictionary(
p => p.Attribute("name").Value,
p => p.Attribute("value").Value
)
}).ToList()
}).ToList();
return config;
}
private void ExecuteConfiguredTest(ConfiguredTest test, Dictionary<string, string> settings)
{
switch (test.Type.ToLower())
{
case "login":
ExecuteLoginTest(test.Parameters, settings);
break;
case "form":
ExecuteFormTest(test.Parameters, settings);
break;
case "navigation":
ExecuteNavigationTest(test.Parameters, settings);
break;
default:
throw new InvalidOperationException($"Unknown test type: {test.Type}");
}
}
}
public class TestConfiguration
{
public List<TestGroup> TestGroups { get; set; } = new List<TestGroup>();
}
public class TestGroup
{
public string Name { get; set; }
public Dictionary<string, string> Settings { get; set; } = new Dictionary<string, string>();
public List<ConfiguredTest> Tests { get; set; } = new List<ConfiguredTest>();
}
public class ConfiguredTest
{
public string Name { get; set; }
public string Type { get; set; }
public Dictionary<string, string> Parameters { get; set; } = new Dictionary<string, string>();
}
API Data Integration
Retrieve test data from REST APIs for dynamic testing scenarios.
REST API Data Provider
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Ranorex;
public class ApiDataDrivenTest
{
private readonly HttpClient httpClient = new HttpClient();
public async Task RunApiDrivenTestAsync()
{
Host.Initialize();
try
{
var testData = await GetTestDataFromApiAsync();
foreach (var data in testData)
{
await ExecuteApiTestCaseAsync(data);
}
}
catch (Exception ex)
{
Report.Failure("API Test", $"Failed: {ex.Message}");
throw;
}
finally
{
Host.Shutdown();
httpClient.Dispose();
}
}
private async Task<List<ApiTestData>> GetTestDataFromApiAsync()
{
var response = await httpClient.GetAsync("https://api.example.com/testdata");
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
var testData = JsonConvert.DeserializeObject<List<ApiTestData>>(json);
Report.Info("API Data", $"Retrieved {testData.Count} test cases from API");
return testData;
}
private async Task ExecuteApiTestCaseAsync(ApiTestData testData)
{
try
{
// Execute the test case using the API data
var repo = MyAppRepository.Instance;
// Navigate to the test page
repo.Application.NavigateToUrl(testData.TargetUrl);
// Fill form with API data
foreach (var field in testData.FormFields)
{
var element = repo.Form.FindChild(field.Key);
element.PressKeys(field.Value);
}
// Submit and validate
repo.Form.SubmitButton.Click();
// Validate against expected API response
var validationResult = await ValidateAgainstApiAsync(testData.ValidationEndpoint, testData.Id);
if (validationResult)
{
Report.Success("API Test", $"Test case {testData.Id} passed validation");
}
else
{
Report.Failure("API Test", $"Test case {testData.Id} failed validation");
}
}
catch (Exception ex)
{
Report.Failure("API Test", $"Test case {testData.Id} failed: {ex.Message}");
}
}
private async Task<bool> ValidateAgainstApiAsync(string endpoint, string testId)
{
var response = await httpClient.GetAsync($"{endpoint}?testId={testId}");
if (!response.IsSuccessStatusCode)
return false;
var json = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<ValidationResult>(json);
return result.IsValid;
}
}
public class ApiTestData
{
public string Id { get; set; }
public string TargetUrl { get; set; }
public Dictionary<string, string> FormFields { get; set; }
public string ValidationEndpoint { get; set; }
}
public class ValidationResult
{
public bool IsValid { get; set; }
public string Message { get; set; }
}
Best Practices
Data Management
public class DataDrivenBestPractices
{
// 1. Use data abstraction layers
public interface ITestDataProvider
{
IEnumerable<TestCaseData> GetTestData(string source);
void UpdateTestResult(string testId, TestResult result);
}
// 2. Implement proper error handling
public void SafeDataRetrieval()
{
try
{
var data = GetTestData();
// Process data
}
catch (DataException ex)
{
Report.Error("Data", $"Data retrieval failed: {ex.Message}");
// Fallback to static data or skip test
}
}
// 3. Use parameterized queries for security
public DataTable GetSecureData(string userId)
{
var query = "SELECT * FROM TestData WHERE UserId = @userId";
// Use parameterized query implementation
return ExecuteParameterizedQuery(query, new { userId });
}
// 4. Implement data validation
public bool ValidateTestData(TestCaseData data)
{
if (string.IsNullOrEmpty(data.TestName))
{
Report.Warn("Data Validation", "Test name is empty");
return false;
}
if (data.InputParameters == null || data.InputParameters.Count == 0)
{
Report.Warn("Data Validation", "No input parameters provided");
return false;
}
return true;
}
// 5. Use data cleanup strategies
public void CleanupTestData()
{
// Remove temporary test data
// Reset database to known state
// Clear generated files
}
}
Performance Optimization
- Connection Pooling: Reuse database connections
- Batch Processing: Group similar operations
- Lazy Loading: Load data only when needed
- Caching: Cache frequently used test data
- Parallel Execution: Run independent tests concurrently
Security Considerations
- Credential Management: Store sensitive data securely
- Data Encryption: Encrypt sensitive test data
- Access Control: Limit access to test data sources
- Audit Logging: Track data access and modifications
Related Topics
- Basic API Usage - Foundation concepts
- Advanced Integration - Enterprise patterns
- Code Cookbook - Utility functions
Next Steps: Choose a data source that matches your testing environment and adapt the examples to your specific application requirements.