Fixie-契約によるテスト

01 しばらく前に、新しいテストオープンソース修正フレームワークを友人が使い始めて、とても喜んでいるというツイートに出会いました。 そのため、新しいエンジン用にプロジェクトのすべてのテストを修正することも決めました。 この後、私はただ我慢できず、それがどんな動物で、他の人をとても喜ばせたのかを見ることさえできませんでした。



次に、フレームワークの概要とその機能を紹介し、これがテストの新しい言葉であるかどうかを理解するために、それを詳しく調べる価値があるか、あなたが通り過ぎて忘れることができます。



サイト自体で述べたように、Fixieは.NETの従来のテストです。 つまり 合意によるテスト。 ここでの合意とは、私たちが一般的に慣れていることを意味します。すべての操作は、紳士の命名規則である「口頭」合意に基づいて実行されます。 最も近い例は足場です。 これは、たとえば、テストクラスにTestという単語が含まれていることや、テストクラスがパブリックであり、何も返さないことで合意したときです。 その後、そのようなクラスはテストクラスとして認識されます。 そして、これ以上の属性やジャズはありません。 クラスとメソッドだけ。



一見すると、これはすべて見栄えがよく、喜ばしいことさえあります。 メソッドとクラスに正しく名前を付けるだけで、幸福が得られることがわかります。 同時に、テストされたクラスのトピックに何らかの関連がある単語の任意の組み合わせではなく、テストクラスを必要に応じて呼び出すようにチームをトレーニングできます。



インストールと最初のテスト



デフォルトでは、Fixieは、テストクラスがTestsで終わるすべてになるように構成されます。 テストメソッド-これらのクラス内のすべてが値を返すわけではありません。 つまり 理論的に、そして実際には、このコードはすでにテストとして認識されています:

public class SuperHeroTests { public void NameShouldBeFilled() { var superHero = new SuperHero(); superHero.Name .Should() .NotBeEmpty(); } }
      
      





先に進む前に、インストール、およびFixieが機能するために必要なことについていくつかの言葉を述べる価値があります。



02



フレームワーク自体をインストールするnugetコマンド。 しかし、これで終わりではありません。 このフレームワークには、テストでデータチェックを書き込むための組み込みの方法はありません。 それらを使用するには、サードパーティのソリューションのいずれかを使用する必要があります。



NuGetを使用してまだインストールされています。 上記の例では、FluentAssertionパッケージが使用されました。私は個人的にそれが好きであり、他のオプションと比較して本質的に大きな違いはないからです。



それだけで、楽しく元気にコードを書くことができます。 特に開発とテストの旅を始めたばかりの場合。 あなたが経験豊富な人なら、多くの疑問が生じます。これがどのように設定され、テストを実行するのがどれほど便利かということです。



統合



テストの実行といえば。 著者は、コンソールからテストを実行する必要があることを正直に認めているため、最後にスタジオとの統合を行いました。 ReSharper用のプラグインがありますが、8.1から8.3までのバージョン用です。 新しいバージョンでは、あなたはプロレタリアです。 テストのために、バージョン8にロールバックしたくなかったので、どれほど快適かは言えません。



スタジオとの統合は、テストを検出して実行するレベルで実行されます。 つまり エディターには強調表示はありません。 特別なテストアイコンを意味します。



03



私の意見では、ここにエラーの潜在的な場所があります 。 子羊。 「テスト」という言葉で封印するのはそれほど難しくありません。テストをスキップすることになります。 視覚的には、目立ちません。 公平に言えば、スタジオの適切なツールを使用してすべてのテストを一括して実行するのではなく、たとえば以前に実行したことがないテストのみを実行する場合、このような状況はまれであり、ありそうにありません。



04



一般的に、当面は、フレームワークの若さと相対的な不確実性、そして結果として多くのツールの欠如を参照することができます。



テストを検出して結果を2回生成するというトピックに戻らないようにするために、FixieはNUnitおよびxUnitのスタイルでレポートを生成でき、これにより通常のCIのユーザーの生活が大幅に促進されることがわかります。



