単体テストと受け入れテストのためのMS-Testのデータ駆動テスト

すぐに、単体テストの世界とユーザーインターフェイスを介した受け入れテストの世界は非常に異なる世界であるという事実を強調したいと思います。独自の法律、異なる機能、制限があります。 また、単体テストの世界がアプリケーションの各部分を個別にカバーするように機能する場合、ユーザーインターフェイスを介したテストは、ほとんどの場合、ボタンを押して入力することによるシステムでのユーザーの作業のエミュレーションです。 。



ツールが単体テストに非常に優れた機能を提供する場合でも、これらの機能はUIテストには実際には適用できないことがよくあります。



Ms-Testフレームワークでデータ駆動型テストを使用することを決めたとき、それは私の練習で起こりました。 この記事では、問題とその解決策についてさらに詳しく説明しますが、それについてはまだ理解していません。誇りに思うか恥ずかしいことが必要です。



単体テストの世界におけるデータ駆動型アプローチ



int ConvertToNumber(文字列入力)メソッドがあり、入力として数値を持つ文字列を受け取り、整数型のみで同じ数値を返すとします。



たとえば、input =“ 3”に対して1つの正のテスト( 喜びをもたらす記述し、結果も3になることを確認しましょう。



他に何? もちろん、境界値と等価クラス:「-1」、「0」、「MaxInt」、「-(MaxInt)」。

さて、小数が入力にヒットしたらどうなるでしょうか? そして、行が数字で始まり、その後に文字が続く場合は?



そのようなさまざまな状況で正しい決定は何ですか?

しかし、正しい決定はありません。



これらの状況は異なるプログラミング言語で異なる方法で処理されることに注意してください。 例えば、操作{“ 10” + 1}の結果としてのJavaScriptは文字列“ 101”を返し、{“ 10”-1}の結果として数字9があります。そしてPerlはそれぞれ11と9と言います。 しかし、「真の」答えがないからといって、私たちが独立して正しい行動を考案し、これが真実であると言って、この真実を石の単体テストで彫ることはできません。



しかし、1つのパラメーターを持つ1つの不幸なメソッドに対して、実際に12個の単体テストを保存して貼り付けますか?

そして、入力値と期待値のテーブルを作成し、そのようなテーブルの各行をパラメーターとして1つのテストに渡すのはなぜですか。

最初は、Ms-Testがこのために非常に豊富な機会を提供していることに、私自身非常に驚きました。



Ms-Testでのデータ駆動型ユニットテストの例



説明のために、ConvertToNumberの実装では、Convert.ToInt32()は使用しません。これは単純すぎます。 代わりに、実装を提供します。

static int ConvertToNumber(string input) { int result = 0; while (input.Length > 0) result = (input[0] >= '0' || input[0] <= '9') ? (result * 10) + input[0] - '0' + (((input = input.Remove(0, 1)).Length > 0) ? 0 : 0) : 0; return result; }
      
      





残念ながら、Ms-Testには、属性を使用してコードに直接入力値と期待値を設定する方法がありません。 しかし、代替手段があります-データソースを使用します。 そして、そのようなソースとして、Excelファイルを使用するとは思わないでしょう。



実際には、.NETからアクセスできる任意のデータソースを使用できます。 CSV、XMLなどを含みます。

しかし、Excelでの作業は素晴らしいです! たとえば、テーブルは次のようになります。







主なことは、目的のフラグメントを選択し、「タイトル付きのテーブル」としてフォーマットすることを忘れないことです。 そうでなければ、魔法は機能しません。



次に、Excelテーブルへの接続の実装とデータの読み取りによる単体テストは次のようになります。



 const string dataDriver = "System.Data.OleDb"; const string connectionStr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\\matrix.xlsx;Extended Properties=\"Excel 12.0 Xml;HDR=YES\";"; [TestMethod] [DataSource(dataDriver, connectionStr, "Shit1$", DataAccessMethod.Sequential)] public void TestMe() { string rowInput = TestContext.DataRow["Input"].ToString(); string rowExpected = TestContext.DataRow["Expected Result"].ToString(); string rowException = TestContext.DataRow["Exception"].ToString(); string rowComment = TestContext.DataRow["Comment"].ToString(); int actualResult = ConvertToNumber(rowInput); Assert.AreEqual(rowExpected, actualResult.ToString()); }
      
      







はい、接続の設定と入力データの変換に少し努力しなければなりませんでしたが、どのような視覚的な結果が得られたかを見てください。







新しいテストを追加するには、Excelに新しい行を追加するだけです。 また、テストで正確にカバーされているものとされていないものを確認するためにコードに登る必要はありません。



さらに、任意のテストに進み、エラーまたはログの詳細を確認できます。



低パフォーマンスのマシンでも、14のテストすべてが1秒未満で合格したことに注意してください。



あなたについては知りませんが、そのような機会と視覚的な結果に感銘を受けました。

注:すべてを機能させるには、matrix.xlsxファイルをC:\ドライブのルートにコピーすることを忘れないでください



ユーザーインターフェイスを介したテストの世界におけるデータ駆動型アプローチ



残念なことに、Selenium WebDriverを使用してUIテストを始めた後、Ms-Testのメガクール機能への憧れは終わりました。



実際、どの実装でも、UIテストはその性質上、モジュラーテストと比較して非常に遅いということです。 ここでは、システムのコアから関数を呼び出して呼び出すだけで、コンテキストやパラメーターを簡単に操作することはできません。 いいえ...複雑なユーザーシナリオを実装するために、多くのボタンをクリックする必要があります。



したがって、考えられるすべての最適化を考慮に入れて、1つのUIテストは30秒から数十分になります。 それはすべて、スクリプトの複雑さに依存します。



たとえば、14のデータ駆動型テストのうち1つが1分かかると仮定した場合、セット全体は14分で合格します。

ここで、たとえば、ページ上のボタンに表示する時間がなかったために、何らかの理由で1つのテストが失敗することを想像してください...

あなたは、あなたが言った、あなたが倒れたテストを修正し、実行することができるだけであることは重要ではありません。

いや Ms-Testは、データ駆動型テストのパックを1つのテストと見なします。 したがって、1回のテストを抜くには、14回すべてを再実行する必要があります。 そして、以前に合格したテストが突然落ちないという事実ではありません。 デバッグの場合、ブレークポイントを設定するだけでなく、条件付きブレークポイントを設定する必要があります。VisualStudioではこれが可能になりますが、追加の労力と時間が必要になります。



落ちたテストのみを追い越すことができる方法を見つけることが必要でした。 そして、私はそのような方法を見つけました。



問題の解決策:「継承によるサイクル」



問題を解決するには、プロジェクトにTestBase.csTestRows.csの 2つのファイルを追加する必要があります。



次に、ネームスペース「MsTestRows.Rows。*」に、 100個から100個の生成されたメソッドを含むクラスTestRows_ 01 ... TestRows_ 100が表示されます。



必要な数のメソッド(NN)を使用してTestRows_NNからTestClassを継承します。



次に、Visual Studioから2つのメソッドを実装するように求められます。



完全なサンプルコード
 using System; using System.Text; using System.Collections.Generic; using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace HabraDrivenTests { // Custom Data Row public class HabrDataRow { public string Input { get; set; } public int Expected { get; set; } public Type ExpectedException { get; set; } public string Comment { get; set; } } [TestClass] public class HabrTestInherienceLoop : MsTestRows.Rows.TestRows_04<HabrDataRow> { // Production-ready method static int ConvertToNumber(string input) { int result = 0; while (input.Length > 0) result = (input[0] >= '0' || input[0] <= '9') ? (result * 10) + input[0] - '0' + (((input = input.Remove(0, 1)).Length > 0) ? 0 : 0) : 0; return result; } // Test Data static HabrDataRow[] testData = new HabrDataRow[] { #region Data new HabrDataRow() { Input = "0", Expected = 0, ExpectedException = null, Comment = " ", }, new HabrDataRow() { Input = "1", Expected = 1, ExpectedException = null, Comment = " ", }, new HabrDataRow() { Input = "-1", Expected = -1, ExpectedException = null, Comment = " ", }, new HabrDataRow() { Input = "2147483647", Expected = 2147483647, ExpectedException = null, Comment = "int.MaxValue", }, #endregion }; // Data Generator public override HabrDataRow GetNextDataRow(int rowIndex) { return testData[rowIndex]; } // Test Implementation public override void TestMethod(HabrDataRow dataRow, int rowIndex) { int actualResult = ConvertToNumber(dataRow.Input); Assert.AreEqual(dataRow.Expected, actualResult); } } }
      
      







その結果、このような倒錯した操作を使用して、落ちたテストのみを再開する機会を得ました。

画像



ソースコードとリンク





その他の関連資料:






注1: TestRows.csファイルには、30,000行のコードが含まれています。 給与ボーナスが記述されたコードの行数に依存し、システムが100万ドルのボーナスを自動的にクレジットする場合、この金額の少なくとも5%を送っていただければ公平だと思います。



注2: ConvertToNumberを実装して、すべてのテストに合格するようにしてください。

注意:「著者のスタイル」に固執します。



All Articles