NDDテストをBDDスタイルで作成します

静かな金曜日の夜、実装されたロジックをテストすることで、1日を終えました。 Royが推奨したように、テストの名前を慎重に選択しました。 そして突然、理解しやすいテスト名がひどいモンスターに変わり、完全に理解不可能になったことに気付きました。 さらに、画面上の120文字に収まらなくなりました。



そして、私はどこかでビヘイビア駆動開発の概念が私に出くわし、このスタイルで書かれたテストがはるかに読みやすく、理解しやすいことを思い出しました。 「nunit bdd」のリクエストで、googleの結果はほとんどありませんが、 この投稿で興味深い考えに至りました。 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), false).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 Document _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 = "Jedi" }; } [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パッケージの作成を試みます。

良い週末を読んで楽しんでいる人たちに注目してくれてありがとう。



UPD:ビデオデモンストレーションを誤って見て、 Mighty Mooseはこのアイデアが元々 Fohjin.DDD.Exampleの例で実装されていることを発見しました。 少し違いますが、本質は同じです。 同じクラスで複数のGiven / And属性を使用できます。



All Articles