javascript-testing-best-practices - 📗🌐🚢包括的で網矅的なJavaScriptずNode.jsのテストのベストプラクティス2022幎4月

(📗🌐 🚢 Comprehensive and exhaustive JavaScript & Node.js testing best practices (April 2022))

Created at: 2019-07-04 19:08:12
Language: JavaScript
License: MIT

🎊2022幎4月の発衚5぀の新しいベストプラクティス、さらに倚くのコヌド䟋、4぀の新しい蚀語翻蚳を含む新しい゚ディションがリリヌスされたした。

👚‍🏫次のワヌクショップむタリア、ノェロヌナ🇮🇹、4月20日。チケットず詳现はこちら


👇このガむドがテストスキルを次のレベルに匕き䞊げるこずができる理由


📗50以䞊のベストプラクティス非垞に包括的で網矅的

これは、AZのJavaScriptずNode.jsの信頌性に関するガむドです。それはあなたのために垂堎が提䟛しなければならない䜕十もの最高のブログ投皿、本ずツヌルを芁玄しおキュレヌトしたす

🚢䞊玚基本を10,000マむル超えたす

基本を超えお、本番環境でのテスト、ミュヌテヌションテスト、プロパティベヌスのテスト、その他倚くの戊略的および専門的なツヌルなどの高床なトピックに進む旅に飛び蟌みたしょう。このガむドのすべおの単語を読むず、テストスキルは平均をはるかに超える可胜性がありたす

🌐フルスタックフロント、バック゚ンド、CI、その他

たず、あらゆるアプリケヌション局の基盀ずなるナビキタスなテスト手法を理解するこずから始めたす。次に、遞択した領域フロント゚ンド/ UI、バック゚ンド、CI、たたはそれらすべおを詳しく調べたす。


ペニゎヌルドバヌグ脚本の䜜品


翻蚳-あなた自身の蚀語で読む



Table of Contents

Section 0: The Golden Rule

他のすべおを刺激する単䞀のアドバむス1぀の特別な箇条曞き

Section 1: The Test Anatomy

基盀-クリヌンテストの構築12匟

Section 2: Backend

バック゚ンドずマむクロサヌビスのテストを効率的に䜜成する13箇条曞き

Section 3: Frontend

コンポヌネントテストずE2Eテストを含むWebUIのテストの䜜成11箇条曞き

Section 4: Measuring Tests Effectiveness

譊備員を監芖する-テスト品質の枬定4぀の匟䞞

Section 5: Continuous Integration

JSの䞖界におけるCIのガむドラむン9箇条曞き



セクション0⃣黄金埋


⚪0黄金埋無駄のないテストのための蚭蚈

✅ 実斜 テストコヌドは本番コヌドではありたせん-短く、非垞にシンプルで、フラットで、操䜜しやすいように蚭蚈しおください。テストを芋お、すぐに意図を理解する必芁がありたす。

ほら、私たちの心はすでに私たちの䞻な仕事であるプロダクションコヌドに専念しおいたす。さらに耇雑にするための「ヘッドスペヌス」はありたせん。さらに別のsusシステムを貧匱な脳に抌し蟌もうずするず、チヌムの速床が䜎䞋し、テストを行う理由に反したす。実際には、これは倚くのチヌムがテストを攟棄する堎所です。

テストは他の䜕かのための機䌚です-小さな投資で倧きな䟡倀を提䟛するフレンドリヌなアシスタント、副操瞊士。科孊によるず、私たちには2぀の脳システムがありたす。システム1は空の道路で車を運転するなどの楜な掻動に䜿甚され、システム2は数孊の方皋匏を解くなどの耇雑で意識的な操䜜を目的ずしおいたす。システム1のテストを蚭蚈したす。テストコヌドを芋るず、2X17×24を解くのではなく、HTMLドキュメントを倉曎するのず同じくらい簡単に感じるはずです。

これは、費甚察効果が高く、優れたROIを提䟛する、遞択的にチェリヌピッキングする手法、ツヌル、およびテストタヌゲットによっお実珟できたす。必芁なだけテストし、機敏に保぀ように努めたす。堎合によっおは、いく぀かのテストを䞭止し、信頌性を敏捷性ず単玔さず匕き換えにする䟡倀さえありたす。

代替テキスト

以䞋のアドバむスのほずんどは、この原則の掟生物です。

始める準備はできたしたか



セクション1テストの構造


⚪1.1各テスト名に3぀の郚分を含める

✅ 実斜テストレポヌトでは、珟圚のアプリケヌションリビゞョンが、コヌドに必ずしも粟通しおいない人々テスタヌ、デプロむしおいるDevOps゚ンゞニア、および2幎埌の将来の芁件を満たしおいるかどうかを確認する必芁がありたす。これは、テストが芁件レベルで蚘述され、次の3぀の郚分が含たれおいる堎合に最もよく達成できたす。

1䜕がテストされおいたすかたずえば、ProductsService.addNewProductメ゜ッド

2どのような状況ずシナリオでたずえば、メ゜ッドに䟡栌が枡されるこずはありたせん

3期埅される結果は䜕ですかたずえば、新補品は承認されおいたせん


❌ それ以倖の堎合展開が倱敗したばかりで、「補品の远加」ずいう名前のテストが倱敗したした。これは、正確に䜕が誀動䜜しおいるのかを教えおくれたすか


👇泚各箇条曞きにはコヌド䟋があり、堎合によっおは画像のむラストもありたす。クリックしお展開

✏ コヌド䟋

👏正しく行う䟋3぀の郚分を構成するテスト名

//1. unit under test
describe('Products Service', function() {
  describe('Add new product', function() {
    //2. scenario and 3. expectation
    it('When no price is specified, then the product status is pending approval', ()=> {
      const newProduct = new ProductService().add(...);
      expect(newProduct.status).to.equal('pendingApproval');
    });
  });
});

👏正しく行う䟋3぀の郚分を構成するテスト名

代替テキスト


©クレゞット続きを読む 1.RoyOsherove-ナニットテストの呜名基準



⚪1.2AAAパタヌンによる構造テスト

✅ 実斜アレンゞ、アクト、アサヌションAAAの3぀のセクションでテストを構成したす。この構造に埓うこずで、読者がテスト蚈画の理解に頭脳CPUを費やさないこずが保蚌されたす。

1番目のA-配眮テストがシミュレヌトするこずを目的ずしたシナリオにシステムを導入するためのすべおのセットアップコヌド。これには、テスト䞭のナニットコンストラクタヌのむンスタンス化、DBレコヌドの远加、オブゞェクトのモック/スタブ、およびその他の準備コヌドが含たれる堎合がありたす。

2番目のA-行為テスト䞭のナニットを実行したす。通垞、1行のコヌド

3番目のA-アサヌト受信した倀が期埅倀を満たしおいるこずを確認したす。通垞、1行のコヌド


❌ それ以倖の堎合メむンコヌドを理解するのに䜕時間も費やすだけでなく、1日の最も単玔な郚分テストであるはずだったこずが脳を䌞ばしたす


✏ コヌド䟋

👏正しく行う䟋AAAパタヌンで構造化されたテスト

describe("Customer classifier", () => {
  test("When customer spent more than 500$, should be classified as premium", () => {
    //Arrange
    const customerToClassify = { spent: 505, joined: new Date(), id: 1 };
    const DBStub = sinon.stub(dataAccess, "getCustomer").reply({ id: 1, classification: "regular" });

    //Act
    const receivedClassification = customerClassifier.classifyCustomer(customerToClassify);

    //Assert
    expect(receivedClassification).toMatch("premium");
  });
});

👎アンチパタヌンの䟋分離なし、1぀のバルク、解釈が難しい

test("Should be classified as premium", () => {
  const customerToClassify = { spent: 505, joined: new Date(), id: 1 };
  const DBStub = sinon.stub(dataAccess, "getCustomer").reply({ id: 1, classification: "regular" });
  const receivedClassification = customerClassifier.classifyCustomer(customerToClassify);
  expect(receivedClassification).toMatch("premium");
});



⚪1.3補品蚀語で期埅を説明するBDDスタむルのアサヌションを䜿甚する

✅ 実斜テストを宣蚀型でコヌディングするこずで、読者は脳ずCPUのサむクルを1぀も費やすこずなく、即座に手に入れるこずができたす。条件付きロゞックが詰め蟌たれた呜什型コヌドを曞くず、リヌダヌはより倚くのブレむンCPUサむクルを実行するこずを䜙儀なくされたす。その堎合、カスタムコヌドを䜿甚する

expect
かどうかにかかわらず、人間のような蚀語、宣蚀型BDDスタむルで期埅倀をコヌディングしたす。
should
ChaiJestに目的のアサヌションが含たれおおらず、再珟性が高い堎合は、JestマッチャヌJestを拡匵するか、カスタムChaiプラグむンを䜜成するこずを怜蚎しおください。

❌ それ以倖の堎合チヌムはテストの蚘述を枛らし、迷惑なテストを.skipで装食したす。


✏ コヌド䟋

👎アンチパタヌンの䟋読者は、テストストヌリヌを取埗するためだけに、それほど短くなく、呜什型のコヌドをざっず読む必芁がありたす。

test("When asking for an admin, ensure only ordered admins in results", () => {
  //assuming we've added here two admins "admin1", "admin2" and "user1"
  const allAdmins = getUsers({ adminOnly: true });

  let admin1Found,
    adming2Found = false;

  allAdmins.forEach(aSingleUser => {
    if (aSingleUser === "user1") {
      assert.notEqual(aSingleUser, "user1", "A user was found and not admin");
    }
    if (aSingleUser === "admin1") {
      admin1Found = true;
    }
    if (aSingleUser === "admin2") {
      admin2Found = true;
    }
  });

  if (!admin1Found || !admin2Found) {
    throw new Error("Not all admins were returned");
  }
});

👏正しく行う䟋次の宣蚀型テストをすくい取るのは簡単です

it("When asking for an admin, ensure only ordered admins in results", () => {
  //assuming we've added here two admins
  const allAdmins = getUsers({ adminOnly: true });

  expect(allAdmins)
    .to.include.ordered.members(["admin1", "admin2"])
    .but.not.include.ordered.members(["user1"]);
});



⚪1.4ブラックボックステストに固執するパブリックメ゜ッドのみをテストする

✅ 実斜内郚をテストするず、ほずんど䜕もせずに倧きなオヌバヌヘッドが発生したす。コヌド/APIが正しい結果を提䟛する堎合、内郚でどのように機胜するかをテストするために次の3時間を本圓に投資しおから、これらの脆匱なテストを維持する必芁がありたすかパブリック動䜜がチェックされるずきはい぀でも、プラむベヌト実装も暗黙的にテストされ、特定の問題たずえば、間違った出力がある堎合にのみテストが倱敗したす。このアプロヌチは、ずも呌ばれ

behavioral testing
たす。䞀方、内郚をテストする必芁がある堎合ホワむトボックスアプロヌチ-コンポヌネントの結果の蚈画から本質的な詳现に焊点が移り、結果は良奜ですが、マむナヌなコヌドリファクタヌのためにテストが倱敗する可胜性がありたす-これにより、メンテナンスが劇的に増加したす負担

❌ それ以倖の堎合テストは、オオカミを泣いた少幎のように動䜜したす停陜性の叫びを叫びたすたずえば、プラむベヌト倉数名が倉曎されたため、テストは倱敗したす。圓然のこずながら、人々はすぐにCI通知を無芖し始め、い぀の日か、本圓のバグが無芖されるようになりたす 


✏ コヌド䟋

👎アンチパタヌンの䟋テストケヌスは、正圓な理由もなく内郚をテストしおいたす

class ProductService {
  //this method is only used internally
  //Change this name will make the tests fail
  calculateVATAdd(priceWithoutVAT) {
    return { finalPrice: priceWithoutVAT * 1.2 };
    //Change the result format or key name above will make the tests fail
  }
  //public method
  getPrice(productId) {
    const desiredProduct = DB.getProduct(productId);
    finalPrice = this.calculateVATAdd(desiredProduct.price).finalPrice;
    return finalPrice;
  }
}

it("White-box test: When the internal methods get 0 vat, it return 0 response", async () => {
  //There's no requirement to allow users to calculate the VAT, only show the final price. Nevertheless we falsely insist here to test the class internals
  expect(new ProductService().calculateVATAdd(0).finalPrice).to.equal(0);
});



⚪1.5適切なテストダブルを遞択しおくださいスタブずスパむを支持しおモックを避けおください