チューニング



フレームワークの主な強みは、テストの識別方法、実行方法、検証方法などを柔軟に構成できることです。 デフォルトでは、フレームワークには何も付属していません。 ただし、 リポジトリには多くの多様な例が含まれています



物事をより面白くするために、ニーズに合わせてフレームワークをカスタマイズする方法について少し掘り下げましょう。 たとえば、他のクラスをテストクラスとして識別する必要があることを言う方法。



すべての設定は、 コンベンションから継承されたクラスのコンストラクターで行われます

 public class CustomConvention : Convention { public CustomConvention () { } }
      
      





デフォルトでは、設定は次のようになります。

 public class DefaultConvention : Convention { public DefaultConvention () { Classes .NameEndsWith("Tests"); Methods .Where(method => method.IsVoid()); } }
      
      





コードでは、テストクラスはTestsで終了する必要があり、テストメソッドはnullを返すだけであると記述されています。 一般的に有効。



私は、テストクラスがWhenという単語で始まり、 Thenという単語でテストするときの原則に従うようにしています。 テストランナーではかなり一貫性のある状況がわかります。テストを作成するときは、テスト対象を既に知っています。 テストの効果について考える必要があります。 追加のボーナスは、テストクラスが短く、テスト間の責任が混在しないことです。



当然、どのルールも方向を示すだけであり、定説ではありません。 「愚かな一貫性は、小さな心のホブゴブリンです」-あなたは常に常識に導かれるべきです。







テストのリストを見ると、すべてのクラスがTestsという単語で終わるか、 Whenで始まるわけではないことがわかります。



たとえば、テストはShouldという単語で始まるという意味で、テストには厳密な命名構造はありません。 しかし、私の意見では、テストを読むことはそこで起こっていることを完全に理解しています。 これは経験に基づいて言います。 このプロジェクトは3〜4年にわたってさまざまな成功を収めてきましたが、そのたびに、何をやったか、何をする必要があるかをすばやく、そしてうまく覚えています。 これは、事務処理が過剰になった場合の単なる一種の出口です。



このネーミングモードでは、テストクラスを定義するキーワードを指定するのが怖い(そして怠けている)のです。 さらに、実際のWhen ... Then ...アプローチは、各テストの開始時に実行され、テスト自体で結果をチェックするか、作成されたオブジェクトに何らかの影響を与えるチューニングメソッドがあることを意味します。 つまり チューニング方法(SetUp)を明示的にマークまたは指定する必要があります。



実装のために、各テストで構成メソッドを明示的に呼び出すことができます。 たとえば、次のように:

 public class SuperHeroTests { public void NameShouldBeFilled() { SetUpEnvironment(); var superHero = new SuperHero(); superHero.Name .Should() .NotBeEmpty(); } }
      
      





もちろん、実際には、SetUpEnvironment()よりも有益な何かを書く必要がありますが、その考えは明確です。 そのような実装では、テスト間で値を保持するクラス変数があります。あるテストで初期化文字列を記述するのを忘れると、簡単に依存テストにつながる可能性があります。



