Як тестувати НЕ-публічні методи в. NET

    imageЛюбиш покривати код тестами? Тобі подобається приємне тепле почуття захищеності, яке виникає при проходженні тестів?
 
Молодець!
 
Справжні професіонали не покладаються на випадок, вони стелять соломку заздалегідь тримають все під контролем.
 
Хочеш щоб усередині, за публічним інтерфейсом, теж все було покрито тестами?
 
 
 
Як і завжди, способів декілька. Є трохи краще, є простіше. Поїхали!
 
 
1. Робимо метод публічним
 
Ні приватного методу — немає проблеми. Можна ще коментар до методу написати
 
 
// For tests only. Do not use directly.

 
 Плюси: дуже просто.
 Мінуси: засмічується інтерфейс, страждає інкапсуляція.
 
 
2. Робимо метод внутрішнім
 
Тобто міняємо модифікатор доступу з
private
на
internal
. Коментар з першого способу тут теж згодиться. Виходить метод, який доступний з будь-якого місця в межах збірки.
 
А тестувати-то його як? Просто. Потрібно додати до збірки атрибут InternalsVisibleTo . Цей атрибут дасть тестирующей збірці доступ до всіх internal методів і властивостей тестованої збірки.
 
Не забудьте, що для функціонування атрибута
InternalsVisibleTo
потрібно, щоб обидві збірки (тестирующая і тестована) були одночасно підписані суворим ім'ям , або одночасно не підписані.
 
 Плюси: досить просто, залишається контроль за тим, хто має доступ до нутрощів збірки.
 Мінуси: все одно засмічується інтерфейс, все одно страждає інкапсуляція, з'являються додаткові умови (див. вище про підписування), атрибут залишається в релізной збірці.
 
 
3. Робимо метод захищеним
 
Замість модифікатора
private
метод має обзавестися модифікатором
protected
. У тестирующей складанні потрібно буде зробити спадкоємця від тестованого класу і — вуаля! — Доступ до методу отриманий.
 
 Плюси: досить просто, є певний контроль за тим, хто має доступ до методу.
 Мінуси: все одно засмічується інтерфейс, все одно страждає інкапсуляція, метод доступний всім, хто побажає його успадкувати, клас з таким методом не може бути позначений закритим для наслідування (sealed).
 
 
4. Використовуємо PrivateObject
 
Цей клас надає Visual Studio Unit Testing Framework , так що якщо в проекті використовується NUnit або ще щось, то цей спосіб не підійде.
 
З
PrivateObject
все просто. Є клас для тестування:
 
 
public class ClassToTest
{
	private string field;

	private void PrintField()
	{
		Console.WriteLine(field);
	}
}

 
Є тестуючий клас:
 
 
[TestClass]
public class TestClass
{
	[TestMethod]
	public void TestPrivateMethod()
	{
		ClassToTest testedClass = new ClassToTest();
		PrivateObject privateObject = new PrivateObject(testedClass);

		privateObject.SetField("field", "Don't panic");
		privateObject.Invoke("PrintField");
	}
}


 
Після виконання тесту в консолі буде рядок
Don't panic
. Завжди хороший рада, але ж?
 
 Плюси: не потрібно міняти існуючий код, досить просто.
 Мінуси: застосовується лише до Visual Studio Unit Testing Framework, при перейменуванні полів і методів тести почнуть падати.
 
 
5. Рефлексія нам допоможе
 
Код буде достовірніше, ніж в попередньому способі, але жити можна.
 
Знову ж, є клас для тестування:
 
 
public class ClassToTest
{
	private string field;

	private void PrintField()
	{
		Console.WriteLine(field);
	}
}

 
У тесті потрібно написати наступне:
 
 
ClassToTest obj = new ClassToTest();
Type t = typeof(ClassToTest);

FieldInfo f = t.GetField("field",  BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
f.SetValue(obj, "Don't panic");

t.InvokeMember("PrintField",
	BindingFlags.InvokeMethod | BindingFlags.NonPublic |
	BindingFlags.Public | BindingFlags.Instance,
	null, obj, null);

 
У консолі знову повинен з'явитися хороший рада.
 
Вищенаведений код можна використовувати в будь-яких тестах. Хоч для NUnit, хоч для Visual Studio Unit Testing Framework, хоч для будь-якої іншої середовища тестування.
 
 Плюси: не потрібно міняти існуючий код, досить просто, застосовне для люой середовища тестування.
 Мінуси: без допоміжного класу в тестах будуть кілометри повторюваного коду, при перейменуванні полів і методів тести почнуть падати.
 
 
Замість висновку
 
Тепер, коли ти знаєш, як тестувати НЕ-публічні методи, саме час задуматися: а чи треба їх тестувати?
 
Аргументи «за»:
 
     
  • Ні-публічні методи можуть містити складну логіку, яку варто перевіряти.
  •  
  • Іноді використання приватних методів в тестах може зменшити обсяг коду, необхідного для створення тестових даних.
  •  
  • Тестувати взагалі потрібно все. Чим більше рядків коду покрито тестами, тим краще.
  •  
 
Аргументи «проти»:
 
     
  • Тестувати потрібно поведінку, а не реалізацію. А значить, тестувати потрібно тільки інтерфейс класу (тільки публічні методи і властивості).
  •  
  • Тести для приватних методів заважають рефактору код, а рефактору код не менш важливо, ніж тестувати.
  •  
  • Якщо метод приватний, то це не просто так. Це означає, що чіпати його не треба.
  •  
 
Особисто я граю за команду «проти». Я думаю, що тестувати потрібно тільки публічні методи і властивості.
  

    
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.