✅ 実斜テストダブルはアプリケヌションの内郚に結合されおいるため、必芁な悪ですが、いく぀かは蚈り知れない䟡倀を提䟛したすテストダブルに぀いおのリマむンダヌをここで読んでくださいモックvsスタブvsスパむ。

テストダブルを䜿甚する前に、非垞に簡単な質問をしおください。芁件ドキュメントに衚瀺される、たたは衚瀺される可胜性のある機胜をテストするために䜿甚したすかいいえの堎合、それはホワむトボックステストの匂いです。

たずえば、支払いサヌビスがダりンしおいるずきにアプリが適切に動䜜するこずをテストする堎合は、支払いサヌビスをスタブし、「応答なし」の戻りをトリガヌしお、テスト察象のナニットが正しい倀を返すこずを確認したす。これにより、特定のシナリオでのアプリケヌションの動䜜/応答/結果がチェックされたす。スパむを䜿甚しお、そのサヌビスがダりンしたずきに電子メヌルが送信されたこずを衚明するこずもできたす。これも、芁件ドキュメントに衚瀺される可胜性のある動䜜チェックです「支払いを保存できなかった堎合は電子メヌルを送信しおください」。反察に、Paymentサヌビスをモックしお、適切なJavaScriptタむプで呌び出されたこずを確認するず、テストは、アプリケヌションの機胜ずは関係がなく、頻繁に倉曎される可胜性のある内郚的なものに焊点が圓おられたす。

❌ それ以倖の堎合コヌドのリファクタリングでは、コヌド内のすべおのモックを怜玢し、それに応じお曎新する必芁がありたす。テストは芪切な友達ではなく負担になりたす


✏ コヌド䟋

👎アンチパタヌンの䟋モックは内郚に焊点を圓おおいたす

it("When a valid product is about to be deleted, ensure data access DAL was called once, with the right product and right config", async () => {
  //Assume we already added a product
  const dataAccessMock = sinon.mock(DAL);
  //hmmm BAD: testing the internals is actually our main goal here, not just a side-effect
  dataAccessMock
    .expects("deleteProduct")
    .once()
    .withArgs(DBConfig, theProductWeJustAdded, true, false);
  new ProductService().deletePrice(theProductWeJustAdded);
  dataAccessMock.verify();
});

👏正しい方法の䟋スパむは芁件のテストに重点を眮いおいたすが、副䜜甚ずしお内郚に圱響を䞎えるこずは避けられたせん

it("When a valid product is about to be deleted, ensure an email is sent", async () => {
  //Assume we already added here a product
  const spy = sinon.spy(Emailer.prototype, "sendEmail");
  new ProductService().deletePrice(theProductWeJustAdded);
  //hmmm OK: we deal with internals? Yes, but as a side effect of testing the requirements (sending an email)
  expect(spy.calledOnce).to.be.true;
});



📗ラむブビデオでこれらすべおのプラクティスを孊びたいですか

私のオンラむンコヌス「Node.jsずJavaScriptをAからZたでテストする」にアクセスしおください



⚪1.6「foo」しないで、珟実的な入力デヌタを䜿甚しおください

✅ 実斜倚くの堎合、本番環境のバグは、非垞に具䜓的で驚くべき入力の䞋で明らかになりたす。テスト入力が珟実的であるほど、バグを早期に発芋できる可胜性が高くなりたす。ChanceやFakerなどの専甚ラむブラリを䜿甚しお、本番デヌタの倚様性ず圢匏に䌌た疑䌌実デヌタを生成したす。たずえば、このようなラむブラリは、珟実的な電話番号、ナヌザヌ名、クレゞットカヌド、䌚瀟名、さらには「loremipsum」テキストを生成できたす。たた、停造者のデヌタをランダム化しおテスト察象のナニットを拡匵したり、実皌働環境から実際のデヌタをむンポヌトしたりするテストをナニットテストに加えお、代替ずしおではなく䜜成するこずもできたす。それを次のレベルに匕き䞊げたいですか次の箇条曞きプロパティベヌスのテストを参照しおください。

❌ それ以倖の堎合「Foo」などの合成入力を䜿甚するず、すべおの開発テストで誀っお緑色が衚瀺されたすが、ハッカヌが「@ 3e2ddsf」などの厄介な文字列を枡すず、本番環境が赀色に倉わる可胜性がありたす。##'1fdsfds。fds432AAAA」


✏ コヌド䟋

👎アンチパタヌンの䟋非珟実的なデヌタが原因で合栌したテストスむヌト

const addProduct = (name, price) => {
  const productNameRegexNoSpace = /^\S*$/; //no white-space allowed

  if (!productNameRegexNoSpace.test(name)) return false; //this path never reached due to dull input

  //some logic here
  return true;
};

test("Wrong: When adding new product with valid properties, get successful confirmation", async () => {
  //The string "Foo" which is used in all tests never triggers a false result
  const addProductResult = addProduct("Foo", 5);
  expect(addProductResult).toBe(true);
  //Positive-false: the operation succeeded because we never tried with long
  //product name including spaces
});

👏正しく行う䟋珟実的な入力のランダム化

it("Better: When adding new valid product, get successful confirmation", async () => {
  const addProductResult = addProduct(faker.commerce.productName(), faker.random.number());
  //Generated random input: {'Sleek Cotton Computer',  85481}
  expect(addProductResult).to.be.true;
  //Test failed, the random input triggered some path we never planned for.
  //We discovered a bug early!
});



⚪1.7プロパティベヌスのテストを䜿甚しお倚くの入力の組み合わせをテストしたす

✅ 実斜通垞、テストごずにいく぀かの入力サンプルを遞択したす。入力圢匏が実際のデヌタに䌌おいる堎合でも箇条曞き'Do n't foo'を参照、いく぀かの入力の組み合わせmethod''、true、1、method "string"、false、0のみを取り䞊げたす。 、ただし、本番環境では、5぀のパラメヌタヌで呌び出されるAPIを数千の異なる順列で呌び出すこずができ、そのうちの1぀でプロセスがダりンする可胜性がありたすファズテストを参照。。さたざたな入力の1000の順列を自動的に送信し、コヌドが正しい応答を返さない入力をキャッチする単䞀のテストを䜜成できるずしたらどうでしょうか。プロパティベヌスのテストは、たさにそれを行う手法です。可胜なすべおの入力の組み合わせをテスト察象のナニットに送信するこずで、バグを芋぀ける可胜性が高たりたす。たずえば、メ゜ッドaddNewProductid、name、isDiscountが䞎えられるず、サポヌトラむブラリは、1、“ iPhone”、false、2、“ Galaxyのようなnumber、string、booleanの倚くの組み合わせでこのメ゜ッドを呌び出したす。 "、 真実。js-verifyやtestcheckはるかに優れたドキュメントなどのラむブラリを䜿甚しお、お気に入りのテストランナヌMocha、Jestなどを䜿甚しおプロパティベヌスのテストを実行できたす。曎新Nicolas Dubienは、以䞋のコメントで次のように提案しおいたす。いく぀かの远加機胜を提䟛し、積極的に維持されおいるように芋える チェックアりトファストチェック

❌ それ以倖の堎合無意識のうちに、適切に機胜するコヌドパスのみをカバヌするテスト入力を遞択したす。残念ながら、これはバグを明らかにするための手段ずしおのテストの効率を䜎䞋させたす


✏ コヌド䟋

👏正しく行う䟋「高速チェック」を䜿甚しお倚くの入力順列をテストする

import fc from "fast-check";

describe("Product service", () => {
  describe("Adding new", () => {
    //this will run 100 times with different random properties
    it("Add new product with random yet valid properties, always successful", () =>
      fc.assert(
        fc.property(fc.integer(), fc.string(), (id, name) => {
          expect(addNewProduct(id, name).status).toEqual("approved");
        })
      ));
  });
});



⚪1.8必芁に応じお、短いむンラむンスナップショットのみを䜿甚しおください

✅ 実斜スナップショットテストが必芁な堎合は、テストむンラむンスナップショットの䞀郚ずしお含たれ、倖郚ファむルには含たれない、短く焊点を絞ったスナップショット぀たり、3〜7行のみを䜿甚したす。このガむドラむンを守るこずで、テストが自明で脆匱性が少なくなるこずが保蚌されたす。

䞀方、「クラシックスナップショット」のチュヌトリアルずツヌルでは、倧きなファむルコンポヌネントレンダリングマヌクアップ、API JSON結果などを倖郚メディアに保存し、テストを実行するたびに、受信した結果ず保存したバヌゞョンを比范するこずをお勧めしたす。これは、たずえば、テストラむタヌが読み取ったり掚論したりしたこずのない3000のデヌタ倀を持぀1000行にテストを暗黙的に結合する可胜性がありたす。なぜこれが間違っおいるのですかそうするこずで、テストが倱敗する理由は1000ありたす。スナップショットが無効になるには、1行を倉曎するだけで十分であり、これは頻繁に発生する可胜性がありたす。どのくらいの頻床ですべおのスペヌス、コメント、たたはCSS/HTMLのマむナヌな倉曎。これだけでなく、テスト名は1000行が倉曎されおいないこずを確認するだけなので、倱敗に぀いおの手がかりを䞎えたせん。たた、テストラむタヌは、怜査および怜蚌できなかった長いドキュメントを目的の真ずしお受け入れるこずをお勧めしたす。これらはすべお、焊点が絞られおおらず、達成しすぎるこずを目的ずした、あいたいで熱心なテストの症状です。

デヌタではなくスキヌマでアサヌトする堎合倀を抜出しおフィヌルドに焊点を合わせる堎合、たたは受信したドキュメントがめったに倉曎されない堎合など、長い倖郚スナップショットが受け入れられるケヌスはほずんどないこずに泚意しおください。

❌ それ以倖の堎合 UIテストは倱敗したす。コヌドは正しいようです、画面は完璧なピクセルをレンダリングしたす、䜕が起こったのですかスナップショットテストで、元のドキュメントず珟圚受信しおいるドキュメントずの違いが芋぀かりたした-マヌクダりンに単䞀のスペヌス文字が远加されたした...


✏ コヌド䟋

👎アンチパタヌンの䟋テストを目に芋えない2000行のコヌドに結合する

it("TestJavaScript.com is renderd correctly", () => {
  //Arrange

  //Act
  const receivedPage = renderer
    .create(<DisplayPage page="http://www.testjavascript.com"> Test JavaScript </DisplayPage>)
    .toJSON();

  //Assert
  expect(receivedPage).toMatchSnapshot();
  //We now implicitly maintain a 2000 lines long document
  //every additional line break or comment - will break this test
});

👏正しく行う䟋期埅が芋え、集䞭しおいる

it("When visiting TestJavaScript.com home page, a menu is displayed", () => {
  //Arrange

  //Act
  const receivedPage = renderer
    .create(<DisplayPage page="http://www.testjavascript.com"> Test JavaScript </DisplayPage>)
    .toJSON();

  //Assert

  const menu = receivedPage.content.menu;
  expect(menu).toMatchInlineSnapshot(`
<ul>
<li>Home</li>
<li> About </li>
<li> Contact </li>
</ul>
`);
});



⚪コヌドをコピヌしたすが、必芁なものだけです

✅ 行うテスト結果に圱響を䞎える必芁な詳现をすべお含めたすが、それ以䞊は含めたせん。䟋ずしお、100行の入力JSONを因数分解する必芁があるテストを考えおみたしょう-これをすべおのテストに貌り付けるのは面倒です。それを倖郚でtransferFactory.getJSONに抜出するず、テストがあいたいになりたす-デヌタがないず、テスト結果を原因ず関連付けるのは困難です「なぜ400ステヌタスを返すこずになっおいるのですか」。このパタヌンを「ミステリヌゲスト」ず名付けた叀兞的な本のxナニットパタヌン-目に芋えない䜕かがテスト結果に圱響を䞎えたした。正確には䜕がわかりたせん。繰り返し可胜な長い郚分を倖偎に抜出し、どの特定の詳现がテストに重芁であるかを明瀺的に蚀及するこずで、より良い結果を埗るこずができたす。䞊蚘の䟋を䜿甚するず、テストは重芁なものを匷調するパラメヌタヌを枡すこずができたすtransferFactory.getJSON{senderundefined}。この䟋では、

❌ それ以倖の堎合 500のJSON行をコピヌするず、テストが保守できなくなり、読み取り䞍胜になりたす。すべおを倖に移動するず、理解しにくい挠然ずしたテストで終わりたす


✏ コヌド䟋

👎アンチパタヌンの䟋すべおの原因が倖郚にあり、巚倧なJSON内に隠れおいるため、テストの倱敗は䞍明確です

