Всем привет. Сегодня хочу вам рассказать о том, как внедрять Data-Driven подход и в качестве источника данных использовать XML-файлы. Язык С#.
В нашем случае будут задействованы следующие классы, методы и файлы:
- XML-файл c тестовыми данными (Login.xml) – логином и паролем для входа на сайт.
- Test Data Model (LoginModel.cs) – модель со свойствами и полями наших тестовых данных, хранящихся в предыдущем файле.
- Test Data Builder (TestDataBuilder.cs) – класс для доступа к данным с нашего XML-файла.
- UI Test (LoginDataDrivenUsage()) – метод с аннотациями ‘DeploymentItem’ и ‘DataSource’, в котором непосредственно отрабатывается сценарий входа на сайт.
Для внедрения этой логики нам будет достаточно использовать проект Unit test в Visual Studio:
1. К нему нужно добавить наш XML-файл с данными:
В этом файле в свойстве ‘Copy to Output Directory’ нужно выбрать ‘Copy if newer’:
и скопировать тестовые данные:
1 2 3 4 5 6 7 8 9 |
<?xml version="1.0" encoding="utf-8" ?> <TestData> <!-- Test Data for Login--> <ValidDates> <!--Test Data Set 1--> <UserName>QAUser</UserName> <UserPass>autotest123</UserPass> </ValidDates> </TestData> |
Название XML-файла мы будем использовать далее в аннотации ‘DeploymentItem’. Вы можете заметить, что для входа на сайт будут взяты следующие данные: логин -‘QAUser’ и пароль – ‘autotest123′.
2. Следующим шагом будет создание модели DataModel со свойствами логина и пароля:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
namespace RewardSmall.DAL { public class LoginModel { private string _userName; private string _userPass; public string UserName { get { return _userName; } set { _userName = value; } } public string UserPass { get { return _userPass; } set { _userPass = value; } } } } |
Обратите внимание, что мы должны использовать идентичные XML-файлу (UserName и UserPass) имена свойств.
3. Класс Test Data Builder:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace RewardSmall.DAL.Utilities { public static class TestDataBuilder { private static object convertValue(string str, Type typeDefault) { Type[] genericTypeArguments = typeDefault.UnderlyingSystemType.GenericTypeArguments; if (genericTypeArguments.Any<Type>()) { return Convert.ChangeType(str, genericTypeArguments.FirstOrDefault<Type>()); } return Convert.ChangeType(str, typeDefault); } private static Dictionary<string, string> createTestData(TestContext testContext) { Dictionary<string, string> dictionary = null; if (testContext.DataRow != null) { dictionary = new Dictionary<string, string>(); foreach (object obj2 in testContext.DataRow.Table.Columns) { if (!string.IsNullOrEmpty(testContext.DataRow[obj2.ToString()].ToString())) { if (testContext.DataRow[obj2.ToString()].ToString() == "empty") { dictionary.Add(obj2.ToString(), string.Empty); } else { dictionary.Add(obj2.ToString(), testContext.DataRow[obj2.ToString()] as string); } } } } return dictionary; } public static T GetTestData<T>(TestContext testContext, T objectA, bool fullfillHierarchy = false) { Func<Type, bool> predicate = null; Type type = typeof(T); T local = (objectA == null) ? ((T)Activator.CreateInstance(type)) : objectA; type = local.GetType(); Dictionary<string, string> dictionary = createTestData(testContext); if (fullfillHierarchy) { foreach (PropertyInfo info in type.GetProperties()) { if (((info.PropertyType.GetProperties().Any<PropertyInfo>() && !info.PropertyType.UnderlyingSystemType.GenericTypeArguments.Any<Type>()) && !info.PropertyType.Name.Contains("String")) && !info.PropertyType.Name.Contains("[]")) { object obj1 = info.GetValue(local); if (obj1 == null) { } info.SetValue(local, GetTestData<object>(testContext, Activator.CreateInstance(info.PropertyType), true)); } } } if (dictionary != null) { foreach (KeyValuePair<string, string> pair in from item in dictionary where type.GetProperty(item.Key) != null select item) { if (pair.Value == "null") { type.GetProperty(pair.Key).SetValue(local, null); } else { Type propertyType = type.GetProperty(pair.Key).PropertyType; if (predicate == null) { predicate = c => c.Namespace.Contains("Collections"); } if (type.GetProperty(pair.Key).PropertyType.GetTypeInfo().ImplementedInterfaces.Any<Type>(predicate) && propertyType.GenericTypeArguments.Any<Type>()) { Type type3 = propertyType.GenericTypeArguments[0]; MethodInfo info3 = typeof(TestDataBuilder).GetMethod("convertArray", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance).MakeGenericMethod(new Type[] { type3 }); type.GetProperty(pair.Key).SetValue(local, info3.Invoke(null, new string[][] { pair.Value.Split(new char[] { Convert.ToChar(",") }) })); } else { type.GetProperty(pair.Key).SetValue(local, convertValue(pair.Value, propertyType)); } } } } return local; } } } |
Это готовый для использования класс, которые использует TestContext и DataModel, который позволяет получать объект DataModel со всеми тестовыми данными с XML.
4. UI Test.
Также нам понадобится абстрактный класс TestContext, который используется для хранения информации, передаваемой для тестов:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
private TestContext testContextInstance; /// <summary> ///Gets or sets the test context which provides ///information about and functionality for the current test run. ///</summary> public TestContext TestContext { get { return testContextInstance; } set { testContextInstance = value; } } |
Теперь мы можем использовать тестовые данные в нашем UI тесте:
1 2 3 4 5 6 7 8 |
[DeploymentItem(@"DataModels\Login.xml")] [DataSource("Microsoft.VisualStudio.TestTools.DataSource.XML", @"|DataDirectory|\DataModels\Login.xml", "ValidDates", DataAccessMethod.Sequential)] [TestMethod] public void LoginDataDrivenUsage() { var testData = TestDataBuilder.GetTestData(TestContext, new LoginModel()); _baseTests.LoginTest(testData); } |
Где DataModels\Login.xml – путь к XML-файлу в иерархии классов
“ValidDates” – соответствующий тег с XML-файла, который определяет тестовые данные
var testData = TestDataBuilder.GetTestData(TestContext, new LoginModel()) - получение тестовых данных с XML-файла
_baseTests.LoginTest(testData) – использование тестовых данных в тестах
Сам метод LoginTest класса BaseTests принимает нашу модель:
1 2 3 4 5 6 7 8 9 10 11 |
public void LoginTest(LoginModel loginModel) { var loginPage = _postfixer.GetPage<LoginPage>(); loginPage.UserName.SendText(loginModel.UserName); loginPage.Password.SendText(loginModel.UserPass); loginPage.SignIn.Click(); var homePage = _postfixer.GetPage<HomePage>(); homePage.Logout.Click(); } |
Для удобства я разбил фреймворк на два подпроекта и покажу его часть:
Спасибо за внимание!