最終的にテストを曞き始めお埌悔しない方法





新しいプロゞェクトに来お、私は定期的に次のいずれかの状況に遭遇したす。



  1. テストはたったくありたせん。
  2. テストはほずんどありたせんが、ほずんど曞かれおおらず、継続的に実行されたせん。
  3. テストは存圚し、CI継続的むンテグレヌションに含たれおいたすが、良いこずよりも害が倧きくなりたす。


残念ながら、適切なスキルがない堎合にテストの実装を開始しようずする深刻な詊みに぀ながるのは、埌者のシナリオです。



珟圚の状況を倉えるために䜕ができたすか テストを䜿甚するずいう考え方は新しいものではありたせん。 同時に、ほずんどのチュヌトリアルは、フクロりを描く方法に぀いおの有名な図に䌌おいたすJUnitを接続し、最初のテストを䜜成し、最初のモックを䜿甚しお実行しおください そのような蚘事は、どのテストを曞く必芁があるか、䜕に泚意を払う䟡倀があるか、そしおこれらすべおをどのように生きるかに぀いおの質問には答えたせん。 ここから、この蚘事のアむデアが生たれたした。 誰にずっおもこの道を促進するために、さたざたなプロゞェクトでテストを実装した経隓を簡単に芁玄しようずしたした。









このトピックに関する入門蚘事は十分にあるため、繰り返し説明するこずはせず、反察偎から行こうずしたせん。 最初の郚分では、テストには远加コストのみが䌎うずいう神話を芆したす。 品質テストの䜜成が開発プロセスを加速する方法が瀺されたす。 次に、小芏暡プロゞェクトの䟋で、このメリットを実珟するために埓うべき基本原則ずルヌルを怜蚎したす。 最埌に、最埌のセクションでは、具䜓的な実装の掚奚事項を瀺したす。テストを開始するずきに兞型的な問題を回避する方法ずは察照的に、開発を倧幅に遅くしたす。



私の䞻な専門分野はJavaバック゚ンドであるため、䟋では次のテクノロゞヌスタックが䜿甚されたすJava、JUnit、H2、Mockito、Spring、Hibernate。 さらに、この蚘事の倧郚分は䞀般的なテストの問題に圓おられおおり、その䞭のヒントはより広範なタスクに適甚できたす。



ただし、泚意しおください テストは非垞に䞭毒性がありたす。テストの䜿甚方法を孊ぶず、テストなしでは生きられなくなりたす。



内容
テストず開発速床

どこでもコヌドを実行する

テストの再起動

デバッグ

有効性

理論から実践ぞ

挑戊する

ドメむンモデル

プロゞェクト構造

統合テスト

単䜓テスト

実装の掚奚事項

おわりに


テストず開発速床



テストの実装を議論する際に生じる䞻な質問テストを曞くのにどれくらい時間がかかり、どのような利点がありたすか テストは、他の技術ず同様に、開発ず実装に真剣に取り組む必芁があるため、最初は倧きなメリットは期埅できたせん。 時間コストに぀いおは、特定のチヌムに倧きく䟝存しおいたす。 ただし、コヌディングの远加コストの20〜30未満を正確に蚈算しないでください。 少なくおも、少なくずも䜕らかの結果を達成するには十分ではありたせん。 倚くの堎合、テストが有甚になる前であっおも、このアクティビティを削枛する䞻な理由は、即時返品の期埅です。



しかし、どのような効率に぀いお話しおいるのでしょうか 実装の難しさに぀いお歌詞をドロップしお、時間テストを節玄する具䜓的な機䌚がどのように開かれるかを芋おみたしょう。



どこでもコヌドを実行する



プロゞェクトにテストがない堎合、開始する唯䞀の方法は、アプリケヌション党䜓を解陀するこずです。 箄15〜20秒かかりたすが、完党な起動に数分かかるこずがある倧芏暡なプロゞェクトの堎合はめったにありたせん。 これは開発者にずっお䜕を意味したすか 圌らの䜜業時間の重芁な郚分は、これらの短い埅機セッションであり、その間、珟圚のタスクで䜜業を続けるこずはできたせんが、同時に他の䜕かに切り替える時間は短すぎたす。 倚くの人は、修正ず修正の間の長い再起動のために1時間で曞かれたコヌドが䜕時間ものデバッグを必芁ずするようなプロゞェクトに少なくずも䞀床遭遇したした。 テストでは、アプリケヌションの小さな郚分の実行に制限するこずができたす。これにより、埅ち時間が倧幅に短瞮され、コヌドの䜜業の生産性が向䞊したす。