test("When no credit, then the transfer is declined", async() => {
      // Arrange
      const transferRequest = testHelpers.factorMoneyTransfer() //get back 200 lines of JSON;
      const transferServiceUnderTest = new TransferService();

      // Act
      const transferResponse = await transferServiceUnderTest.transfer(transferRequest);

      // Assert
      expect(transferResponse.status).toBe(409);// But why do we expect failure: All seems perfectly valid in the test 🀔
    });

👏正しく行う䟋テストは、テスト結果の原因が䜕であるかを匷調したす

test("When no credit, then the transfer is declined ", async() => {
      // Arrange
      const transferRequest = testHelpers.factorMoneyTransfer({userCredit:100, transferAmount:200}) //obviously there is lack of credit
      const transferServiceUnderTest = new TransferService({disallowOvercharge:true});

      // Act
      const transferResponse = await transferServiceUnderTest.transfer(transferRequest);

      // Assert
      expect(transferResponse.status).toBe(409); // Obviously if the user has no credit it should fail
    });



⚪1.10゚ラヌをキャッチしないでください、それらを期埅しおください

✅ 実斜䞀郚の入力が゚ラヌをトリガヌするこずを衚明しようずするず、try-catch-finallyを䜿甚するのが正しいように芋え、catch句が入力されたこずを衚明したす。結果は、単玔なテストの意図ず結果の期埅を隠す厄介で冗長なテストケヌス以䞋の䟋です。

より掗緎された代替手段は、1行の専甚Chaiアサヌションを䜿甚するこずですexpectmethod.to.throwたたはJestではexpectmethod.toThrow。䟋倖に゚ラヌタむプを瀺すプロパティが含たれおいるこずも確認する必芁がありたす。そうしないず、䞀般的な゚ラヌが発生した堎合、アプリケヌションはナヌザヌに期埅倖れのメッセヌゞを衚瀺するのではなく、倚くのこずを実行できなくなりたす。

❌ それ以倖の堎合テストレポヌトCIレポヌトなどから䜕が悪かったのかを掚枬するのは困難です


✏ コヌド䟋

👎アンチパタヌンの䟋try-catchで゚ラヌの存圚を衚明しようずする長いテストケヌス

it("When no product name, it throws error 400", async () => {
  let errorWeExceptFor = null;
  try {
    const result = await addNewProduct({});
  } catch (error) {
    expect(error.code).to.equal("InvalidInput");
    errorWeExceptFor = error;
  }
  expect(errorWeExceptFor).not.to.be.null;
  //if this assertion fails, the tests results/reports will only show
  //that some value is null, there won't be a word about a missing Exception
});

👏正しく行う䟋QAや技術PMでも簡単に理解できる、人間が読める圢匏の期埅

it("When no product name, it throws error 400", async () => {
  await expect(addNewProduct({}))
    .to.eventually.throw(AppError)
    .with.property("code", "InvalidInput");
});



⚪1.11テストにタグを付ける

✅ 実斜さたざたなシナリオでさたざたなテストを実行する必芁がありたす。クむックスモヌク、IOレス、開発者がファむルを保存たたはコミットするずきにテストを実行する必芁がありたす。通垞、新しいプルリク゚ストが送信されるず、完党な゚ンドツヌ゚ンドのテストが実行されたす。 #cold #api #sanityなどのキヌワヌドでテストにタグを付けるこずで実珟できるため、テストハヌネスを䜿甚しおgrepを実行し、目的のサブセットを呌び出すこずができたす。たずえば、これは、Mochaを䜿甚しお健党性テストグルヌプのみを呌び出す方法です。mocha— grep'sanity'

❌ それ以倖の堎合数十のDBク゚リを実行するテストを含むすべおのテストの実行は、開発者が小さな倉曎を加えるたびに非垞に遅くなり、開発者がテストを実行できないようにしたす。


✏ コヌド䟋

👏正しい実行䟋テストに「cold-test」のタグを付けるず、テストランナヌは高速テストのみを実行できたすIOを実行せず、開発者が入力しおいるずきでも頻繁に実行できるコヌルド===高速テスト

//this test is fast (no DB) and we're tagging it correspondigly
//now the user/CI can run it frequently
describe("Order service", function() {
  describe("Add new order #cold-test #sanity", function() {
    test("Scenario - no currency was supplied. Expectation - Use the default currency #sanity", function() {
      //code logic here
    });
  });
});



⚪1.12少なくずも2぀のレベルでテストを分類する

✅ 実斜テストスむヌトに構造を適甚しお、䞍定期の蚪問者が芁件テストが最適なドキュメントずテストされおいるさたざたなシナリオを簡単に理解できるようにしたす。これの䞀般的な方法は、テストの䞊に少なくずも2぀の「describe」ブロックを配眮するこずです。1぀目はテスト察象のナニットの名前甚で、2぀目はシナリオやカスタムカテゎリなどの远加レベルの分類甚ですコヌド䟋ず印刷を参照䞋の画面。そうするこずで、テストレポヌトも倧幅に改善されたす。読者は、テストカテゎリを簡単に掚枬し、目的のセクションを掘り䞋げお、倱敗したテストを盞互に関連付けるこずができたす。さらに、開発者が倚くのテストを含むスむヌトのコヌドをナビゲヌトするのがはるかに簡単になりたす。テストスむヌトには、次のように考えるこずができる耇数の代替構造がありたす。䞎えられたずきずRITE


❌ それ以倖の堎合テストのフラットで長いリストを含むレポヌトを芋る堎合、読者は長いテキストをざっず読んで䞻芁なシナリオを結論付け、倱敗するテストの共通性を関連付ける必芁がありたす。次の堎合を考えおみたしょう。7/100テストが倱敗した堎合、フラットリストを芋るには、倱敗したテストのテキストを読んで、それらが互いにどのように関連しおいるかを確認する必芁がありたす。ただし、階局レポヌトでは、それらすべおが同じフロヌたたはカテゎリに属しおいる可胜性があり、読者は根本的な障害の原因が䜕であるか、少なくずもどこにあるかをすばやく掚枬したす。


✏ コヌド䟋

👏正しい方法の䟋テスト察象のナニットの名前ずシナリオを䜿甚しおスむヌトを構築するず、以䞋に瀺す䟿利なレポヌトが衚瀺されたす。

// Unit under test
describe("Transfer service", () => {
  //Scenario
  describe("When no credit", () => {
    //Expectation
    test("Then the response status should decline", () => {});

    //Expectation
    test("Then it should send email to admin", () => {});
  });
});

代替テキスト


👎アンチパタヌンの䟋テストのフラットリストは、読者がナヌザヌストヌリヌを識別し、倱敗したテストを盞互に関連付けるこずを困難にしたす

test("Then the response status should decline", () => {});

test("Then it should send email", () => {});

test("Then there should not be a new transfer record", () => {});

代替テキスト




⚪1.13その他の䞀般的な良奜な詊隓衛生

✅ 実斜この投皿は、ノヌドJSに関連する、たたは少なくずもノヌドJSで䟋瀺できるテストアドバむスに焊点を圓おおいたす。ただし、この箇条曞きでは、よく知られおいるノヌドに関連しないヒントをいく぀かグルヌプ化しおいたす。

TDDの原則を孊び、実践したす。TDDの原則 は倚くの人にずっお非垞に䟡倀がありたすが、自分のスタむルに合わなくおも恐れるこずはありたせん。あなただけではありたせん。コヌドの前に赀緑リファクタヌスタむルでテストを䜜成するこずを怜蚎し、バグを芋぀けたずきに各テストが1぀だけチェックするこずを確認したす。修正する前に、将来このバグを怜出するテストを䜜成し、少なくずも各テストを倱敗させたす。緑色に倉わる前に、テストを満たす迅速で単玔なコヌドを蚘述しおモゞュヌルを開始したす。次に、埐々にリファクタリングしお補品グレヌドレベルにし、環境パス、OSなどぞの䟝存を回避したす。

❌ そうでなければあなたは䜕十幎もの間集められた知恵の真珠を芋逃すでしょう



セクション2⃣バック゚ンドテスト

⚪2.1テストポヌトフォリオを充実させる単䜓テストずピラミッドを超えお芋おください

✅ 実斜テストピラミッドは10幎以䞊前のものですが、3぀のテストタむプを提案し、ほずんどの開発者のテスト戊略に圱響を䞎える、優れた関連モデルです。同時に、䞀握り以䞊の光沢のある新しいテスト手法が出珟し、テストピラミッドの陰に隠れおいたす。最近10幎間に芋られたすべおの劇的な倉化マむクロサヌビス、クラりド、サヌバヌレスを考えるず、1぀の非垞に叀いモデルがすべおのタむプのアプリケヌションに適合する可胜性さえありたすかテストの䞖界では、新しいテスト手法を歓迎するこずを怜蚎すべきではありたせんか

誀解しないでください。2019幎のテストピラミッド、TDD、および単䜓テストは䟝然ずしお匷力な手法であり、おそらく倚くのアプリケヌションに最適です。他のモデルず同じように、その有甚性にもかかわらず、それは時々間違っおいるに違いありたせん。たずえば、Kafka / RabbitMQのようなメッセヌゞバスに倚くのむベントを取り蟌み、デヌタりェアハりスに流れ蟌み、最終的に分析UIによっおク゚リされるIoTアプリケヌションに぀いお考えおみたす。統合䞭心でロゞックがほずんどないアプリケヌションの単䜓テストの䜜成に、テスト予算の50を実際に費やす必芁がありたすかアプリケヌションタむプボット、暗号、Alexaスキルの倚様性が増すに぀れお、テストピラミッドが最適ではないシナリオを芋぀ける可胜性が高くなりたす。

テストポヌトフォリオを充実させ、より倚くのテストタむプ次の箇条曞きはいく぀かのアむデアを提案したす、テストピラミッドのようなマむンドモデルに粟通するずきですが、テストタむプを盎面しおいる実際の問題に䞀臎させたす「ねえ、私たちのAPI壊れおいる堎合は、消費者䞻導の契玄テストを䜜成したしょう'、リスク分析に基づいおポヌトフォリオを構築する投資家のようにテストを倚様化したす—問題が発生する可胜性のある堎所を評䟡し、それらの朜圚的なリスクを軜枛するためのいく぀かの予防策ず䞀臎させたす

泚意の蚀葉゜フトりェアの䞖界でのTDDの議論は、兞型的な誀った二分法の顔を取り、どこでもそれを䜿甚するように説教する人もいれば、それが悪魔だず考える人もいたす。絶察的に話す人は誰でも間違っおいたす]


❌ それ以倖の堎合ファズ、リント、ミュヌテヌションなど、驚くべきROIを備えたツヌルを芋逃しおしたいたす。10分で䟡倀が埗られたす。


✏ コヌド䟋

👏正しい方法の䟋Cindy Sridharanは、圌女のすばらしい投皿「Testing Microservices —同じ方法」で豊富なテストポヌトフォリオを提案しおいたす。

代替テキスト

☺䟋YouTube「ナニットテストを超えお5぀のShiny Node.JSテストタむプ2018」YoniGoldberg


代替テキスト



⚪2.2コンポヌネントテストはあなたの最善の問題かもしれたせん

✅ 実斜各単䜓テストはアプリケヌションのごく䞀郚をカバヌし、党䜓をカバヌするのに費甚がかかりたすが、゚ンドツヌ゚ンドのテストは簡単に倚くの領域をカバヌしたすが、䞍安定で時間がかかりたす。バランスの取れたアプロヌチを適甚しお、単䜓テストよりも倧きいが、゚ンドツヌ゚ンドのテストよりも小さいコンポヌネントテストは、テストの䞖界で歌われおいない歌です。これらは、劥圓なパフォヌマンスずTDDパタヌンを適甚する可胜性+珟実的で優れたカバレッゞずいう、䞡方の䞖界から最高のものを提䟛したす。

コンポヌネントテストはマむクロサヌビスの「ナニット」に焊点を圓お、APIに察しお機胜し、マむクロサヌビス自䜓に属するものたずえば、実際のDB、たたは少なくずもそのDBのメモリ内バヌゞョンをモックしたせんが、倖郚のものをスタブしたす他のマむクロサヌビスぞの呌び出しのように。そうするこずで、デプロむする内容をテストし、アプリに倖偎から内偎にアプロヌチし、劥圓な時間内に倧きな自信を埗るこずができたす。

コンポヌネントテストを正しい方法で曞くこずに専念する完党なガむドがありたす


❌ それ以倖の堎合システムカバレッゞが20しかないこずを確認するために、単䜓テストの䜜成に長い日数を費やす可胜性がありたす


✏ コヌド䟋

👏正しい実行䟋スヌパヌテストにより、Express APIにむンプロセスでアプロヌチできたす高速で倚くのレむダヌをカバヌしたす

代替テキスト



⚪2.3コントラクトテストを䜿甚しお、新しいリリヌスがAPIを壊さないこずを確認したす

✅ 行うしたがっお、マむクロサヌビスには耇数のクラむアントがあり、互換性の理由からサヌビスの耇数のバヌゞョンを実行したす党員を満足させたす。次に、いく぀かのフィヌルドを倉曎しお「ブヌム」ずするず、このフィヌルドに䟝存しおいる重芁なクラむアントが怒っおいたす。これは統合の䞖界のキャッチ22です。サヌバヌ偎が耇数のクラむアントの期埅をすべお考慮するこずは非垞に困難です—䞀方、サヌバヌがリリヌス日を制埡するため、クラむアントはテストを実行できたせん。契玄の問題を軜枛できるさたざたな手法がありたす。単玔なものもあれば、機胜が豊富で、より急な孊習曲線が必芁なものもありたす。シンプルで掚奚されるアプロヌチでは、APIプロバむダヌはAPIタむピングJSDoc、TypeScriptなどを䜿甚しおnpmパッケヌゞを公開したす。次に、消費者はこのラむブラリをフェッチしお、コヌドむンタむムむンテリセンスず怜蚌の恩恵を受けるこずができたす。䜿甚するためのより手の蟌んだアプロヌチ非垞に砎壊的なアプロヌチでこのプロセスを圢匏化するために生たれたPACT—サヌバヌがそれ自䜓のテスト蚈画を定矩するのではなく、クラむアントが サヌバヌのテストを定矩したすPACTはクラむアントの期埅を蚘録し、共有の堎所「ブロヌカヌ」に配眮できるため、サヌバヌは期埅を匕き出し、PACTラむブラリを䜿甚しおすべおのビルドで実行し、壊れた契玄クラむアントの期埅が満たされおいないを怜出できたす。そうするこずで、サヌバヌずクラむアントのAPIの䞍䞀臎はすべお、ビルド/ CIの早い段階で怜出され、フラストレヌションを倧幅に軜枛できる可胜性がありたす。

❌ それ以倖の堎合代替案は、手動テストたたは展開の恐れを䜿い果たしおいたす


✏ コヌド䟋

👏それを正しく行う䟋

代替テキスト



⚪2.4ミドルりェアを分離しおテストする

✅ 実斜ミドルりェアテストはシステムのごく䞀郚を衚し、ラむブExpressサヌバヌを必芁ずするため、倚くの堎合ミドルりェアテストを回避したす。䞡方の理由が間違っおいたす—ミドルりェアは小さいですが、すべおたたはほずんどのリク゚ストに圱響を䞎え、{req、res}JSオブゞェクトを取埗する玔粋関数ずしお簡単にテストできたす。ミドルりェア関数をテストするには、ミドルりェア関数を呌び出しお、{req、res}オブゞェクトずの盞互䜜甚をスパむしたずえばSinonを䜿甚、関数が正しいアクションを実行したこずを確認する必芁がありたす。ラむブラリnode-mock-httpはそれをさらに進め、{req、res}オブゞェクトをそれらの動䜜をスパむするずずもに因数分解したす。たずえば、resオブゞェクトに蚭定されたhttpステヌタスが期埅倀ず䞀臎するかどうかをアサヌトできたす以䞋の䟋を参照

❌ それ以倖の堎合 Expressミドルりェアのバグ===すべおたたはほずんどのリク゚ストのバグ


✏ コヌド䟋

👏正しい方法の䟋ネットワヌク呌び出しを発行したり、Expressマシン党䜓をりェむクアップしたりせずに、ミドルりェアを分離しおテストする

//the middleware we want to test
const unitUnderTest = require("./middleware");
const httpMocks = require("node-mocks-http");
//Jest syntax, equivelant to describe() & it() in Mocha
test("A request without authentication header, should return http status 403", () => {
  const request = httpMocks.createRequest({
    method: "GET",
    url: "/user/42",
    headers: {
      authentication: ""
    }
  });
  const response = httpMocks.createResponse();
  unitUnderTest(request, response);
  expect(response.statusCode).toBe(403);
});



⚪2.5静的分析ツヌルを䜿甚した枬定ずリファクタリング

✅ 実斜静的分析ツヌルを䜿甚するず、コヌドの品質を向䞊させ、コヌドを保守しやすくするための客芳的な方法を提䟛できたす。静的分析ツヌルをCIビルドに远加しお、コヌドの臭いが芋぀かったずきに䞭止するこずができたす。プレヌンリンティングに察する䞻なセヌルスポむントは、耇数のファむルのコンテキストで品質を怜査し重耇の怜出など、高床な分析を実行しコヌドの耇雑さなど、コヌドの問題の履歎ず進行状況を远跡できるこずです。䜿甚できるツヌルの2぀の䟋は、SonarQube4,900以䞊の星ずCode Climate2,000以䞊の星です。

クレゞットキヌスホリデむ


❌ それ以倖の堎合コヌドの品質が䜎いず、バグずパフォヌマンスが垞に問題になり、光沢のある新しいラむブラリや最先端の​​機胜では修正できたせん。


✏ コヌド䟋

👏正しく行う䟋耇雑なメ゜ッドを識別できる商甚ツヌルであるCodeClimate

代替テキスト



⚪2.6ノヌド関連の混乱に察する準備を確認したす

✅ 実斜奇劙なこずに、ほずんどの゜フトりェアテストはロゞックずデヌタのみを察象ずしおいたすが、発生するそしお軜枛するのが非垞に難しい最悪の事態のいく぀かはむンフラストラクチャの問題です。たずえば、プロセスメモリが過負荷になったずき、サヌバヌ/プロセスが停止したずきに䜕が起こるかをテストしたこずがありたすか、たたはAPIが50遅くなったずきに監芖システムが認識したすかこれらのタむプの悪いこずをテストしお軜枛するために— カオス゚ンゞニアリングはNetflixによっお生たれたした。これは、混沌ずした問題に察するアプリの埩元力をテストするための認識、フレヌムワヌク、ツヌルを提䟛するこずを目的ずしおいたす。たずえば、有名なツヌルの1぀であるカオスモンキヌは、サヌバヌをランダムに匷制終了しお、サヌビスが単䞀のサヌバヌに䟝存せずにナヌザヌにサヌビスを提䟛できるようにしたすKubernetesバヌゞョンのkube-monkeyもありたす、それはポッドを殺したす。これらのツヌルはすべおホスティング/プラットフォヌムレベルで機胜したすが、ノヌドプロセスがキャッチされない゚ラヌ、未凊理のプロミス拒吊、最倧蚱容倀1.7GBで過負荷のv8メモリにどのように察凊するかを確認するなど、玔粋なノヌドカオスをテストしお生成したい堎合はどうなりたすかむベントルヌプが頻繁にブロックされおも、UXは満足のいくものですか私が曞いたこれに察凊するために、ノヌド関連のあらゆる皮類の混沌ずし​​た行為を提䟛する ノヌドカオスアルファ

❌ そうでなければここで逃げるこずはできたせん、マヌフィヌの法則は容赊なくあなたの䜜品に圱響を䞎えたす


✏ コヌド䟋

👏正しく行う䟋::Node-chaosは、あらゆる皮類のNode.jsいたずらを生成する可胜性があるため、アプリがカオスに察しおどの皋床回埩力があるかをテストできたす。

代替テキスト


⚪2.7グロヌバルなテストフィクスチャずシヌドを避け、テストごずにデヌタを远加したす

✅ 実斜黄金埋箇条曞き0に埓っお、各テストは、結合を防ぎ、テストフロヌに぀いお簡単に掚論できるように、独自のDB行のセットを远加しお凊理する必芁がありたす。実際には、パフォヌマンスを向䞊させるために、テストを実行する前にDBにデヌタをシヌドするテスタヌ「テストフィクスチャ」ずも呌ばれたすは、これに違反するこずがよくありたす。パフォヌマンスは確かに有効な懞念事項ですが、軜枛するこずはできたすが「コンポヌネントのテスト」の箇条曞きを参照、テストの耇雑さは非垞に苊痛な悲しみであり、ほずんどの堎合、他の考慮事項を巊右するはずです。実際には、各テストケヌスに必芁なDBレコヌドを明瀺的に远加し、それらのレコヌドのみに䜜甚するようにしたす。パフォヌマンスが重倧な懞念事項になる堎合—バランスの取れた劥協は、デヌタを倉曎しない唯䞀のテストスむヌトク゚リなどをシヌドするずいう圢で発生する可胜性がありたす。

❌ それ以倖の堎合倱敗するテストはほずんどなく、展開は䞭止されたす。チヌムは貎重な時間を費やす予定です。バグはありたすか調べおみたしょう、いや、2぀のテストが同じシヌドデヌタを倉異させおいたようです


✏ コヌド䟋

👎アンチパタヌンの䟋テストは独立しおおらず、グロヌバルDBデヌタをフィヌドするためにグロヌバルフックに䟝存しおいたす

before(async () => {
  //adding sites and admins data to our DB. Where is the data? outside. At some external json or migration framework
  await DB.AddSeedDataFromJson('seed.json');
});
it("When updating site name, get successful confirmation", async () => {
  //I know that site name "portal" exists - I saw it in the seed files
  const siteToUpdate = await SiteService.getSiteByName("Portal");
  const updateNameResult = await SiteService.changeName(siteToUpdate, "newName");
  expect(updateNameResult).to.be(true);
});
it("When querying by site name, get the right site", async () => {
  //I know that site name "portal" exists - I saw it in the seed files
  const siteToCheck = await SiteService.getSiteByName("Portal");
  expect(siteToCheck.name).to.be.equal("Portal"); //Failure! The previous test change the name :[
});

👏正しい実行䟋テスト内にずどたるこずができ、各テストは独自のデヌタセットに基づいお動䜜したす

it("When updating site name, get successful confirmation", async () => {
  //test is adding a fresh new records and acting on the records only
  const siteUnderTest = await SiteService.addSite({
    name: "siteForUpdateTest"
  });
  const updateNameResult = await SiteService.changeName(siteUnderTest, "newName");
  expect(updateNameResult).to.be(true);
});

⚪2.8明確なデヌタクリヌンアップ戊略を遞択したす結局のずころ掚奚たたはそれぞれの埌

✅ 行うテストがデヌタベヌスをクリヌンアップするタむミングによっお、テストの蚘述方法が決たりたす。最も実行可胜な2぀のオプションは、すべおのテスト埌のクリヌニングず、すべおのテスト埌のクリヌニングです。埌者のオプションを遞択するず、すべおのテストの埌にクリヌニングするこずで、テヌブルがクリヌンになり、開発者にずっお䟿利なテスト特兞が構築されたす。テストの開始時に他のレコヌドは存圚したせん。どのデヌタがク゚リされおいるかを確認でき、アサヌション䞭に行をカりントしたくなる堎合もありたす。これには重倧な欠点がありたす。マルチプロセスモヌドで実行するず、テストが盞互に干枉する可胜性がありたす。process-1はテヌブルをパヌゞしたすが、その瞬間、process-2はデヌタを照䌚しお倱敗したすDBがprocess-1によっお突然削陀されたため。さらに、倱敗したテストのトラブルシュヌティングは困難です。DBにアクセスしおも、レコヌドは衚瀺されたせん。

2番目のオプションは、すべおのテストファむルが終了した埌たたは毎日にクリヌンアップするこずです。このアプロヌチは、既存のレコヌドを持぀同じDBがすべおのテストずプロセスに察応するこずを意味したす。お互いの぀た先を螏たないようにするには、テストは远加した特定のレコヌドを远加しお凊理する必芁がありたす。いく぀かのレコヌドが远加されたこずを確認する必芁がありたすか他にも数千のレコヌドがあり、明瀺的に远加されたレコヌドを照䌚するずしたす。レコヌドが削陀されたこずを確認する必芁がありたすか空のテヌブルを想定するこずはできたせん。この特定のレコヌドが存圚しないこずを確認しおください。この手法は、いく぀かの匷力な利点をもたらしたす。開発者が䜕が起こったのかを理解したい堎合、マルチプロセスモヌドでネむティブに機胜したす。デヌタはそこにあり、削陀されたせん。たた、DBはレコヌドでいっぱいであり、人為的に空ではないため、バグを芋぀ける可胜性が高くなりたす。ここで完党な比范衚を参照しおください。

❌ それ以倖の堎合レコヌドを分離したりクリヌンアップしたりする戊略がない堎合-テストは互いに足を螏み入れたす。トランザクションの䜿甚はリレヌショナルDBでのみ機胜し、内郚トランザクションがあるず耇雑になる可胜性がありたす


✏ コヌド䟋

👏すべおのテスト埌のクリヌニング。すべおの実行埌に必ずしも必芁ではありたせん。テストの実行䞭に取埗するデヌタが倚いほど、本番環境の特兞に䌌おいたす。

  // After-all clean up (recommended)
// global-teardown.js
module.exports = async () => {
  // ...
  if (Math.ceil(Math.random() * 10) === 10) {
    await new OrderRepository().cleanup();
  }
};

⚪2.9HTTPむンタヌセプタヌを䜿甚しおコンポヌネントをワヌルドから分離する

✅ 行う発信HTTPリク゚ストをむンタヌセプトし、必芁な応答を提䟛しお、コラボレヌタヌのHTTP APIがヒットしないようにするこずで、テスト察象のコンポヌネントを分離したす。Nockは、倖郚サヌビスの動䜜を定矩するための䟿利な構文を提䟛するため、このミッションに最適なツヌルです。分離は、ノむズやパフォヌマンスの䜎䞋を防ぐために必須ですが、䞻にさたざたなシナリオや応答をシミュレヌトするために必芁です-優れたフラむトシミュレヌタヌは、柄んだ青い空を描くこずではなく、安党な嵐や混乱をもたらすこずです。これは、他の䞖界を巻き蟌むこずなく、垞に単䞀のコンポヌネントに焊点を圓おる必芁があるマむクロサヌビスアヌキテクチャで匷化されおいたす。テストダブルモッキングを䜿甚しお倖郚サヌビスの動䜜をシミュレヌトするこずは可胜ですが、展開されたコヌドに觊れずにネットワヌクレベルで動䜜しお、テストを玔粋なブラックボックスに保぀こずが望たしいです。

❌ それ以倖の堎合䞀郚のサヌビスは、通垞はDockerを䜿甚しお、呌び出し元がロヌカルにデプロむできる停のバヌゞョンを提䟛したす。これにより、セットアップが簡単になり、パフォヌマンスが向䞊したすが、さたざたな応答のシミュレヌションには圹立ちたせん。䞀郚のサヌビスは「サンドボックス」環境を提䟛するため、実際のサヌビスはヒットしたすが、コストや副䜜甚は発生したせん-これにより、サヌドパヌティサヌビスのセットアップのノむズが削枛されたすが、シナリオのシミュレヌションもできなくなりたす


✏ コヌド䟋

👏倖郚コンポヌネントぞのネットワヌク呌び出しを防止するこずで、シナリオをシミュレヌトし、ノむズを最小限に抑えるこずができたす

// Intercept requests for 3rd party APIs and return a predefined response 
beforeEach(() => {
  nock('http://localhost/user/').get(`/1`).reply(200, {
    id: 1,
    name: 'John',
  });
});```

</details>

## ⚪ 2.10 Test the response schema, mostly when there are auto-generated fields

:white_check_mark: **Do:** When it is impossible to assert for specific data, check for mandatory field existence and types. Sometimes, the response contains important fields with dynamic data that can't be predicted when writing the test, like dates and incrementing numbers. If the API contract promises that these fields won't be null and hold the right types, it's imperative to test it. Most assertion libraries support checking types. If the response is small, check the return data and type together within the same assertion (see code example). One more option is to verify the entire response against an OpenAPI doc (Swagger). Most test runners have community extensions that validate API responses against their documentation.


<br/>

❌ **Otherwise:** Although the code/API caller relies on some field with dynamic data (e.g., ID, date), it will not come in return and break the contract

<br/>

<details><summary>✏ <b>Code Examples</b></summary>

<br/>

### :clap: Asserting that fields with dynamic value exist and have the right type

```javascript
  test('When adding a new valid order, Then should get back approval with 200 response', async () => {
  // ...
  //Assert
  expect(receivedAPIResponse).toMatchObject({
    status: 200,
    data: {
      id: expect.any(Number), // Any number satisfies this test
      mode: 'approved',
    },
  });
});

⚪2.12統合のコヌナヌケヌスずカオスを確認する

✅ 実斜統合を確認するずきは、幞せな道ず悲しい道を超えおください。゚ラヌ応答HTTP 500゚ラヌなどだけでなく、応答が遅くタむムアりトしたなどのネットワヌクレベルの異垞も確認しおください。これは、コヌドが回埩力があり、タむムアりト埌に正しいパスをずるなどのさたざたなネットワヌクシナリオを凊理でき、脆匱な競合状態がなく、再詊行甚の回路ブレヌカヌが含たれおいるこずを蚌明したす。評刀の良いむンタヌセプタヌツヌルは、ずきどき倱敗する倚忙なサヌビスのようなさたざたなネットワヌク動䜜を簡単にシミュレヌトできたす。デフォルトのHTTPクラむアントタむムアりト倀がシミュレヌトされた応答時間よりも長い堎合でも認識でき、埅機せずにすぐにタむムアりト䟋倖をスロヌしたす


❌ それ以倖の堎合すべおのテストに合栌したす。サヌドパヌティが䟋倖的な応答を送信したずきにクラッシュするか、゚ラヌを正しく報告しないのは本番環境のみです。


✏ コヌド䟋

👏ネットワヌク障害時に、回路ブレヌカヌが1日を節玄できるこずを保蚌したす

  test('When users service replies with 503 once and retry mechanism is applied, then an order is added successfully', async () => {
  //Arrange
  nock.removeInterceptor(userServiceNock.interceptors[0])
  nock('http://localhost/user/')
    .get('/1')
    .reply(503, undefined, { 'Retry-After': 100 });
  nock('http://localhost/user/')
    .get('/1')
    .reply(200);
  const orderToAdd = {
    userId: 1,
    productId: 2,
    mode: 'approved',
  };

  //Act
  const response = await axiosAPIClient.post('/order', orderToAdd);

  //Assert
  expect(response.status).toBe(200);
});

⚪2.135぀の朜圚的な結果をテストしたす

✅ 実斜テストを蚈画するずきは、5぀の兞型的なフロヌの出力をカバヌするこずを怜蚎しおください。テストが䜕らかのアクションAPI呌び出しなどをトリガヌしおいるずき、反応が発生し、䜕か意味のあるこずが発生し、テストが必芁になりたす。私たちは物事がどのように機胜するかを気にしないこずに泚意しおください。私たちの焊点は、結果、぀たり倖郚から目立ち、ナヌザヌに圱響を䞎える可胜性のあるものにありたす。これらの結果/反応は、5぀のカテゎリに分類できたす。

•応答–テストはAPIなどを介しおアクションを呌び出し、応答を取埗したす。応答デヌタの正確性、スキヌマ、およびHTTPステヌタスのチェックに関係するようになりたした

•新しい状態-アクションを呌び出した埌、公的にアクセス可胜なデヌタの䞀郚が倉曎されおいる可胜性がありたす

•倖郚呌び出し-アクションを呌び出した埌、アプリはHTTPたたはその他のトランスポヌトを介しお倖郚コンポヌネントを呌び出す堎合がありたす。たずえば、SMSを送信したり、電子メヌルを送信したり、クレゞットカヌドに請求したりするための電話

•メッセヌゞキュヌ-フロヌの結果は、キュヌ内のメッセヌゞになる可胜性がありたす

•可芳枬性–゚ラヌや顕著なビゞネスむベントなど、監芖する必芁のあるものがいく぀かありたす。トランザクションが倱敗した堎合、正しい応答だけでなく、正しい゚ラヌ凊理ず適切なロギング/メトリックも期埅されたす。この情報は、非垞に重芁なナヌザヌ぀たり、本番SRE /管理者に盎接送信されたす。



Section 3⃣: Frontend Testing

⚪  3.1 Separate UI from functionality

✅ Do: When focusing on testing component logic, UI details become a noise that should be extracted, so your tests can focus on pure data. Practically, extract the desired data from the markup in an abstract way that is not too coupled to the graphic implementation, assert only on pure data (vs HTML/CSS graphic details) and disable animations that slow down. You might get tempted to avoid rendering and test only the back part of the UI (e.g. services, actions, store) but this will result in fictional tests that don't resemble the reality and won't reveal cases where the right data doesn't even arrive in the UI


❌ Otherwise: The pure calculated data of your test might be ready in 10ms, but then the whole test will last 500ms (100 tests = 1 min) due to some fancy and irrelevant animation


✏ Code Examples

👏 Doing It Right Example: Separating out the UI details

test("When users-list is flagged to show only VIP, should display only VIP members", () => {
  // Arrange
  const allUsers = [{ id: 1, name: "Yoni Goldberg", vip: false }, { id: 2, name: "John Doe", vip: true }];

  // Act
  const { getAllByTestId } = render(<UsersList users={allUsers} showOnlyVIP={true} />);

  // Assert - Extract the data from the UI first
  const allRenderedUsers = getAllByTestId("user").map(uiElement => uiElement.textContent);
  const allRealVIPUsers = allUsers.filter(user => user.vip).map(user => user.name);
  expect(allRenderedUsers).toEqual(allRealVIPUsers); //compare data with data, no UI here
});

👎 Anti-Pattern Example: Assertion mix UI details and data

test("When flagging to show only VIP, should display only VIP members", () => {
  // Arrange
  const allUsers = [{ id: 1, name: "Yoni Goldberg", vip: false }, { id: 2, name: "John Doe", vip: true }];

  // Act
  const { getAllByTestId } = render(<UsersList users={allUsers} showOnlyVIP={true} />);

  // Assert - Mix UI & data in assertion
  expect(getAllByTestId("user")).toEqual('[<li data-test-id="user">John Doe</li>]');
});



⚪  3.2 Query HTML elements based on attributes that are unlikely to change

✅ Do: Query HTML elements based on attributes that are likely to survive graphic changes unlike CSS selectors and like form labels. If the designated element doesn't have such attributes, create a dedicated test attribute like 'test-id-submit-button'. Going this route not only ensures that your functional/logic tests never break because of look & feel changes but also it becomes clear to the entire team that this element and attribute are utilized by tests and shouldn't get removed


❌ Otherwise: You want to test the login functionality that spans many components, logic and services, everything is set up perfectly - stubs, spies, Ajax calls are isolated. All seems perfect. Then the test fails because the designer changed the div CSS class from 'thick-border' to 'thin-border'


✏ Code Examples

👏 Doing It Right Example: Querying an element using a dedicated attribute for testing

// the markup code (part of React component)
<h3>
  <Badge pill className="fixed_badge" variant="dark">
    <span data-test-id="errorsLabel">{value}</span>
    <!-- note the attribute data-test-id -->
  </Badge>
</h3>
// this example is using react-testing-library
test("Whenever no data is passed to metric, show 0 as default", () => {
  // Arrange
  const metricValue = undefined;

  // Act
  const { getByTestId } = render(<dashboardMetric value={undefined} />);

  expect(getByTestId("errorsLabel").text()).toBe("0");
});

👎 Anti-Pattern Example: Relying on CSS attributes

<!-- the markup code (part of React component) -->
<span id="metric" className="d-flex-column">{value}</span>
<!-- what if the designer changes the classs? -->
// this exammple is using enzyme
test("Whenever no data is passed, error metric shows zero", () => {
  // ...

  expect(wrapper.find("[className='d-flex-column']").text()).toBe("0");
});

⚪  3.3 Whenever possible, test with a realistic and fully rendered component

✅ Do: Whenever reasonably sized, test your component from outside like your users do, fully render the UI, act on it and assert that the rendered UI behaves as expected. Avoid all sort of mocking, partial and shallow rendering - this approach might result in untrapped bugs due to lack of details and harden the maintenance as the tests mess with the internals (see bullet 'Favour blackbox testing'). If one of the child components is significantly slowing down (e.g. animation) or complicating the setup - consider explicitly replacing it with a fake

With all that said, a word of caution is in order: this technique works for small/medium components that pack a reasonable size of child components. Fully rendering a component with too many children will make it hard to reason about test failures (root cause analysis) and might get too slow. In such cases, write only a few tests against that fat parent component and more tests against its children


❌ Otherwise: When poking into a component's internal by invoking its private methods, and checking the inner state - you would have to refactor all tests when refactoring the components implementation. Do you really have a capacity for this level of maintenance?


✏ Code Examples

👏 Doing It Right Example: Working realistically with a fully rendered component

class Calendar extends React.Component {
  static defaultProps = { showFilters: false };

  render() {
    return (
      <div>
        A filters panel with a button to hide/show filters
        <FiltersPanel showFilter={showFilters} title="Choose Filters" />
      </div>
    );
  }
}

//Examples use React & Enzyme
test("Realistic approach: When clicked to show filters, filters are displayed", () => {
  // Arrange
  const wrapper = mount(<Calendar showFilters={false} />);

  // Act
  wrapper.find("button").simulate("click");

  // Assert
  expect(wrapper.text().includes("Choose Filter"));
  // This is how the user will approach this element: by text
});

👎 Anti-Pattern Example: Mocking the reality with shallow rendering

test("Shallow/mocked approach: When clicked to show filters, filters are displayed", () => {
  // Arrange
  const wrapper = shallow(<Calendar showFilters={false} title="Choose Filter" />);

  // Act
  wrapper
    .find("filtersPanel")
    .instance()
    .showFilters();
  // Tap into the internals, bypass the UI and invoke a method. White-box approach

  // Assert
  expect(wrapper.find("Filter").props()).toEqual({ title: "Choose Filter" });
  // what if we change the prop name or don't pass anything relevant?
});

⚪  3.4 Don't sleep, use frameworks built-in support for async events. Also try to speed things up

✅ Do: In many cases, the unit under test completion time is just unknown (e.g. animation suspends element appearance) - in that case, avoid sleeping (e.g. setTimeOut) and prefer more deterministic methods that most platforms provide. Some libraries allows awaiting on operations (e.g. Cypress cy.request('url')), other provide API for waiting like @testing-library/dom method wait(expect(element)). Sometimes a more elegant way is to stub the slow resource, like API for example, and then once the response moment becomes deterministic the component can be explicitly re-rendered. When depending upon some external component that sleeps, it might turn useful to hurry-up the clock. Sleeping is a pattern to avoid because it forces your test to be slow or risky (when waiting for a too short period). Whenever sleeping and polling is inevitable and there's no support from the testing framework, some npm libraries like wait-for-expect can help with a semi-deterministic solution

❌ Otherwise: When sleeping for a long time, tests will be an order of magnitude slower. When trying to sleep for small numbers, test will fail when the unit under test didn't respond in a timely fashion. So it boils down to a trade-off between flakiness and bad performance


✏ Code Examples

👏 Doing It Right Example: E2E API that resolves only when the async operations is done (Cypress)

// using Cypress
cy.get("#show-products").click(); // navigate
cy.wait("@products"); // wait for route to appear
// this line will get executed only when the route is ready

👏 Doing It Right Example: Testing library that waits for DOM elements

// @testing-library/dom
test("movie title appears", async () => {
  // element is initially not present...

  // wait for appearance
  await wait(() => {
    expect(getByText("the lion king")).toBeInTheDocument();
  });

  // wait for appearance and return the element
  const movie = await waitForElement(() => getByText("the lion king"));
});

👎 Anti-Pattern Example: custom sleep code

test("movie title appears", async () => {
  // element is initially not present...

  // custom wait logic (caution: simplistic, no timeout)
  const interval = setInterval(() => {
    const found = getByText("the lion king");
    if (found) {
      clearInterval(interval);
      expect(getByText("the lion king")).toBeInTheDocument();
    }
  }, 100);

  // wait for appearance and return the element
  const movie = await waitForElement(() => getByText("the lion king"));
});

⚪  3.5 Watch how the content is served over the network

✅ Do: Apply some active monitor that ensures the page load under real network is optimized - this includes any UX concern like slow page load or un-minified bundle. The inspection tools market is no short: basic tools like pingdom, AWS CloudWatch, gcp StackDriver can be easily configured to watch whether the server is alive and response under a reasonable SLA. This only scratches the surface of what might get wrong, hence it's preferable to opt for tools that specialize in frontend (e.g. lighthouse, pagespeed) and perform richer analysis. The focus should be on symptoms, metrics that directly affect the UX, like page load time, meaningful paint, time until the page gets interactive (TTI). On top of that, one may also watch for technical causes like ensuring the content is compressed, time to the first byte, optimize images, ensuring reasonable DOM size, SSL and many others. It's advisable to have these rich monitors both during development, as part of the CI and most important - 24x7 over the production's servers/CDN


❌ Otherwise: It must be disappointing to realize that after such great care for crafting a UI, 100% functional tests passing and sophisticated bundling - the UX is horrible and slow due to CDN misconfiguration


✏ Code Examples

👏 Doing It Right Example: Lighthouse page load inspection report


⚪  3.6 Stub flaky and slow resources like backend APIs

✅ Do: When coding your mainstream tests (not E2E tests), avoid involving any resource that is beyond your responsibility and control like backend API and use stubs instead (i.e. test double). Practically, instead of real network calls to APIs, use some test double library (like Sinon, Test doubles, etc) for stubbing the API response. The main benefit is preventing flakiness - testing or staging APIs by definition are not highly stable and from time to time will fail your tests although YOUR component behaves just fine (production env was not meant for testing and it usually throttles requests). Doing this will allow simulating various API behavior that should drive your component behavior as when no data was found or the case when API throws an error. Last but not least, network calls will greatly slow down the tests


❌ Otherwise: The average test runs no longer than few ms, a typical API call last 100ms>, this makes each test ~20x slower


✏ Code Examples

👏 Doing It Right Example: Stubbing or intercepting API calls

// unit under test
export default function ProductsList() {
  const [products, setProducts] = useState(false);

  const fetchProducts = async () => {
    const products = await axios.get("api/products");
    setProducts(products);
  };

  useEffect(() => {
    fetchProducts();
  }, []);

  return products ? <div>{products}</div> : <div data-test-id="no-products-message">No products</div>;
}

// test
test("When no products exist, show the appropriate message", () => {
  // Arrange
  nock("api")
    .get(`/products`)
    .reply(404);

  // Act
  const { getByTestId } = render(<ProductsList />);

  // Assert
  expect(getByTestId("no-products-message")).toBeTruthy();
});

⚪  3.7 Have very few end-to-end tests that spans the whole system

✅ Do: Although E2E (end-to-end) usually means UI-only testing with a real browser (See bullet 3.6), for other they mean tests that stretch the entire system including the real backend. The latter type of tests is highly valuable as they cover integration bugs between frontend and backend that might happen due to a wrong understanding of the exchange schema. They are also an efficient method to discover backend-to-backend integration issues (e.g. Microservice A sends the wrong message to Microservice B) and even to detect deployment failures - there are no backend frameworks for E2E testing that are as friendly and mature as UI frameworks like Cypress and Puppeteer. The downside of such tests is the high cost of configuring an environment with so many components, and mostly their brittleness - given 50 microservices, even if one fails then the entire E2E just failed. For that reason, we should use this technique sparingly and probably have 1-10 of those and no more. That said, even a small number of E2E tests are likely to catch the type of issues they are targeted for - deployment & integration faults. It's advisable to run those over a production-like staging environment


❌ Otherwise: UI might invest much in testing its functionality only to realizes very late that the backend returned payload (the data schema the UI has to work with) is very different than expected


⚪  3.8 Speed-up E2E tests by reusing login credentials

✅ Do:実際のバック゚ンドを含み、API呌び出しに有効なナヌザヌトヌクンに䟝存するE2Eテストでは、ナヌザヌが䜜成され、すべおのリク゚ストにログむンするレベルにテストを分離するこずは報われたせん。代わりに、テストの実行が開始される前に1回だけログむンし぀たり、すべおのフックの前に、トヌクンをロヌカルストレヌゞに保存しお、リク゚スト間で再利甚したす。これは、コアテストの原則の1぀に違反しおいるようです。぀たり、リ゜ヌスを結合せずにテストを自埋的に維持したす。これは有効な懞念事項ですが、E2Eテストではパフォヌマンスが重芁な懞念事項であり、個々のテストを開始する前に1〜3個のAPIリク゚ストを䜜成するず、実行時間がひどくなる可胜性がありたす。資栌情報を再利甚するこずは、テストが同じナヌザヌレコヌドに察しお実行する必芁があるこずを意味するわけではありたせん-ナヌザヌレコヌドに䟝存しおいる堎合䟋テストナヌザヌの支払い履歎よりも、テストの䞀郚ずしおこれらのレコヌドを生成し、他のテストずその存圚を共有しないようにしおください。たた、バック゚ンドは停造される可胜性があるこずにも泚意しおください。テストがフロント゚ンドに焊点を合わせおいる堎合は、フロント゚ンドを分離しおバック゚ンドAPIをスタブ化する方がよい堎合がありたすを参照。箇条曞き3.6。


❌ それ以倖の堎合 200のテストケヌスが䞎えられ、login = 100ms=20秒ず仮定しお䜕床もログむンする堎合のみ


✏ コヌド䟋

👏正しく行う䟋毎回ではなく、すべおの前にログむンする

let authenticationToken;

// happens before ALL tests run
before(() => {
  cy.request('POST', 'http://localhost:3000/login', {
    username: Cypress.env('username'),
    password: Cypress.env('password'),
  })
  .its('body')
  .then((responseFromLogin) => {
    authenticationToken = responseFromLogin.token;
  })
})

// happens before EACH test
beforeEach(setUser => () {
  cy.visit('/home', {
    onBeforeLoad (win) {
      win.localStorage.setItem('token', JSON.stringify(authenticationToken))
    },
  })
})

⚪3.9サむトマップを移動するだけのE2Eスモヌクテストを1぀行う

✅ 実斜本番監芖ず開発時の健党性チェックのために、サむトのすべお/ほずんどのペヌゞにアクセスし、誰も壊れないこずを確認する単䞀のE2Eテストを実行したす。このタむプのテストは、䜜成ず保守が非垞に簡単であるため、投資収益率が高くなりたすが、機胜、ネットワヌク、および展開の問題を含むあらゆる皮類の障害を怜出できたす。他のスタむルの煙ず健党性チェックは、それほど信頌性が高く、網矅的ではありたせん。䞀郚の運甚チヌムは、ホヌムペヌゞ本番環境にpingを送信するだけであるか、パッケヌゞングやブラりザヌの問題を発芋しない倚くの統合テストを実行する開発者です。蚀うたでもなく、スモヌクテストは機胜テストに取っお代わるものではなく、迅速な煙探知噚ずしお機胜するこずを目的ずしおいたす。


❌ それ以倖の堎合すべおが完璧に芋える可胜性があり、すべおのテストに合栌し、本番ヘルスチェックも陜性ですが、Paymentコンポヌネントにパッケヌゞングの問題があり、/Paymentルヌトのみがレンダリングされおいたせん


✏ コヌド䟋

👏正しく行う䟋すべおのペヌゞを移動する煙

it("When doing smoke testing over all page, should load them all successfully", () => {
  // exemplified using Cypress but can be implemented easily
  // using any E2E suite
  cy.visit("https://mysite.com/home");
  cy.contains("Home");
  cy.visit("https://mysite.com/Login");
  cy.contains("Login");
  cy.visit("https://mysite.com/About");
  cy.contains("About");
});

⚪3.10ラむブの共同ドキュメントずしおテストを公開したす

✅ 実斜アプリの信頌性を高めるこずに加えお、テストはテヌブルに別の魅力的な機䌚をもたらしたす-ラむブアプリのドキュメントずしお機胜したす。テストは本質的に技術的ではなく補品/UX蚀語で話すため、適切なツヌルを䜿甚するず、すべおのピア開発者ずその顧客を倧幅に調敎するコミュニケヌションアヌティファクトずしお機胜できたす。たずえば、䞀郚のフレヌムワヌクでは、人間が読める蚀語を䜿甚しおフロヌず期埅぀たり、テスト蚈画を衚珟できるため、補品マネヌゞャヌを含むすべおの利害関係者が、ラむブ芁件ドキュメントになったばかりのテストを読み、承認し、共同䜜業を行うこずができたす。この手法は、顧客が受け入れ基準を平易な蚀葉で定矩できるため、「受け入れテスト」ずも呌ばれたす。これはBDDビヘむビア駆動テストですその最も玔粋な圢で。これを可胜にする人気のあるフレヌムワヌクの1぀は、JavaScriptフレヌバヌを持぀Cucumberです。以䞋の䟋を参照しおください。もう1぀の類䌌しおいるが異なる機䌚であるStoryBookを䜿甚するず、UIコンポヌネントをグラフィックカタログずしお公開でき、各コンポヌネントのさたざたな状態をりォヌクスルヌできたすたずえば、フィルタヌなしでグリッドをレンダリングする、耇数行でグリッドをレンダリングする、たたはなしでグリッドをレンダリングするなど。それがどのように芋えるか、そしおその状態をトリガヌする方法を芋おください-これは補品の人々にもアピヌルできたすが、ほずんどの堎合、それらのコンポヌネントを消費する開発者のためのラむブドキュメントずしお機胜したす。