Fixieはこの状況に対する解決策を提供します。 ここにあります:

 public class DefaultConvention : Convention { public DefaultConvention () { Classes .NameEndsWith("Tests"); Methods .Where(method => method.IsVoid()); CaseExecution .Wrap<HeroUniverseSetup>(); } } public class HeroUniverseSetup : CaseBehavior { public void Execute(Case context, Action next) { //  } }
      
      





つまり CaseBehaviorの後継を実装し、必要なすべてを登録する必要があります。 ドキュメントには実装が記載されています







しかし、そうであっても、このアプローチでは、「DefaultConvention」の多くのバリエーションを作成し、テストから離れた初期化ロジックを規定する必要があることがわかります。起動時に、そのようなコンテキストが存在することさえ知りません。



これを実証するのは簡単です。 DefaultConventionを上記の例のようにして、クラスが開始したとき

 public class SuperHeroTests { public void NameShouldBeFilled() { var superHero = new SuperHero(); superHero.Name .Should() .NotBeEmpty(); } }
      
      





SetUp / TearDownがあるとは何もわかりません。







マジック!!! これは一般に興味深いですが、この場合はそうではありません。 私はプロジェクトでそのような魔法を想像したくありません。 これは、テストが透明でなければならないという事実と完全に矛盾しています。 同じクラスでCaseBehaviorの拡張機能を記述したとしても、それがすべて設定されるクラスを探す場所があまり明確ではないためこれは解決策ではありません。 永続的なコピーと貼り付けを行うこともオプションではありません。



比較する:

 public class DefaultConvention : Convention { public DefaultConvention () { Classes .InTheSameNamespaceAs(typeof(DefaultConvention)) .NameEndsWith("Tests"); Methods .Where(method => method.IsVoid()); CaseExecution .Wrap<HeroUniverseSetup>(); } } public class HeroUniverseSetup : CaseBehavior { public void Execute(Case context, Action next) { //  } } public class SuperHeroTests { public void NameShouldBeFilled() { var superHero = new SuperHero(); superHero.Name .Should() .NotBeEmpty(); } }
      
      





に対して:

 [TestFixture] public class SuperHeroTests { [SetUp] public void Init() { //  } [Test] public void NameShouldBeFilled() { var superHero = new SuperHero(); superHero.Name .Should() .NotBeEmpty(); } }
      
      





NUnitの場合、コードの行はなんとなく小さくなり、クラスのSetUpはプロジェクトで失われません。



たぶん私は偏見があり、誤ってテストを書いていますが、...わからないことがあります。 SetUpがいかに悪いかについての記事を見たことがありません。 つまり すべてを不条理のポイントに持ち込み、プロジェクトの半分のセットアップで初期化を行うことができますが、これは別の問題です。







ただし、この場合には解決策があります。 自己記述属性を使用して、テスト、テスト、およびクラスの適切な部分を識別することができます。 つまり プロジェクトでNUnit、xUnitを完全にエミュレートできます。



作成できる属性を使用する







カテゴリを作成して起動する小さな例を次に示します。

 public class CustomConvention : Convention { public CustomConvention() { var desiredCategories = Options["include"].ToArray(); var shouldRunAll = !desiredCategories.Any(); Classes .InTheSameNamespaceAs(typeof(CustomConvention)) .NameEndsWith("Tests"); Methods .Where(method => method.IsVoid()) .Where(method => shouldRunAll || MethodHasAnyDesiredCategory(method, desiredCategories)); if (!shouldRunAll) { Console.WriteLine("Categories: " + string.Join(", ", desiredCategories)); Console.WriteLine(); } } static bool MethodHasAnyDesiredCategory(MethodInfo method, string[] desiredCategories) { return Categories(method).Any(testCategory => desiredCategories.Contains(testCategory.Name)); } static CategoryAttribute[] Categories(MethodInfo method) { return method.GetCustomAttributes<CategoryAttribute>(true).ToArray(); } }
      
      





そして、カテゴリに応じてテストを実行します:

Fixie.console.exe path / to / your / test / project.dll --include CategoryA



私の決断



Fixieについてもっと詳しく言うことができ、特定の問題を解決する例を示しますが、すでに第一印象があります。 したがって、曲線を描く価値があります。



Fixieはテスト用のメタフレームであることがわかりました。 ルールとテストの機会を自由に構築できますが、正直に言うと、非常に簡単に構築されます。 唯一の質問は便宜です。 これをすべて行う必要がありますか? もちろん、少し怖いのは、R#のサポートが不足していることと、テストが実際にテストであり、フレームワークによって認識されたことがわからないという事実です。 実稼働環境では使用しませんが、家庭用および有望なツールとして-Fixieは非常に興味深いものです。 少なくとも、標準のNUnitツールを使用して解決するのが困難で、興味深く具体的なテストタスクがある場合、私は間違いなく彼を覚えています。



All Articles