第 2 章、試乗について共有します。ここでのテストは主に Web バックエンド テスト用です。テスト ケースを作成する理由 (つまり、テスト ケースの改善は時間の無駄かどうか)、テスト ケースの改善方法、コード設計でテスト ケースの作成を簡素化する方法、そしてその後のいくつかのアイデア。
1. テストケースを作成する理由
この習慣は、通常、開発の進行を遅らせる動作であると考えられています。テスト ケースを徐々に改善するには、コードの開発とほぼ同じ時間を費やす必要があります。ただし、開発プロセス中、コードを開発した後、自分に責任があり、問題の発見をテスターに任せない場合は、通常、この時点で手動テストを行うことになります。例:
コード内の特定のメソッドを実行して、出力値が期待どおりであるかどうかを確認します。
データベース/キャッシュを変更し、特定のメソッドを実行して、データベースの変更が期待どおりであるかどうかを確認します。
ツールを使用して特定のインターフェイスのリクエストをシミュレートし、インターフェイスの戻り値/データベース内の変更された値が期待どおりかどうかを確認します。
フロントエンド ページがある場合は、フロントエンドとバックエンドの共同デバッグも必要になります。つまり、フロントエンド ページでのフロントエンドの対話を通じて、フロントエンドのフィードバックが期待を満たしているかどうかをチェックして、間接的に検証します。バックエンドコードの正確さ。
最新のテスト ツールは、これらの手動テストの動作をコード ブロックに抽象化するために最善を尽くしています。意識的に手動テストを実行すると、実際にテスト ケースの動作を試し始めたことになります。テストは手動で実行できるのに、なぜコードを使用してテストを実装する必要があるのでしょうか?
コードは再利用したり、単純なリファクタリング後にさらに多くの機能を実現したりできますが、手動で行うことを選択した場合は、毎回最初からやり直す必要があります。
成熟したワークフローには、コード レビュー プロセスが含まれている必要があります。コードを 1 文ずつ読んだり、テスト コードの完全性と正確性を確認したり、テスト ケースを実行したりするには、さまざまな方法があります。後者の方が簡単です。
バグの修正など、コードが変更された場合、その変更がコードに依存する他の部分に影響を与えるかどうかを保証することは困難です。手動テストの時代には、バグを修正した後にシステムを再テストする回帰テストと呼ばれるものがあります。ただし、完全なテスト ケースがすでにある場合は、コマンドを直接実行するだけです。
コードをリファクタリングするときも同様です。
2. テストケースを改善する方法
改良段階に入る前に、まずテスト ケースをどのように実装するかについて話し合います。
describe Meme do before do @meme = Meme.new end describe "when asked about cheeseburgers" do it "must respond positively" do @meme.i_can_has_cheezburger?.must_equal "OHAI!" end end describe "when asked about blending possibilities" do it "won't say no" do @meme.will_it_blend?.wont_match /^no/i end end end
上記のコードは Ruby のミニテストからのものです。 before に含まれるコード ブロックは、次のテスト ケースを実行する前に実行する必要があるもので、通常、テスト ケースの実行後に実行される対応するメソッドもサポートしています。ユースケースごとにいくつかの小さな判断が行われます。
最初の段落では、手動テストによく関係するいくつかのテスト内容について説明しました。説明のためにその 2 と 3 を示します。データベース関連のテストを行う場合、beforeにテストデータを挿入し、afterにテストデータを削除する必要があります。中間のテスト ケースでは、実行が完了した後、対応するメソッドを実行することによってコードの正しさが確認されます。データの変更を確認するか、予期される例外があるかどうか、予期した結果が返されるかどうかを確認します。インターフェイスの場合は、コードを通じて対応するリクエストを開始し、返されたコンテンツが期待される結果を返すかどうかを確認し、必要に応じてデータベース内のデータが期待される変更を満たしているかどうかを確認します。
これでテスト ケースが完成しましたが、考慮する必要がある特別なケースがまだあります。関数の比較的完全なテスト ケースを作成し、実行した後、テストに合格しましたが、オンライン ログにその関数のエラーがまだ残っていることがわかりました。確認したところ、以前のテスト中に関数の特定のブランチがテストされていなかったことがわかりました。その結果、非常に不明瞭な構文エラーが報告されました。すべてのコードはテスト済みですか?ここで導入する必要があるのは、テスト ケース カバレッジと呼ばれる概念です。基本的に、すべての言語には対応する実装があります。テスト ケースのカバレッジを通じて、テスト ケースが特定のファイル内のすべてのコードを実行したかどうかを定量的に知ることができます。必要なのは、カバレッジが可能な限り 100% に保たれていることを確認することです。
ある意味、テスト ケースとテスト カバレッジは、開発者が自分のコードに対する自信を高めるために使用されるツールです。ただし、それらは全能ではありません。もちろん、テスト ケースの一部のパラメータが欠落している可能性は常にありますが、コードはこの可能性を考慮して作成されているわけではありません。 . テストはしましたが、考慮していない可能性についてはどうすることもできません。したがって、JavaScript では == の代わりに === を使用する、強い型のプログラミング標準を使用するなど、できる限り厳密なコードを作成して、大きすぎるパラメーターを受け入れることによって引き起こされる潜在的なリスクを軽減します。範囲。
3. コード設計によりテスト ケースの作成が簡素化される方法
Web 全体 (Web に限定されない) には、通常、純粋なデータ処理と計算、データベース関連、および特定のネットワーク プロトコル関連の 3 つのレベルのコードが含まれます。このうち、純粋なデータ操作は、主に通常の操作を処理する関数またはその他のコードであり、特定のネットワーク プロトコルに関しては、伝統的な意味での MVC に相当します。これら 3 つのテストは、第 1 セクションの定期テスト内容の最初の 3 項目に対応します。
C レベルには通常、ページのレンダリングと対応するプロトコルのシミュレーションが含まれるため、通常は関数とデータベース関連のコードにテストを集中させることで、必要なコントローラー コードをできる限り少なくするテスト ケース コードの複雑さを軽減できます。 。より複雑なアプリケーションに対する現在の推奨事項:
データの基本的な検証を M 層に配置します。Ruby を使用して開発した場合、ActiveRecord と Mongoid はどちらも非常に便利な検証機能を提供します。
モデル間の通信を実現するために、ORM で提供されるいくつかのフックを使用して、コード内で Pub/Sub モードを使用してみてください。 たとえば、A がメッセージの作成時にメッセージをパブリッシュすると、B はメッセージをリッスンした後に自身の属性値の 1 つを変更します。
コマンド モードを使用して、電子メール送信などのビジネス以外の機能をシステムから抽出します。
上記の提案の参考: Laravel wisper resque
4. アイデア
上記のコンテンツは、フロントエンドとバックエンドの共同デバッグを必要とするテストケースを回避しています。以下のコンテンツは主にこの領域に焦点を当てています。 Ruby には、この方向のエレガントな実装がすでにいくつかあります。興味がある場合は、まず Capybara に直接アクセスしてください。
Selenium Phantomjs や前者をベースにした Watir を含む一連のブラウザ ドライバーの人気により、コードを使用してブラウザを制御することはもはや複雑な問題ではなくなりました。この機能に基づいて、フロントエンドベースのテストを 4 つのステップに分割してみることができます。
等待某标志性元素出现(例如等待页面载入玩,或者某个内容异步加载出现)
模拟用户操作,这里的操作包括且不局限于用户点击、用户输入
等待反馈中标志性元素出现(例如某某输入框出现)
判断内容,是否符合预期
基于这个流程,可以解决绝大多数的前端测试。但是单纯依靠这个流程任然不够,因为页面中可能出现例如验证码这样的阻碍元素,在不修改代码的前提下,可以尝试通过数据库/缓存来取到这些内容。同样,和测试接口相同,这里也涉及到在测试前数据库中插入测试数据,测试用例执行后严重数据库里面数据变化,以及全部测试完毕后删除测试数据的内容。最终导致这块测试用例代码的实现需要同时对前端后端有一定的了解。目前还在考虑在借鉴 Capybara 的基础上,设计出更加通用的方案。
最后贴一段 Capybara 的代码结束这段内容:
feature "Signing in" do background do User.make(:email => 'user@example.com', :password => 'caplin') end scenario "Signing in with correct credentials" do visit '/sessions/new' within("#session") do fill_in 'Email', :with => 'user@example.com' fill_in 'Password', :with => 'caplin' end click_button 'Sign in' expect(page).to have_content 'Success' end given(:other_user) { User.make(:email => 'other@example.com', :password => 'rous') } scenario "Signing in as another user" do visit '/sessions/new' within("#session") do fill_in 'Email', :with => other_user.email fill_in 'Password', :with => other_user.password end click_button 'Sign in' expect(page).to have_content 'Invalid email or password' end end