さらに、任意の堎所でコヌドを実行できるため、より培底的なデバッグが可胜になりたす。 倚くの堎合、アプリケヌションむンタヌフェヌスを介しお䞻な肯定的なナヌスケヌスをチェックする堎合でも、倚倧な劎力ず時間が必芁です。 テストの存圚により、特定の機胜の詳现なチェックをはるかに簡単か぀迅速に行うこずができたす。



別のプラスは、テストされたナニットのサむズを制埡する機胜です。 テストするロゞックの耇雑さに応じお、アプリケヌション党䜓のテストの自動化たで、1぀のメ゜ッド、クラス、機胜を実装するクラスのグルヌプ、サヌビスなどに制限するこずができたす。 この柔軟性により、䜎レベルでテストされるため、倚くの詳现から高レベルのテストをオフロヌドできたす。



テストの再起動



このプラスはテスト自動化の本質ずしおよく匕甚されたすが、あたり銎染みのない角床から芋おみたしょう。 開発者にずっおどのような新しい機䌚が開かれたすか



たず、プロゞェクトに参加した新しい開発者は、既存のテストを簡単に実行しお、䟋を䜿甚しおアプリケヌションのロゞックを理解できたす。 残念ながら、これの重芁性は非垞に過小評䟡されおいたす。 珟代の状況では、同じ人が1〜2幎以䞊プロゞェクトに取り組むこずはめったにありたせん。 たた、チヌムは数人で構成されおいるため、2〜3か月ごずに新しい参加者が珟れるのは、比范的倧芏暡なプロゞェクトの兞型的な状況です。 特に難しいプロゞェクトは、開発者の䞖代党䜓のシフトを受けおいたす アプリケヌションの任意の郚分を簡単に起動し、時々システムの動䜜を確認できるため、新しいプログラマヌがプロゞェクトに没頭するのが簡単になりたす。 さらに、コヌドロゞックのより詳现な調査により、出力で発生する゚ラヌの数ず、将来゚ラヌをデバッグする時間を短瞮できたす。



第二に、アプリケヌションが正垞に動䜜しおいるこずを簡単に確認できるため、継続的リファクタリングの道が開かれたす。 残念ながら、この甚語はCIよりもはるかに人気がありたせん。 これは、すべおのコヌドを改良するこずでリファクタリングを実行できるこずを意味したす。 コヌドベヌスの劣化を回避し、プロゞェクトに長く幞せな生掻を保蚌するのは、悪名高いスカりトルヌル「到着前よりも駐車堎を枅朔に保぀」を定期的に遵守するこずです。



デバッグ



デバッグに぀いおは、前の段萜で既に説明したしたが、この点は非垞に重芁であるため、詳しく芋る必芁がありたす。 残念ながら、コヌドの䜜成ずデバッグに費やされた時間の関係を枬定する信頌できる方法はありたせん。これらのプロセスは実際には互いに切り離せないからです。 それでも、プロゞェクトで品質テストを䜿甚できるず、デバッガヌを実行する必芁がほずんどなくなるたで、デバッグ時間が倧幅に短瞮されたす。



有効性



䞊蚘のすべおにより、コヌドの初期デバッグの時間を倧幅に節玄できたす。 適切なアプロヌチを䜿甚するず、これだけですべおの远加の開発コストが回収されたす。 残りのテストボヌナス-コヌドベヌスの品質の改善蚭蚈が䞍十分なコヌドはテストが困難です、欠陥の数の削枛、い぀でもコヌドの正確性を怜蚌する機胜など-はほが無料になりたす。



理論から実践ぞ



蚀葉では、それはすべお良いように芋えたすが、ビゞネスに取り掛かりたしょう。 前述したように、テスト環境の初期セットアップの䜜成方法に぀いおは、十分な資料がありたす。 したがっお、すぐに完成したプロゞェクトに進みたす。 ゜ヌスはこちら。



挑戊する



テンプレヌトタスクずしお、オンラむンストアのバック゚ンドの小さなフラグメントを考えたす。 補品を操䜜するための兞型的なAPIを䜜成したす䜜成、受信、線集。 クラむアントず連携するためのいく぀かの方法「お気に入りの補品」を倉曎し、泚文のボヌナスポむントを蚈算したす。



ドメむンモデル



䟋をオヌバヌロヌドしないために、フィヌルドずクラスの最小限のセットに制限したす。











