Создаем NUnit тесты в BDD стиле

от автора

Тихим пятничным вечером я закачивал свой рабочий день покрывая тестами реализованную логику. Названия тестов я тщательно подбирал как и рекомендовал Рой. И вдруг я осознал, что понятное развернутое название теста превратилось в ужастного монстра и совсем перестало быть понятным. И более того перестало помещаться в 120 символов на экране.

И вот я вспомнил что мне где-то встечалось понятиe Behavior-Driven Development и тесты написанные в этом стиле намного читабельнее и понятнее. По запросу «nunit bdd» гугл выдает совсем немного результатов, но этот пост навел меня на интересную мысль. Методы Given и When не дают четкого описания условия и тем более описать больше чем одно условие наглядно не получится. Я решил слегка доработать решение.

Вот, кстати, то самое имя теста:
CreateMonthlyPaymentInvoice_WhenCustomerIsRegularCompany_ShouldCreateMonthlyUsageInvoiceWithoutBankFee.
Жутко, правда?

Я внес довольно незначительные изменения в подход, описанные в статье. Добавил атрибуты Given, And и When и логику вызова методов отмеченных этими атрибутами.

namespace NUnit.Specification {     public abstract class SpecificationBase     {         [TestFixtureSetUp]         public void SetUp()         {             Setup();             Given();             When();         }          public virtual void Setup() { }          private void Given()         {             IEnumerable<MethodInfo> publicMethods = this.GetType().GetMethods();              publicMethods.Where(m => m.GetCustomAttributes(typeof(GivenAttribute), false).Count() != 0)                 .ToList()                 .ForEach(m => m.Invoke(this, new object[0]));              publicMethods.Where(m => m.GetCustomAttributes(typeof(AndAttribute), false).Count() != 0)                 .ToList()                 .ForEach(m => m.Invoke(this, new object[0]));         }          private void When()         {             this.GetType()                 .GetMethods()                 .Where(m => m.GetCustomAttributes(typeof(WhenAttribute), true).Count() != 0)                 .ToList()                 .ForEach(m => m.Invoke(this, new object[0]));         }          [TestFixtureTearDown]         public void TearDown()         {             Teardown();         }          public virtual void Teardown() { }     }      public class SpecificationAttribute : TestFixtureAttribute { }     public class GivenAttribute : Attribute { }     public class AndAttribute : Attribute { }     public class WhenAttribute : Attribute { }     public class ThenAttribute : TestAttribute { } } 

Теперь тесты стали намного более читабельными и гибкими.

    [Specification]     public class Create_invoice_for_regular_customer : SpecificationBase     {         private Customer _customer;          private List<Transaction> _transactions;          private XmlDocument _invoice;          public override void Setup()         {             // General context setup like             DateTimeProvider.Current = new Mock<DateTimeProvider>().Object;             Mock.Get(DateTimeProvider.Current).Setup(m => m.GetCurrentTime()).Returns(new DateTime(2012, 10, 26));         }          [Given]         public void Customer_is_regular_company()         {             _customer= new Customer () { Name = "Jedy" } };         }          [And]         public void Set_of_transactions_representing_monthly_usage()         {             _transactions = new[]                 {                     new Transaction() { Amount = 25 },                     new Transaction() { Amount = 16 },                     new Transaction() { Amount = 32 },                 }.ToList();         }          [When]         public void Creating_monthly_usage_document()         {             _invoice = DocumentCreator.CreateMonthlyPaymentInvoice(_customer, _transactions);         }          [Then]         public void Document_should_contain_transactions_and_not_contain_bank_fee()         {             var expectedInvoice = new ExpectedInvoice();             Assert.That(_invoice, Is.EqualTo(expectedInvoice));         }          public override void Teardown()         {             DateTimeProvider.Current = new DateTimeProvider();         }     } 

Если такая штука окажется полезной я попробую создать NuGet пакет для нее.
Спасибо за внимание тем, кто дочитал и хороших выходных.

ссылка на оригинал статьи http://habrahabr.ru/post/156325/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *