Часто бывает так, что для тестирования класса нужен набор объектов, заполненных произвольными или предопределенными данными. Но заполнять эти объекты - дело муторное. Ниже простой в исполнении и использовании класс, предназначенный специально для заполнения объектов.
Предположим, что у нас есть интерфейс
И, соответственно, класс
Пусть наша задача - протестировать какое-либо действие над нашим объектом
Теперь поставим задачу: пусть, для тестирования этого действия, мы должны передать ему объект IPerson, который будет заполнен адекватными данными. То есть Имя не пустое, возраст неотрицательный, есть хотя бы 1 друг, данные которого тоже заполнены, но нет друзей, например. Вот пример теста:
Думаю, недостатки этого подхода очевидны. А если надо заполнить не 3 поля, а 30? А если поля в класс добавляются уже после написания теста? так неохота править такие тесты...
Ниже представлен целый класс, который упрощает процесс установки значений полей произвольными значениями
Теперь рассмотрим самое простой использование нашего помощника:
Как видно, заполнены текстовые поля. Также целые числа установлены в 0 - допустим, это нас не устраивает. Поэтому можно сразу тут же установить возраст. Это можно сделать 2мя способами: установить все поля типа int в определенное значение, либо установить только поле возраста. Вот оба варианта использования:
Теперь нам нужно добавить хотя бы 1 друга нашему программисту. Делается очень просто.
Таким образом, наш тест можно переписать вот так:
Как видно, он стал меньше за счет уменьшения кода при инициализации объектов для теста.
Предположим, что у нас есть интерфейс
Code Snippet
- public interface IPerson
- {
- string Name { get; }
- int Age { get; }
- IEnumerable<IPerson> Friends { get; }
- void AddFriend(IPerson person);
- void RemoveFriend(IPerson person);
- }
Code Snippet
- public class Programmer : IPerson
- {
- private readonly List<IPerson> _friends;
- public Programmer()
- {
- _friends = new List<IPerson>();
- }
- public string Name { get; set; }
- public int Age { get; set; }
- public String Email { get; set; }
- public IEnumerable<IPerson> Friends
- {
- get { return _friends; }
- }
- public void AddFriend(IPerson person)
- {
- _friends.Add(person);
- }
- public void RemoveFriend(IPerson person)
- {
- _friends.Remove(person);
- }
- }
Code Snippet
- public class SomeAction
- {
- public IPerson Action(IPerson person)
- {
- // Что то происходит
- return person;
- }
- }
Code Snippet
- [TestClass]
- public class SampleTest
- {
- [TestMethod]
- public void Test()
- {
- var programmer = new Programmer
- {
- Age = 18,
- Email = "email@email.em",
- Name = "Name"
- };
- programmer.AddFriend(new Programmer
- {
- Age = 18,
- Email = "email@email.em",
- Name = "Name"
- });
- var act = new SomeAction();
- var result = act.Action(programmer);
- // Гора Assert'ов
- }
- }
Code Snippet
- public static class TestDataSetter
- {
- /// <summary>
- /// Устанавливаем значение объекта
- /// </summary>
- public static T SetData<T, TK>(this T source, TK value) where T : class
- {
- var type = typeof(T);
- var tktype = typeof(TK);
- SetData(type, tktype, source, value);
- return source;
- }
- /// <summary>
- /// Используем действие для установки значения объекта или поля
- /// </summary>
- public static T SetData<T, TK>(this T source, Func<T, TK> action) where T : class
- {
- action(source);
- return source;
- }
- /// <summary>
- /// Используем действие, возвращающее Void для установки значения объекта или поля
- /// </summary>
- public static T SetData<T>(this T source, Action<T> action) where T : class
- {
- action(source);
- return source;
- }
- /// <summary>
- /// Устанавливаем значения простых полей классов
- /// </summary>
- public static T SetData<T>(this T source) where T : class
- {
- var type = typeof(T);
- SetStringData(type, source);
- SetData(source, 0);
- SetData(source, (int?)0);
- SetData(source, false);
- SetData(source, (bool?)false);
- SetData(source, Guid.Empty);
- SetData(source, (Guid?)Guid.Empty);
- SetData(source, new DateTime(2011, 4, 1));
- SetData(source, (DateTime?)new DateTime(2011, 4, 1));
- return source;
- }
- /// <summary>
- /// Устанавливаем строковые значения класса
- /// </summary>
- private static void SetStringData(Type sourceType, object source)
- {
- var valueType = typeof(String);
- var props = sourceType.GetProperties();
- foreach (var propertyInfo in props)
- {
- if (propertyInfo.PropertyType == valueType)
- propertyInfo.SetValue(source, propertyInfo.Name, null);
- }
- if (sourceType.BaseType != null)
- {
- SetStringData(sourceType.BaseType, source);
- }
- }
- /// <summary>
- /// Проход по всем полям и установка их значений
- /// </summary>
- private static void SetData(Type sourceType, Type valueType, object source, object value)
- {
- var props = sourceType.GetProperties();
- foreach (var propertyInfo in props)
- {
- if (propertyInfo.PropertyType == valueType)
- propertyInfo.SetValue(source, value, null);
- }
- if (sourceType.BaseType != null)
- {
- SetData(sourceType.BaseType, valueType, source, value);
- }
- }
- }
Как видно, заполнены текстовые поля. Также целые числа установлены в 0 - допустим, это нас не устраивает. Поэтому можно сразу тут же установить возраст. Это можно сделать 2мя способами: установить все поля типа int в определенное значение, либо установить только поле возраста. Вот оба варианта использования:
Теперь нам нужно добавить хотя бы 1 друга нашему программисту. Делается очень просто.
Таким образом, наш тест можно переписать вот так:
Code Snippet
- [TestClass]
- public class SampleTest
- {
- [TestMethod]
- public void Test2()
- {
- var programmer = new Programmer()
- .SetData()
- .SetData(x => x.Age = 18)
- .SetData(x => x.AddFriend(
- new Programmer().SetData().SetData(x2 => x2.Age = 18)
- ));
- var act = new SomeAction();
- var result = act.Action(programmer);
- // Гора Assert'ов
- }
- }
AutoFixture
ОтветитьУдалитьСпасибо, почитаю, заценю. Век живи - век учись :)
Удалить