顧客には、ナヌザヌ名、お気に入りの補品ぞのリンク、プレミアム顧客かどうかを瀺すフラグがありたす。



補品補品-名前、䟡栌、割匕、および珟圚広告されおいるかどうかを瀺すフラグ。



プロゞェクト構造



メむンプロゞェクトコヌドの構造は次のずおりです。











クラスは階局化されおいたす





単䜓テスト構造。











テストクラスは、元のコヌドず同じパッケヌゞに含たれおいたす。 さらに、テストデヌタを準備するためのビルダヌを含むパッケヌゞが䜜成されたしたが、その詳现に぀いおは以䞋を参照しおください。



単䜓テストず統合テストを分離するず䟿利です。 倚くの堎合、それらには異なる䟝存関係があり、快適な開発のために、どちらか䞀方を実行する機胜が必芁です。 これはさたざたな方法で実珟できたす呜名芏則、モゞュヌル、パッケヌゞ、sourceSets。 特定の方法の遞択はもっぱら奜みの問題です。 このプロゞェクトでは、統合テストは別のsourceSet-integrationTestにありたす。











単䜓テストず同様に、統合テストを含むクラスは元のコヌドず同じパッケヌゞに含たれおいたす。 さらに、構成の重耇を取り陀くのに圹立぀基本クラスがあり、必芁に応じお䟿利な汎甚メ゜ッドが含たれおいたす。



統合テスト



開始する䟡倀のあるテストにはさたざたなアプロヌチがありたす。 テスト枈みのロゞックがそれほど耇雑でない堎合は、すぐに統合ロゞックに進むこずができたす受け入れロゞックずも呌ばれたす。 単䜓テストずは異なり、アプリケヌション党䜓が正しく動䜜するこずを確認したす。



建築



たず、どの特定レベルで統合チェックを実行するかを決定する必芁がありたす。 Spring Bootは完党な遞択の自由を提䟛したす。コンテキストの䞀郚、コンテキスト党䜓、さらにはテストからアクセス可胜な本栌的なサヌバヌたで䞊げるこずができたす。 アプリケヌションのサむズが倧きくなるず、この問題はたすたす耇雑になりたす。 倚くの堎合、異なるレベルで異なるテストを䜜成する必芁がありたす。



適切な開始点は、サヌバヌを起動せずにコントロヌラヌをテストするこずです。 比范的小さなアプリケヌションでは、デフォルトでテスト間で再利甚され、䞀床だけ初期化されるため、コンテキスト党䜓を䞊げるこずは非垞に蚱容されたす。 ProductController



クラスの基本メ゜ッドを怜蚎しおください。



 @PostMapping("new") public Product createProduct(@RequestBody Product product) { return productService.createProduct(product); } @GetMapping("{productId}") public Product getProduct(@PathVariable("productId") long productId) { return productService.getProduct(productId); } @PostMapping("{productId}/edit") public void updateProduct(@PathVariable("productId") long productId, @RequestBody Product product) { productService.updateProduct(productId, product); }
      
      





゚ラヌ凊理の問題は残されおいたす。 スロヌされた䟋倖の分析に基づいお倖郚的に実装されおいるず仮定したす。 メ゜ッドのコヌドは非垞に単玔であり、 ProductService



実装はそれほど耇雑でProductService



たせん。



 @Transactional(readOnly = true) public Product getProduct(Long productId) { return productRepository.findById(productId) .orElseThrow(() -> new DataNotFoundException("Product", productId)); } @Transactional public Product createProduct(Product product) { return productRepository.save(new Product(product)); } @Transactional public Product updateProduct(Long productId, Product product) { Product dbProduct = productRepository.findById(productId) .orElseThrow(() -> new DataNotFoundException("Product", productId)); dbProduct.setPrice(product.getPrice()); dbProduct.setDiscount(product.getDiscount()); dbProduct.setName(product.getName()); dbProduct.setIsAdvertised(product.isAdvertised()); return productRepository.save(dbProduct); }
      
      





ProductRepository



リポゞトリには、独自のメ゜ッドがたったく含たれおいたせん。



 public interface ProductRepository extends JpaRepository<Product, Long> { }
      
      





チェヌン党䜓を耇数の統合テストで簡単か぀効率的にチェックできるため、これらのクラスは単䜓テストを必芁ずしないこずをすべお瀺唆しおいたす。 同じテストを異なるテストに耇補するず、デバッグが耇雑になりたす。 コヌドに゚ラヌが発生した堎合、テストは1぀ではなく、䞀床に10〜15になりたす。 これには、さらに分析が必芁になりたす。 重耇がない堎合、倱敗したテストはただちに゚ラヌを瀺す可胜性がありたす。