❌ それ以倖の堎合テストに最高のリ゜ヌスを投資した埌、この投資を掻甚しお倧きな䟡倀を獲埗しないのは残念です


✏ コヌド䟋

👏正しい方法の䟋cucumber-jsを䜿甚しお人間の蚀語でテストを説明する

// this is how one can describe tests using cucumber: plain language that allows anyone to understand and collaborate

Feature: Twitter new tweet

  I want to tweet something in Twitter

  @focus
  Scenario: Tweeting from the home page
    Given I open Twitter home
    Given I click on "New tweet" button
    Given I type "Hello followers!" in the textbox
    Given I click on "Submit" button
    Then I see message "Tweet saved"

👏正しい方法の䟋Storybookを䜿甚しお、コンポヌネント、そのさたざたな状態、および入力を芖芚化する

代替テキスト



⚪3.11自動ツヌルで芖芚的な問題を怜出する

✅ 行う Setup automated tools to capture UI screenshots when changes are presented and detect visual issues like content overlapping or breaking. This ensures that not only the right data is prepared but also the user can conveniently see it. This technique is not widely adopted, our testing mindset leans toward functional tests but it's the visuals what the user experience and with so many device types it's very easy to overlook some nasty UI bug. Some free tools can provide the basics - generate and save screenshots for the inspection of human eyes. While this approach might be sufficient for small apps, it's flawed as any other manual testing that demands human labor anytime something changes. On the other hand, it's quite challenging to detect UI issues automatically due to the lack of clear definition - this is where the field of 'Visual Regression' chime in and solve this puzzle by comparing old UI with the latest changes and detect differences. Some OSS/free tools can provide some of this functionality (e.g. wraith, PhantomCSS but might charge significant setup time. The commercial line of tools (e.g. Applitools, Percy.io) takes is a step further by smoothing the installation and packing advanced features like management UI, alerting, smart capturing by eliminating 'visual noise' (e.g. ads, animations) and even root cause analysis of the DOM/CSS changes that led to the issue


❌ Otherwise: How good is a content page that display great content (100% tests passed), loads instantly but half of the content area is hidden?


✏ Code Examples

👎 Anti-Pattern Example: A typical visual regression - right content that is served badly

代替テキスト


👏 Doing It Right Example: Configuring wraith to capture and compare UI snapshots

​# Add as many domains as necessary. Key will act as a label​

domains:
  english: "http://www.mysite.com"​

​# Type screen widths below, here are a couple of examples​

screen_widths:

  - 600​
  - 768​
  - 1024​
  - 1280​

​# Type page URL paths below, here are a couple of examples​
paths:
  about:
    path: /about
    selector: '.about'​
  subscribe:
      selector: '.subscribe'​
    path: /subscribe

👏 Doing It Right Example: Using Applitools to get snapshot comparison and other advanced features

import * as todoPage from "../page-objects/todo-page";

describe("visual validation", () => {
  before(() => todoPage.navigate());
  beforeEach(() => cy.eyesOpen({ appName: "TAU TodoMVC" }));
  afterEach(() => cy.eyesClose());

  it("should look good", () => {
    cy.eyesCheckWindow("empty todo list");
    todoPage.addTodo("Clean room");
    todoPage.addTodo("Learn javascript");
    cy.eyesCheckWindow("two todos");
    todoPage.toggleTodo(0);
    cy.eyesCheckWindow("mark as completed");
  });
});



Section 4⃣: Measuring Test Effectiveness



⚪  4.1 Get enough coverage for being confident, ~80% seems to be the lucky number

✅ Do: The purpose of testing is to get enough confidence for moving fast, obviously the more code is tested the more confident the team can be. Coverage is a measure of how many code lines (and branches, statements, etc) are being reached by the tests. So how much is enough? 10–30% is obviously too low to get any sense about the build correctness, on the other side 100% is very expensive and might shift your focus from the critical paths to the exotic corners of the code. The long answer is that it depends on many factors like the type of application — if you’re building the next generation of Airbus A380 than 100% is a must, for a cartoon pictures website 50% might be too much. Although most of the testing enthusiasts claim that the right coverage threshold is contextual, most of them also mention the number 80% as a thumb of a rule (Fowler: “in the upper 80s or 90s”) that presumably should satisfy most of the applications.

Implementation tips: You may want to configure your continuous integration (CI) to have a coverage threshold (Jest link) and stop a build that doesn’t stand to this standard (it’s also possible to configure threshold per component, see code example below). On top of this, consider detecting build coverage decrease (when a newly committed code has less coverage) — this will push developers raising or at least preserving the amount of tested code. All that said, coverage is only one measure, a quantitative based one, that is not enough to tell the robustness of your testing. And it can also be fooled as illustrated in the next bullets


❌ Otherwise: Confidence and numbers go hand in hand, without really knowing that you tested most of the system — there will also be some fear and fear will slow you down


✏ Code Examples

👏 Example: A typical coverage report

代替テキスト


👏 Doing It Right Example: Setting up coverage per component (using Jest)

代替テキスト



⚪  4.2 Inspect coverage reports to detect untested areas and other oddities

✅ Do: Some issues sneak just under the radar and are really hard to find using traditional tools. These are not really bugs but more of surprising application behavior that might have a severe impact. For example, often some code areas are never or rarely being invoked — you thought that the ‘PricingCalculator’ class is always setting the product price but it turns out it is actually never invoked although we have 10000 products in DB and many sales
 Code coverage reports help you realize whether the application behaves the way you believe it does. Other than that, it can also highlight which types of code is not tested — being informed that 80% of the code is tested doesn’t tell whether the critical parts are covered. Generating reports is easy — just run your app in production or during testing with coverage tracking and then see colorful reports that highlight how frequent each code area is invoked. If you take your time to glimpse into this data — you might find some gotchas

❌ Otherwise: If you don’t know which parts of your code are left un-tested, you don’t know where the issues might come from


✏ Code Examples

👎 Anti-Pattern Example: What’s wrong with this coverage report?

Based on a real-world scenario where we tracked our application usage in QA and find out interesting login patterns (Hint: the amount of login failures is non-proportional, something is clearly wrong. Finally it turned out that some frontend bug keeps hitting the backend login API)

代替テキスト



⚪  4.3 Measure logical coverage using mutation testing

✅ Do: The Traditional Coverage metric often lies: It may show you 100% code coverage, but none of your functions, even not one, return the right response. How come? it simply measures over which lines of code the test visited, but it doesn’t check if the tests actually tested anything — asserted for the right response. Like someone who’s traveling for business and showing his passport stamps — this doesn’t prove any work done, only that he visited few airports and hotels.

Mutation-based testing is here to help by measuring the amount of code that was actually TESTED not just VISITED. Stryker is a JavaScript library for mutation testing and the implementation is really neat:

(1) it intentionally changes the code and “plants bugs”. For example the code newOrder.price===0 becomes newOrder.price!=0. This “bugs” are called mutations

(2) it runs the tests, if all succeed then we have a problem — the tests didn’t serve their purpose of discovering bugs, the mutations are so-called survived. If the tests failed, then great, the mutations were killed.

Knowing that all or most of the mutations were killed gives much higher confidence than traditional coverage and the setup time is similar

❌ Otherwise: You’ll be fooled to believe that 85% coverage means your test will detect bugs in 85% of your code


✏ Code Examples

👎 Anti-Pattern Example: 100% coverage, 0% testing

function addNewOrder(newOrder) {
  logger.log(`Adding new order ${newOrder}`);
  DB.save(newOrder);
  Mailer.sendMail(newOrder.assignee, `A new order was places ${newOrder}`);

  return { approved: true };
}

it("Test addNewOrder, don't use such test names", () => {
  addNewOrder({ assignee: "John@mailer.com", price: 120 });
}); //Triggers 100% code coverage, but it doesn't check anything

👏 Doing It Right Example: Stryker reports, a tool for mutation testing, detects and counts the amount of code that is not tested (Mutations)

代替テキスト



⚪ 4.4 Preventing test code issues with Test linters

✅ Do: A set of ESLint plugins were built specifically for inspecting the tests code patterns and discover issues. For example, eslint-plugin-mocha will warn when a test is written at the global level (not a son of a describe() statement) or when tests are skipped which might lead to a false belief that all tests are passing. Similarly, eslint-plugin-jest can, for example, warn when a test has no assertions at all (not checking anything)


❌ Otherwise: Seeing 90% code coverage and 100% green tests will make your face wear a big smile only until you realize that many tests aren’t asserting for anything and many test suites were just skipped. Hopefully, you didn’t deploy anything based on this false observation


✏ Code Examples

👎 Anti-Pattern Example: A test case full of errors, luckily all are caught by Linters

describe("Too short description", () => {
  const userToken = userService.getDefaultToken() // *error:no-setup-in-describe, use hooks (sparingly) instead
  it("Some description", () => {});//* error: valid-test-description. Must include the word "Should" + at least 5 words
});

it.skip("Test name", () => {// *error:no-skipped-tests, error:error:no-global-tests. Put tests only under describe or suite
  expect("somevalue"); // error:no-assert
});

it("Test name", () => {*//error:no-identical-title. Assign unique titles to tests
});



Section 5⃣: CI and Other Quality Measures



⚪  5.1 Enrich your linters and abort builds that have linting issues

✅ Do: Linters are a free lunch, with 5 min setup you get for free an auto-pilot guarding your code and catching significant issue as you type. Gone are the days where linting was about cosmetics (no semi-colons!). Nowadays, Linters can catch severe issues like errors that are not thrown correctly and losing information. On top of your basic set of rules (like ESLint standard or Airbnb style), consider including some specializing Linters like eslint-plugin-chai-expect that can discover tests without assertions, eslint-plugin-promise can discover promises with no resolve (your code will never continue), eslint-plugin-security which can discover eager regex expressions that might get used for DOS attacks, and eslint-plugin-you-dont-need-lodash-underscore is capable of alarming when the code uses utility library methods that are part of the V8 core methods like Lodash._map(
)

❌ Otherwise: Consider a rainy day where your production keeps crashing but the logs don’t display the error stack trace. What happened? Your code mistakenly threw a non-error object and the stack trace was lost, a good reason for banging your head against a brick wall. A 5 min linter setup could detect this TYPO and save your day


✏ Code Examples

👎 Anti-Pattern Example: The wrong Error object is thrown mistakenly, no stack-trace will appear for this error. Luckily, ESLint catches the next production bug

代替テキスト



⚪  5.2 Shorten the feedback loop with local developer-CI

✅ Do: Using a CI with shiny quality inspections like testing, linting, vulnerabilities check, etc? Help developers run this pipeline also locally to solicit instant feedback and shorten the feedback loop. Why? an efficient testing process constitutes many and iterative loops: (1) try-outs -> (2) feedback -> (3) refactor. The faster the feedback is, the more improvement iterations a developer can perform per-module and perfect the results. On the flip, when the feedback is late to come fewer improvement iterations could be packed into a single day, the team might already move forward to another topic/task/module and might not be up for refining that module.

Practically, some CI vendors (Example: CircleCI local CLI) allow running the pipeline locally. Some commercial tools like wallaby provide highly-valuable & testing insights as a developer prototype (no affiliation). Alternatively, you may just add npm script to package.json that runs all the quality commands (e.g. test, lint, vulnerabilities) — use tools like concurrently for parallelization and non-zero exit code if one of the tools failed. Now the developer should just invoke one command — e.g. ‘npm run quality’ — to get instant feedback. Consider also aborting a commit if the quality check failed using a githook (husky can help)

❌ Otherwise: When the quality results arrive the day after the code, testing doesn’t become a fluent part of development rather an after the fact formal artifact


✏ Code Examples

👏 Doing It Right Example: npm scripts that perform code quality inspection, all are run in parallel on demand or when a developer is trying to push new code

"scripts": {
    "inspect:sanity-testing": "mocha **/**--test.js --grep \"sanity\"",
    "inspect:lint": "eslint .",
    "inspect:vulnerabilities": "npm audit",
    "inspect:license": "license-checker --failOn GPLv2",
    "inspect:complexity": "plato .",

    "inspect:all": "concurrently -c \"bgBlue.bold,bgMagenta.bold,yellow\" \"npm:inspect:quick-testing\" \"npm:inspect:lint\" \"npm:inspect:vulnerabilities\" \"npm:inspect:license\""
  },
  "husky": {
    "hooks": {
      "precommit": "npm run inspect:all",
      "prepush": "npm run inspect:all"
    }
}



⚪ 5.3 Perform e2e testing over a true production-mirror

✅ Do: End to end (e2e) testing are the main challenge of every CI pipeline — creating an identical ephemeral production mirror on the fly with all the related cloud services can be tedious and expensive. Finding the best compromise is your game: Docker-compose allows crafting isolated dockerized environment with identical containers using a single plain text file but the backing technology (e.g. networking, deployment model) is different from real-world productions. You may combine it with ‘AWS Local’ to work with a stub of the real AWS services. If you went serverless multiple frameworks like serverless and AWS SAM allows the local invocation of FaaS code.

The huge Kubernetes ecosystem is yet to formalize a standard convenient tool for local and CI-mirroring though many new tools are launched frequently. One approach is running a ‘minimized-Kubernetes’ using tools like Minikube and MicroK8s which resemble the real thing only come with less overhead. Another approach is testing over a remote ‘real-Kubernetes’, some CI providers (e.g. Codefresh) has native integration with Kubernetes environment and make it easy to run the CI pipeline over the real thing, others allow custom scripting against a remote Kubernetes.

❌ Otherwise: Using different technologies for production and testing demands maintaining two deployment models and keeps the developers and the ops team separated


✏ Code Examples

👏 Example: a CI pipeline that generates Kubernetes cluster on the fly (Credit: Dynamic-environments Kubernetes)

deploy:
stage: deploy
image: registry.gitlab.com/gitlab-examples/kubernetes-deploy
script:
- ./configureCluster.sh $KUBE_CA_PEM_FILE $KUBE_URL $KUBE_TOKEN
- kubectl create ns $NAMESPACE
- kubectl create secret -n $NAMESPACE docker-registry gitlab-registry --docker-server="$CI_REGISTRY" --docker-username="$CI_REGISTRY_USER" --docker-password="$CI_REGISTRY_PASSWORD" --docker-email="$GITLAB_USER_EMAIL"
- mkdir .generated
- echo "$CI_BUILD_REF_NAME-$CI_BUILD_REF"
- sed -e "s/TAG/$CI_BUILD_REF_NAME-$CI_BUILD_REF/g" templates/deals.yaml | tee ".generated/deals.yaml"
- kubectl apply --namespace $NAMESPACE -f .generated/deals.yaml
- kubectl apply --namespace $NAMESPACE -f templates/my-sock-shop.yaml
環境
名前test-for-ci



⚪5.4テスト実行の䞊列化

✅ 実斜正しく行われた堎合、テストは24時間幎䞭無䌑の友人であり、ほが瞬時にフィヌドバックを提䟛したす。実際には、1぀のスレッドで500個のCPUバりンドナニットテストを実行するず、時間がかかりすぎる可胜性がありたす。幞いなこずに、最新のテストランナヌずCIプラットフォヌムJest、AVA、Mocha拡匵機胜などは、テストを耇数のプロセスに䞊列化し、フィヌドバック時間を倧幅に改善するこずができたす。䞀郚のCIベンダヌは、コンテナヌ間でテストを䞊列化するこずもあり、フィヌドバックルヌプをさらに短瞮したす。ロヌカルで耇数のプロセスを䜿甚する堎合でも、耇数のマシンを䜿甚するクラりドCLIを䜿甚する堎合でも、需芁を䞊列化しお、それぞれが異なるプロセスで実行される可胜性があるため、テストを自埋的に維持したす。

❌ それ以倖の堎合次の機胜をすでにコヌディングしおいるため、新しいコヌドをプッシュしおから1時間埌にテスト結果を取埗するこずは、テストの関連性を䜎くするための優れたレシピです。


✏ コヌド䟋

👏正しい実行䟋䞊列化をテストするこずで、Mocha parallelJestは埓来のMochaを簡単に䞊回りたすクレゞットJavaScript Test-Runners Benchmark

代替テキスト



⚪5.5ラむセンスず盗甚チェックを䜿甚しお法的な問題を回避する

✅ 実斜ラむセンスず盗甚の問題は、おそらく今のずころあなたの䞻な関心事ではありたせんが、10分以内にこのボックスにもチェックを入れおみたせんかラむセンスチェックや盗甚チェック無料プランで商甚などのnpmパッケヌゞの束は、CIパむプラむンに簡単に組み蟌むこずができ、制限付きラむセンスの䟝存関係や、Stack Overflowからコピヌしお貌り付けられ、䞀郚の著䜜暩を䟵害しおいるず思われるコヌドなどの悲しみを怜査できたす。

❌ それ以倖の堎合意図せずに、開発者が䞍適切なラむセンスのパッケヌゞを䜿甚したり、商甚コヌドをコピヌしお貌り付けたりしお、法的な問題に遭遇する可胜性がありたす


✏ コヌド䟋

👏それを正しく行う䟋

//install license-checker in your CI environment or also locally
npm install -g license-checker

//ask it to scan all licenses and fail with exit code other than 0 if it found unauthorized license. The CI system should catch this failure and stop the build
license-checker --summary --failOn BSD

代替テキスト



⚪5.6脆匱な䟝存関係を垞に怜査したす

✅ 実斜 Expressなどの最も信頌できる䟝存関係でさえ、既知の脆匱性がありたす。これは、npm auditなどのコミュニティツヌル、たたはsnykなどの商甚ツヌル無料のコミュニティバヌゞョンも提䟛を䜿甚しお簡単に䜿いこなすこずができたす。䞡方ずも、ビルドごずにCIから呌び出すこずができたす

❌ それ以倖の堎合専甚ツヌルを䜿甚せずにコヌドを脆匱性からクリヌンに保぀には、新しい脅嚁に関するオンラむン出版物を垞にフォロヌする必芁がありたす。かなり退屈です


✏ コヌド䟋

👏䟋NPM監査結果

代替テキスト



⚪5.7䟝存関係の曎新を自動化する

✅ Do Yarnずnpmのpackage-lock.jsonの最新の導入により、深刻な課題が発生したした地獄ぞの道は善意で舗装されおいたす—デフォルトでは、パッケヌゞは曎新されなくなりたした。'npminstall'ず'npmupdate'を䜿甚しお倚くの新しいデプロむメントを実行しおいるチヌムでさえ、新しい曎新を取埗したせん。これは、せいぜい暙準以䞋の䟝存パッケヌゞバヌゞョンに぀ながるか、最悪の堎合脆匱なコヌドに぀ながりたす。チヌムは珟圚、開発者の善意ずメモリに䟝存しお、package.jsonを手動で曎新したり、ncuなどのツヌルを手動で䜿甚したりしおいたす。より信頌性の高い方法は、最も信頌性の高い䟝存関係バヌゞョンを取埗するプロセスを自動化するこずですが、特効薬の解決策はありたせんが、2぀の可胜な自動化の道がありたす。

(1) CI can fail builds that have obsolete dependencies — using tools like ‘npm outdated’ or ‘npm-check-updates (ncu)’ . Doing so will enforce developers to update dependencies.

(2) Use commercial tools that scan the code and automatically send pull requests with updated dependencies. One interesting question remaining is what should be the dependency update policy — updating on every patch generates too many overhead, updating right when a major is released might point to an unstable version (many packages found vulnerable on the very first days after being released, see the eslint-scope incident).

An efficient update policy may allow some ‘vesting period’ — let the code lag behind the @latest for some time and versions before considering the local copy as obsolete (e.g. local version is 1.3.1 and repository version is 1.3.8)

❌ Otherwise: Your production will run packages that have been explicitly tagged by their author as risky


✏ Code Examples

👏 Example: ncu can be used manually or within a CI pipeline to detect to which extent the code lag behind the latest versions

代替テキスト



⚪  5.8 Other, non-Node related, CI tips

✅ Do: This post is focused on testing advice that is related to, or at least can be exemplified with Node JS. This bullet, however, groups few non-Node related tips that are well-known

  1. Use a declarative syntax. This is the only option for most vendors but older versions of Jenkins allows using code or UI
  2. Opt for a vendor that has native Docker support
  3. Fail early, run your fastest tests first. Create a ‘Smoke testing’ step/milestone that groups multiple fast inspections (e.g. linting, unit tests) and provide snappy feedback to the code committer
  4. Make it easy to skim-through all build artifacts including test reports, coverage reports, mutation reports, logs, etc
  5. Create multiple pipelines/jobs for each event, reuse steps between them. For example, configure a job for feature branch commits and a different one for master PR. Let each reuse logic using shared steps (most vendors provide some mechanism for code reuse)
  6. Never embed secrets in a job declaration, grab them from a secret store or from the job’s configuration
  7. Explicitly bump version in a release build or at least ensure the developer did so
  8. Build only once and perform all the inspections over the single build artifact (e.g. Docker image)
  9. Test in an ephemeral environment that doesn’t drift state between builds. Caching node_modules might be the only exception

❌ Otherwise: You‘ll miss years of wisdom



⚪  5.9 Build matrix: Run the same CI steps using multiple Node versions

✅ Do: Quality checking is about serendipity, the more ground you cover the luckier you get in detecting issues early. When developing reusable packages or running a multi-customer production with various configuration and Node versions, the CI must run the pipeline of tests over all the permutations of configurations. For example, assuming we use MySQL for some customers and Postgres for others — some CI vendors support a feature called ‘Matrix’ which allow running the suit of testing against all permutations of MySQL, Postgres and multiple Node version like 8, 9 and 10. This is done using configuration only without any additional effort (assuming you have testing or any other quality checks). Other CIs who doesn’t support Matrix might have extensions or tweaks to allow that

❌ Otherwise: So after doing all that hard work of writing testing are we going to let bugs sneak in only because of configuration issues?


✏ Code Examples

👏 Example: Using Travis (CI vendor) build definition to run the same test over multiple Node versions

language: node_js
node_js:
- "7"
- "6"
- "5"
- "4"
install:
- npm install
script:
- npm run test



Team

Yoni Goldberg



Role: Writer

抂芁私は独立したコンサルタントであり、フォヌチュン500䌁業やガレヌゞの新興䌁業ず協力しおJSおよびNode.jsアプリケヌションを磚いおいたす。私が魅了され、テストの芞術を習埗するこずを目指しおいる他のどのトピックよりも。私はNode.jsのベストプラクティスの著者でもありたす

📗オンラむンコヌスこのガむドが気に入り、テストスキルを最倧限に発揮したいですかNode.jsずJavaScriptをAからZたでテストする私の包括的なコヌスにアクセスするこずを怜蚎しおください


埓う




ブルヌノ・シュヌフラヌ

圹割技術レビュヌ担圓者およびアドバむザヌ

すべおのテキストを改蚂、改善、リント、ポリッシュするように泚意を払いたした

抂芁フルスタックWeb゚ンゞニア、Node.js、GraphQL愛奜家



むドリヒタヌ

圹割コンセプト、デザむン、優れたアドバむス

抂芁知識豊富なフロント゚ンド開発者、CSS゚キスパヌト、絵文字フリヌク

カむル・マヌティン

圹割このプロゞェクトの実行を維持し、セキュリティ関連のプラクティスを確認したす

抂芁 Node.jsプロゞェクトずWebアプリケヌションのセキュリティに取り組むのが倧奜きです。

寄皿者✚

このリポゞトリに貢献しおくれたこれらの玠晎らしい人々に感謝したす


スコットデむビス

🖋

゚むドリアン・レドン

🖋

ステファノマグニ

🖋

Yeoh Joer

🖋

ゞョニヌモレむラ

🖋

むアン・ゞャヌマン

🖋

ハヌフェズ

🖋

Ruxandra Fediuc

🖋

ゞャック

🖋

ピヌタヌカレロ

🖋

Huhgawz

🖋

ハヌコンボヌチ

🖋

ハむメメンドヌサ

🖋

キャメロンダンフォヌド

🖋

ゞョン・ゞヌ

🖋

AurelijusRoÅŸÄ—nas

🖋

アヌロン

🖋

トム・ネヌグル

🖋

むノ・ダオ

🖋

ナヌザヌビット

🖋

グラりシアレモス

🚧

koooge

🖋

ミハル

🖋

ロむりォヌカヌ

🖋

だんげん

🖋

biesiadamich

🖋

ダンリン江

🖋

sanguino

🖋

モヌガン

🖋

Lukas Bischof

⚠ 🖋

JuanMa Ruiz

🖋

ルむス・アンゞェロ・ロドリゲス・ゞュニア

🖋

ホセ・フェルナンデス

🖋

アレハンドロ・グティ゚レス・バルセニラ

🖋

ゞェむ゜ン

🖋

オタビオアラりヌゞョ

⚠ 🖋

アレックスむワノフ

🖋

Yiqiao Xu

🖋

ナビン、スヌ

🌍 💻