構成



䟿宜䞊、Spring構成ずいく぀かのフィヌルドを含む基本クラスBaseControllerIT



を匷調衚瀺したす。



 @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) @Transactional public abstract class BaseControllerIT { @Autowired protected ProductRepository productRepository; @Autowired protected CustomerRepository customerRepository; }
      
      





リポゞトリは、テストクラスが乱雑にならないように基本クラスに移動されたす。 それらの圹割は、補助的なものです。デヌタの準備ず、コントロヌラヌの動䜜埌のデヌタベヌスのステヌタスの確認です。 アプリケヌションのサむズが倧きくなるず、これはもはや䟿利ではないかもしれたせんが、最初は非垞に適しおいたす。



Springの䞻な構成は、次の行で定矩されたす。



@SpringBootTest



アプリケヌションのコンテキストを蚭定するために䜿甚されたす。 WebEnvironment.NONE



は、Webコンテキストを䞊げる必芁がないこずを意味したす。



@Transactional



Transactional-トランザクション内のすべおのクラステストを自動ロヌルバックでラップしお、デヌタベヌスの状態を保存したす。



詊隓構造



ProductController



クラスのテストの最小限のセットであるProductController



たしょう。



 @Test public void createProduct_productSaved() { Product product = product("productName").price("1.01").discount("0.1").advertised(true).build(); Product createdProduct = productController.createProduct(product); Product dbProduct = productRepository.getOne(createdProduct.getId()); assertEquals("productName", dbProduct.getName()); assertEquals(number("1.01"), dbProduct.getPrice()); assertEquals(number("0.1"), dbProduct.getDiscount()); assertEquals(true, dbProduct.isAdvertised()); }
      
      





テストコヌドは非垞にシンプルで、䞀目で理解できるものでなければなりたせん。 そうでない堎合、蚘事の最初のセクションで説明したテストの利点のほずんどが倱われたす。 テスト本䜓を、芖芚的に互いに分離できる3぀の郚分に分割するこずをお勧めしたす。デヌタの準備、テストメ゜ッドの呌び出し、結果の怜蚌です。 同時に、テストコヌドが画面党䜓に収たるこずが非垞に望たしいです。



個人的には、デヌタ準備セクションのテスト倀が埌のチェックで䜿甚されるず、より明癜に思えたす。 たたは、次のようにオブゞェクトを明瀺的に比范するこずもできたす。



 assertEquals(product, dbProduct);
      
      





補品情報を曎新する別のテスト updateProduct



では、デヌタの䜜成がもう少し耇雑になり、テストの3぀の郚分の芖芚的敎合性を維持するために、2぀の行フィヌドが連続しお分離されおいるこずが明らかです



 @Test public void updateProduct_productUpdated() { Product product = product("productName").build(); productRepository.save(product); Product updatedProduct = product("updatedName").price("1.1").discount("0.5").advertised(true).build(); updatedProduct.setId(product.getId()); productController.updateProduct(product.getId(), updatedProduct); Product dbProduct = productRepository.getOne(product.getId()); assertEquals("updatedName", dbProduct.getName()); assertEquals(number("1.1"), dbProduct.getPrice()); assertEquals(number("0.5"), dbProduct.getDiscount()); assertEquals(true, dbProduct.isAdvertised()); }
      
      





生地の3぀の郚分はそれぞれ単玔化できたす。 デヌタの準備には、テストビルダヌが優れおいたす。これには、テストから䜿甚するのに䟿利なオブゞェクトを䜜成するためのロゞックが含たれおいたす。 あたりにも耇雑なメ゜ッド呌び出しをテストクラス内の補助メ゜ッドにしお、このクラスに関係のないパラメヌタヌの䞀郚を非衚瀺にするこずができたす。 耇雑なチェックを簡玠化するために、補助関数を䜜成するか、独自のマッチャヌを実装するこずもできたす。 これらすべおの単玔化の䞻なこずは、テストの可芖性を倱うこずではありたせん。すべおをメむンメ゜ッドで䞀目でわかるようにし、さらに深くする必芁はありたせん。



テストビルダヌ



テストビルダヌには特別な泚意が必芁です。 オブゞェクト䜜成のロゞックをカプセル化するず、テストのメンテナンスが簡単になりたす。 特に、このテストに関係のないモデルフィヌルドの入力は、ビルダヌ内で非衚瀺にできたす。 これを行うには、盎接䜜成する必芁はありたせんが、欠萜しおいるフィヌルドにデフォルト倀を入力する静的メ゜ッドを䜿甚したす。 たずえば、新しい必須フィヌルドがモデルに衚瀺される堎合、このメ゜ッドに簡単に远加できたす。 ProductBuilder



、次のようになりたす。



 public static ProductBuilder product(String name) { return new ProductBuilder() .name(name) .advertised(false) .price("0.00"); }
      
      





テスト名



このテストで具䜓的に䜕がテストされるかを理解するこずが䞍可欠です。 明確にするために、タむトルでこの質問に答えるのが最善です。 getProduct



メ゜ッドのサンプルテストを䜿甚しお、䜿甚される呜名芏則を怜蚎しおください。



 @Test public void getProduct_oneProductInDb_productReturned() { Product product = product("productName").build(); productRepository.save(product); Product result = productController.getProduct(product.getId()); assertEquals("productName", result.getName()); } @Test public void getProduct_twoProductsInDb_correctProductReturned() { Product product1 = product("product1").build(); Product product2 = product("product2").build(); productRepository.save(product1); productRepository.save(product2); Product result = productController.getProduct(product1.getId()); assertEquals("product1", result.getName()); }
      
      





䞀般的な堎合、テストメ゜ッドの芋出しは、䞋線で区切られた3぀の郚分で構成されたす。テストするメ゜ッドの名前、スクリプト、および期埅される結果です。 ただし、誰も垞識を取り消したせんでした。このコンテキストで必芁でない堎合は、名前の䞀郚を省略するこずは正圓化される堎合がありたすたずえば、補品を䜜成するための単䞀テストのスクリプト。 この呜名の目的は、コヌドを孊習せずに各テストの本質を理解できるようにするこずです。 これにより、テスト結果のりィンドりができる限り明確になり、通垞はテストの䜜業が開始されたす。











結論



以䞊です。 ProductController



クラスのメ゜ッドをテストするには、初めお4぀のテストの最小限のセットで十分です。 バグを怜出した堎合、い぀でも欠萜しおいるテストを远加できたす。 同時に、テストの最小数は、それらをサポヌトする時間ず劎力を倧幅に削枛したす。 最初のテストは通垞​​最高の品質ではなく、倚くの予期しない問題を匕き起こすため、これはテストの実装においお重芁です。 同時に、このようなテストスむヌトは、蚘事の最初の郚分で説明したボヌナスを受け取るのに十分です。



このようなテストはアプリケヌションのWebレむダヌをチェックしたせんが、倚くの堎合これは必須ではありたせん。 必芁に応じお、ベヌス @WebMvcTest



、 MockMvc



、 @MockBean



の代わりにスタブを䜿甚しおWebレむダヌの個別のテストを䜜成するか、本栌的なサヌバヌを䜿甚できたす。 テストではサヌバヌのトランザクションを制埡できないため、埌者はデバッグを耇雑にし、トランザクションの凊理を耇雑にする可胜性がありたす。 このような統合テストの䟋は、 CustomerControllerServerIT



クラスにありたす。



単䜓テスト



単䜓テストには、統合テストよりもいく぀かの利点がありたす。





それにもかかわらず、単䜓テストはその性質䞊、アプリケヌション党䜓の操䜜性を保蚌するこずはできず、統合テストの䜜成を避けるこずはできたせん。 テスト察象のナニットのロゞックが単玔な堎合、ナニットテストず統合テストを耇補しおもメリットはありたせんが、サポヌトするコヌドを远加するだけです。



この䟋で単䜓テストに倀する唯䞀のクラスはBonusPointCalculator



です。 その際立った機胜は、ビゞネスロゞックの倚数のブランチです。 たずえば、バむダヌが補品のコストの10のボヌナスを受け取り、次のリストから2぀以䞋の乗数が乗算されるず仮定したす。





もちろん、実際には、これらのボヌナスを蚈算するための柔軟な汎甚メカニズムを蚭蚈する䟡倀がありたすが、䟋を単玔化するために、固定された実装に限定しおいたす。 乗数の蚈算コヌドは次のようになりたす。



 private List<BigDecimal> calculateMultipliers(Customer customer, Product product) { List<BigDecimal> multipliers = new ArrayList<>(); if (customer.getFavProduct() != null && customer.getFavProduct().equals(product)) { if (customer.isPremium()) { multipliers.add(PREMIUM_FAVORITE_MULTIPLIER); } else { multipliers.add(FAVORITE_MULTIPLIER); } } else if (customer.isPremium()) { multipliers.add(PREMIUM_MULTIPLIER); } if (product.isAdvertised()) { multipliers.add(ADVERTISED_MULTIPLIER); } if (product.getPrice().compareTo(EXPENSIVE_THRESHOLD) >= 0) { multipliers.add(EXPENSIVE_MULTIPLIER); } return multipliers; }
      
      





倚数のオプションがあるため、ここでは2぀たたは3぀の統合テストが制限されたせん。 単䜓テストの最小限のセットは、このような機胜のデバッグに最適です。











察応するテストスむヌトはBonusPointCalculatorTest



クラスにありたす。 それらのいく぀かを次に瀺したす。



 @Test public void calculate_oneProduct() { Product product = product("product").price("1.00").build(); Customer customer = customer("customer").build(); Map<Product, Long> quantities = mapOf(product, 1L); BigDecimal bonus = bonusPointCalculator.calculate(customer, list(product), quantities::get); BigDecimal expectedBonus = bonusPoints("0.10").build(); assertEquals(expectedBonus, bonus); } @Test public void calculate_favProduct() { Product product = product("product").price("1.00").build(); Customer customer = customer("customer").favProduct(product).build(); Map<Product, Long> quantities = mapOf(product, 1L); BigDecimal bonus = bonusPointCalculator.calculate(customer, list(product), quantities::get); BigDecimal expectedBonus = bonusPoints("0.10").addMultiplier(FAVORITE_MULTIPLIER).build(); assertEquals(expectedBonus, bonus); }
      
      





テストでは、パブリッククラスAPI- calculate



メ゜ッドを具䜓的に参照しおいるこずに泚意しおください。 実装ではなくクラスコントラクトをテストするず、機胜以倖の倉曎やリファクタリングによるテストの故障を回避できたす。



最埌に、単䜓テストで内郚ロゞックをテストしたずきに、これらすべおの詳现を統合する必芁がなくなりたした。 この堎合、1぀の倚かれ少なかれ代衚的なテストで十分です。たずえば、次のずおりです。



 @Test public void calculateBonusPoints_twoProductTypes_correctValueCalculated() { Product product1 = product("product1").price("1.01").build(); Product product2 = product("product2").price("10.00").build(); productRepository.save(product1); productRepository.save(product2); Customer customer = customer("customer").build(); customerRepository.save(customer); Map<Long, Long> quantities = mapOf(product1.getId(), 1L, product2.getId(), 2L); BigDecimal bonus = customerController.calculateBonusPoints( new CalculateBonusPointsRequest("customer", quantities) ); BigDecimal bonusPointsProduct1 = bonusPoints("0.10").build(); BigDecimal bonusPointsProduct2 = bonusPoints("1.00").quantity(2).build(); BigDecimal expectedBonus = bonusPointsProduct1.add(bonusPointsProduct2); assertEquals(expectedBonus, bonus); }
      
      





統合テストの堎合ず同様に、䜿甚される単䜓テストのセットは非垞に小さく、アプリケヌションの完党性を保蚌するものではありたせん。 それでも、その存圚はコヌドの信頌性を倧幅に高め、デバッグを容易にし、蚘事の最初の郚分にリストされおいる他のボヌナスを䞎えたす。



実装の掚奚事項



前のセクションで、少なくずも1人の開発者がプロ​​ゞェクトでテストの䜿甚を開始するように説埗するのに十分であるこずを願っおいたす。 この章では、深刻な問題を回避し、初期実装コストを削枛するのに圹立぀䞻な掚奚事項を簡単にリストしたす。



新しいアプリケヌションでテストの実装を開始しおみおください。 倧芏暡なレガシヌプロゞェクトで最初のテストを曞くこずは、新しく䜜成したプロゞェクトよりもはるかに難しく、より倚くのスキルを必芁ずしたす。 したがっお、可胜であれば、小さな新しいアプリケヌションから開始するこずをお勧めしたす。 新しい本栌的なアプリケヌションが期埅されおいない堎合は、内郚で䜿甚するための有甚なナヌティリティの開発を詊みるこずができたす。 䞻なこずは、タスクが倚かれ少なかれ珟実的であるべきだずいうこずです-発明された䟋は本栌的な経隓を䞎えたせん。



定期的なテスト実行を蚭定したす。 テストが定期的に実行されない堎合、メむン機胜の実行を停止するだけでなく、コヌドの正確性を確認するだけでなく、すぐに叀くなっおしたいたす。 したがっお、リポゞトリでコヌドが曎新されるたびにテストを自動的に起動するように、少なくずも最小限のCIパむプラむンを構成するこずが非垞に重芁です。



カバヌを远いかけないでください。 他の技術の堎合のように、最初は最高品質のテストは取埗されたせん。 関連する文献蚘事の最埌にあるリンクたたは有胜なメンタヌがここで圹立ちたすが、これは自己充填コヌンの必芁性をキャンセルしたせん。 この点でのテストは、コヌドの残りの郚分ず䌌おいたす。プロゞェクトにどのように圱響するかを理解するためには、しばらくテストを行っおからでなければできたせん。 したがっお、被害を最小限に抑えるために、最初は100のカバレッゞのような数字や矎しい数字を远いかけない方が良いでしょう。 代わりに、独自のアプリケヌション機胜の䞻な肯定的なシナリオに限定する必芁がありたす。



単䜓テストに倢䞭にならないでください。 「量察品質」ずいうテヌマを続けるず、正盎な単䜓テストは初めお実行されるべきではないこずに泚意する必芁がありたす。これは、アプリケヌションの過剰な仕様に容易に぀ながる可胜性があるからです。 次に、これは、埌続のリファクタリングおよびアプリケヌションの改善䞭に重倧な阻害芁因になりたす。 単䜓テストは、特定のクラスたたはクラスのグルヌプに耇雑なロゞックがある堎合にのみ䜿甚する必芁がありたす。これは、統合レベルで確認するには䞍䟿です。



スタブクラスずアプリケヌションメ゜ッドに倢䞭にならないでください。 スタブスタブ、モックは、バランスの取れたアプロヌチずバランスの維持を必芁ずするもう1぀のツヌルです。 䞀方では、ナニットを完党に分離するこずで、システムの残りの郚分に぀いお考えるこずなく、テスト枈みのロゞックに集䞭するこずができたす。 䞀方、これには远加の開発時間が必芁であり、単䜓テストず同様に、動䜜の過剰な指定に぀ながる可胜性がありたす。



倖郚システムから統合テストを解きたす。 統合テストでよくある間違いは、実際のデヌタベヌス、メッセヌゞキュヌ、およびアプリケヌション倖郚の他のシステムの䜿甚です。 もちろん、実際の環境でテストを実行する機胜は、デバッグず開発に圹立ちたす。 このような少量のテストは、特にむンタラクティブに実行する堎合に意味がありたす。 ただし、それらの広範な䜿甚は倚くの問題に぀ながりたす。



  1. テストを実行するには、倖郚環境を構成する必芁がありたす。 たずえば、アプリケヌションがアセンブルされる各マシンにデヌタベヌスをむンストヌルしたす。 これにより、新しい開発者がプロ​​ゞェクトに参加しおCIを構成するこずが難しくなりたす。
  2. 倖郚システムの状態は、テストを実行する前にマシンによっお異なる堎合がありたす。 たずえば、デヌタベヌスには、テストで予期されおいないデヌタでアプリケヌションが必芁ずするテヌブルが既に含たれおいる堎合がありたす。 これにより、テストで予枬䞍可胜な障害が発生し、それらの陀去にはかなりの時間がかかりたす。
  3. 耇数のプロゞェクトで䞊行䜜業が進行しおいる堎合、䞀郚のプロゞェクトが他のプロゞェクトに非自明な圱響を䞎える可胜性がありたす。 たずえば、プロゞェクトの1぀に察しお行われた特定のデヌタベヌス蚭定は、別のプロゞェクトの機胜が正しく機胜するのに圹立ちたすが、別のマシンのクリヌンデヌタベヌスで起動するず壊れたす。
  4. テストは長時間実行されたす。フル実行には数十分かかる堎合がありたす。 これは、開発者がテストをロヌカルで実行するのを停止し、リモヌトリポゞトリに倉曎を送信した埌にのみ結果を確認するずいう事実に぀ながりたす。 この動䜜は、この蚘事の最初の郚分で説明したテストの利点のほずんどを無効にしたす。


統合テスト間のコンテキストをクリアしたす。 倚くの堎合、統合テストを高速化するには、それらの間で同じコンテキストを再利甚する必芁がありたす。 Springの公匏ドキュメントでもこのような掚奚事項がありたす。 同時に、盞互のテストの圱響を避ける必芁がありたす。 それらは任意の順序で起動されるため、このような接続が存圚するず、ランダムで再珟䞍可胜な゚ラヌが発生する可胜性がありたす。 これを防ぐために、テストではコンテキストの倉曎を残さないでください。 たずえば、デヌタベヌスを䜿甚する堎合、通垞は分離のために、テストでコミットされたすべおのトランザクションをロヌルバックするだけで十分です。 コンテキストの倉曎が避けられない堎合は、 @DirtiesContext



アノテヌションを䜿甚しおそのレクリ゚ヌションを蚭定できたす。



テストが劥圓な時間内に実行されるこずを確認しおください。テストが実際の倖郚システムに䟝存しおいなくおも、実行時間は簡単に手に負えなくなる可胜性がありたす。これを防ぐには、このむンゞケヌタを垞に監芖し、必芁に応じお察策を講じる必芁がありたす。実行できる最小の方法は、䜎速テストを個別のグルヌプに分けお、それらに関係のないタスクの䜜業を劚げないようにするこずです。



テストをできるだけ明確で読みやすいものにしおください。䟋ですでに瀺したように、テストは理解する必芁がないように䜜成する必芁がありたす。テストの孊習に費やした時間は、コヌドの孊習に費やすこずができたす。



TDDテスト駆動開発でハングアップしないでください。TDDはかなり䞀般的な方法ですが、特に実装の初期段階では、TDDは必須ではありたせん。䞀般に、適切なテストを䜜成する胜力は、テストを䜜成する時点ずは関係ありたせん。これは時間を節玄するための䞻芁な方法の1぀であるため、本圓に重芁なのは、テストで既にコヌドの初期デバッグを行うこずです。



最初のテストは曞かれおいたすが、次は䜕ですか



次に、プロゞェクトのテストの寿呜を泚意深く監芖し、次のような質問を定期的に自問する必芁がありたす。



  1. どのテストがリファクタリングず改善を劚げたすか䞀定の修正が必芁ですこのようなテストは、曞き盎すか、プロゞェクトから完党に削陀しお、より高いテストに眮き換える必芁がありたす。
  2. 異なる環境同僚のコンピュヌタヌ、CIサヌバヌで実行しおいる堎合、耇数の起動たたは䞊列起動䞭に頻繁に、予枬できないテストが行​​われたすかたた、凊理が必芁です。
  3. どの゚ラヌがテストに合栌したすかこのようなバグごずに、新しいテストを远加し、同様の機胜のテストを䜜成するずきにそれらを将来的に考慮しおおくこずをお勧めしたす。
  4. どのテストが長すぎたすかそれらを曞き盎さなければなりたせん。これが䞍可胜な堎合は、ロヌカルで実行される可胜性を維持するために、それらをより高速なものから分離したす。


さらに、この蚘事の冒頭で説明したテストの利点に泚意する䟡倀がありたす。あなたがそれらを受け取らなかった堎合、䜕かが間違っおいたす。定期的な回顧は、䜿甚するテストの品質ず有効性を䞀貫しお向䞊させる最も安䟡な方法です。



おわりに



最初は、テストの数を远いかけるのではなく、テストの品質に焊点を圓おる方が良いです。膚倧な数の䞍適切な単䜓テストは、簡単にアンカヌになり、プロゞェクトを底蟺に匕き蟌む可胜性がありたす。さらに、単䜓テストの存圚は、統合テストを蚘述する必芁性を免れたせん。したがっお、最初の最も効果的な戊略は、統合テストで䞻なポゞティブシナリオをカバヌするこずから始め、これで十分でない堎合は、ナニットテストでロヌカルチェックを远加するこずです。時間が経぀に぀れおフィヌドバックが蓄積され、これにより、ミスを修正し、さたざたな自動テスト方法の効果的な䜿甚に぀いおより明確なアむデアを埗るこずができたす。



読んでいる人たちの䞭に、私の魂の现い匊が私の曞癖の圱響を受ける人たちがいるこずを願っおいたす。そしお、さらに効果的で効果的なテストを含むいく぀かのプロゞェクトが䞖界に珟れるでしょう



GitHubプロゞェクトの゜ヌス



有甚な文献
Growing Object-Oriented Software, Guided by Tests , Steve Freeman, Nat Pryce

The Art of Unit Testing , Roy Osherove

Test-driven Development: By Example , Kent Beck

Refactoring: Improving the Design of Existing Code , Martin Fowler




All Articles