apeescape2.com
  • メイン
  • 製品の担当者とチーム
  • エンジニアリング管理
  • 人とチーム
  • モバイル
バックエンド

自信を持って構築する:JUnitテストのガイド

ウォーターフォールモデルからアジャイル、そして今ではDevOpsに移行するテクノロジーと業界の進歩に伴い、アプリケーションの変更と拡張機能は、作成された瞬間に本番環境に展開されます。コードがこれほど速く本番環境にデプロイされるので、変更が機能し、既存の機能が損なわれないことを確信する必要があります。

この自信を築くには、 フレームワーク 自動回帰テスト用。回帰テストを実行するには、APIレベルの観点から実行する必要のあるテストが多数ありますが、ここでは2つの主要なタイプのテストについて説明します。

デザインの要素と原則
  • ユニットテスト 、任意のテストがプログラムの最小単位(関数またはプロシージャ)をカバーする場合。一部の入力パラメーターを受け取る場合と受け取らない場合があり、一部の値を返す場合と返さない場合があります。
  • 統合テスト 、個々のユニットを一緒にテストして、すべてのユニットが期待どおりに相互作用するかどうかを確認します。

すべてのプログラミング言語で利用できるフレームワークは多数あります。で書かれたウェブアプリのユニットと統合テストの作成に焦点を当てます Java のSpringフレームワーク。



ほとんどの場合、クラスにメソッドを記述し、これらは他のクラスのメソッドと相互作用します。今日の世界、特に エンタープライズアプリケーション —アプリケーションの複雑さは、単一のメソッドが複数のクラスの複数のメソッドを呼び出す可能性があるようなものです。したがって、そのようなメソッドの単体テストを作成するときは、それらの呼び出しからモックされたデータを返す方法が必要です。これは、この単体テストの目的が1つのメソッドのみをテストすることであり、この特定のメソッドが行うすべての呼び出しをテストすることではないためです。


JUnitフレームワークを使用してSpringでのJavaユニットテストに飛び込みましょう。聞いたことがあるかもしれない何かから始めましょう:モック。

モックとは何ですか?それはいつ登場しますか?

関数CalculateAreaを持つクラスcalculateArea(Type type, Double... args)があるとします。これは、指定されたタイプ(円、正方形、または長方形)の形状の面積を計算します。

依存性注入を使用しない通常のアプリケーションでは、コードは次のようになります。

public class CalculateArea { SquareService squareService; RectangleService rectangleService; CircleService circleService; CalculateArea(SquareService squareService, RectangleService rectangeService, CircleService circleService) { this.squareService = squareService; this.rectangleService = rectangeService; this.circleService = circleService; } public Double calculateArea(Type type, Double... r ) { switch (type) { case RECTANGLE: if(r.length >=2) return rectangleService.area(r[0],r[1]); else throw new RuntimeException('Missing required params'); case SQUARE: if(r.length >=1) return squareService.area(r[0]); else throw new RuntimeException('Missing required param'); case CIRCLE: if(r.length >=1) return circleService.area(r[0]); else throw new RuntimeException('Missing required param'); default: throw new RuntimeException('Operation not supported'); } } } public class SquareService { public Double area(double r) { return r * r; } } public class RectangleService { public Double area(Double r, Double h) { return r * h; } } public class CircleService { public Double area(Double r) { return Math.PI * r * r; } } public enum Type { RECTANGLE,SQUARE,CIRCLE; }

ここで、関数をユニットテストする場合calculateArea()クラスCalculateAreaの場合、私たちの動機はswitchかどうかを確認することです。ケースと例外条件は機能します。シェイプサービスが正しい値を返しているかどうかをテストするべきではありません。前述のように、関数の単体テストの目的は、関数が行っている呼び出しのロジックではなく、関数のロジックをテストすることです。

したがって、個々のサービス関数(rectangleService.area()など)によって返される値をモックし、それらのモックされた値に基づいて呼び出し元の関数(CalculateArea.calculateArea()など)をテストします。

長方形サービスの簡単なテストケース—それをテストするcalculateArea()確かにrectangleService.area()を呼び出します正しいパラメータを使用すると、次のようになります。

import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; public class CalculateAreaTest { RectangleService rectangleService; SquareService squareService; CircleService circleService; CalculateArea calculateArea; @Before public void init() { rectangleService = Mockito.mock(RectangleService.class); squareService = Mockito.mock(SquareService.class); circleService = Mockito.mock(CircleService.class); calculateArea = new CalculateArea(squareService,rectangleService,circleService); } @Test public void calculateRectangleAreaTest() { Mockito.when(rectangleService.area(5.0d,4.0d)).thenReturn(20d); Double calculatedArea = this.calculateArea.calculateArea(Type.RECTANGLE, 5.0d, 4.0d); Assert.assertEquals(new Double(20d),calculatedArea); } }

ここで注意すべき2つの主要な行は次のとおりです。

  • rectangleService = Mockito.mock(RectangleService.class); —これにより、実際のオブジェクトではなく、モックされたオブジェクトであるモックが作成されます。
  • Mockito.when(rectangleService.area(5.0d,4.0d)).thenReturn(20d); —これは、嘲笑されたとき、そしてrectangleServiceオブジェクトのarea指定されたパラメーターを使用してメソッドが呼び出され、20dが返されます。

さて、上記のコードがSpringアプリケーションの一部である場合はどうなりますか?

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class CalculateArea { SquareService squareService; RectangleService rectangleService; CircleService circleService; public CalculateArea(@Autowired SquareService squareService, @Autowired RectangleService rectangeService, @Autowired CircleService circleService) { this.squareService = squareService; this.rectangleService = rectangeService; this.circleService = circleService; } public Double calculateArea(Type type, Double... r ) { // (same implementation as before) } }

ここに、コンテキストの初期化時に検出する、基盤となるSpringフレームワークの2つのアノテーションがあります。

  • @Component:タイプCalculateAreaのBeanを作成します
  • @Autowired:Beanを検索しますrectangleService、squareService、およびcircleServiceそしてそれらを豆に注入しますcalculatedArea

同様に、他のクラスのBeanも作成します。

import org.springframework.stereotype.Service; @Service public class SquareService { public Double area(double r) { return r*r; } } import org.springframework.stereotype.Service; @Service public class CircleService { public Double area(Double r) { return Math.PI * r * r; } } import org.springframework.stereotype.Service; @Service public class RectangleService { public Double area(Double r, Double h) { return r*h; } }

ここでテストを実行すると、結果は同じになります。ここではコンストラクタインジェクションを使用しましたが、幸い、テストケースを変更しないでください。

ただし、正方形、円、および長方形のサービスのBeanを注入する別の方法があります。フィールド注入です。それを使用する場合、テストケースにいくつかの小さな変更が必要になります。

記事の範囲外であるため、どちらの注入メカニズムが優れているかについては説明しません。しかし、私たちはこれを言うことができます:Beanを注入するために使用するメカニズムのタイプに関係なく、それに対するJUnitテストを作成する方法は常にあります。

フィールドインジェクションの場合、コードは次のようになります。

@Component public class CalculateArea { @Autowired SquareService squareService; @Autowired RectangleService rectangleService; @Autowired CircleService circleService; public Double calculateArea(Type type, Double... r ) { // (same implementation as before) } }

注:フィールドインジェクションを使用しているため、パラメーター化されたコンストラクターは必要ありません。したがって、オブジェクトはデフォルトのコンストラクターを使用して作成され、値はフィールドインジェクションメカニズムを使用して設定されます。

サービスクラスのコードは上記と同じですが、テストクラスのコードは次のとおりです。

public class CalculateAreaTest { @Mock RectangleService rectangleService; @Mock SquareService squareService; @Mock CircleService circleService; @InjectMocks CalculateArea calculateArea; @Before public void init() { MockitoAnnotations.initMocks(this); } @Test public void calculateRectangleAreaTest() { Mockito.when(rectangleService.area(5.0d,4.0d)).thenReturn(20d); Double calculatedArea = this.calculateArea.calculateArea(Type.RECTANGLE, 5.0d, 4.0d); Assert.assertEquals(new Double(20d),calculatedArea); } }

ここではいくつかの点が異なります。基本ではなく、それを達成する方法です。

まず、オブジェクトをモックする方法:@Mockを使用しますinitMocks()とともに注釈モックを作成します。次に、@InjectMocksを使用して実際のオブジェクトにモックを注入します。 initMocks()と一緒に。

これは、コードの行数を減らすために行われます。

テストランナーとは何ですか?また、どのような種類のランナーがいますか?

上記のサンプルでは、​​すべてのテストを実行するために使用される基本的なランナーはBlockJUnit4ClassRunnerです。これはすべての注釈を検出し、それに応じてすべてのテストを実行します。

さらに機能が必要な場合は、カスタムランナーを作成できます。たとえば、上記のテストクラスで、MockitoAnnotations.initMocks(this);の行をスキップする場合次に、BlockJUnit4ClassRunnerの上に構築された別のランナーを使用できます。 MockitoJUnitRunner。

MockitoJUnitRunnerを使用すると、モックを初期化して注入する必要もありません。それはMockitoJUnitRunnerによって行われます注釈を読むだけでそれ自体。

(Springアプリケーションの起動時にSpringJUnit4ClassRunnerが作成されるのと同じように、Spring統合テストに必要なApplicationContextを初期化するApplicationContextもあります。これについては後で説明します。)

部分的なモック

テストクラスのオブジェクトがいくつかのメソッドをモックするだけでなく、いくつかの実際のメソッドを呼び出すようにする場合は、部分的なモックが必要です。これは@Spyを介して達成されますJUnitで。

@Mockを使用するのとは異なり、@Spyを使用すると、実際のオブジェクトが作成されますが、そのオブジェクトのメソッドは、モックしたり、実際に呼び出すことができます。

たとえば、areaの場合クラスのメソッドRectangleService追加のメソッドを呼び出すlog()実際にそのログを印刷したい場合、コードは次のように変更されます。

@Service public class RectangleService { public Double area(Double r, Double h) { log(); return r*h; } public void log() { System.out.println('skip this'); } }

@Mockを変更した場合rectangleServiceの注釈@Spyに移動し、以下に示すようにコードを変更すると、結果では実際にログが出力されますが、メソッドarea()嘲笑されます。つまり、元の関数はその副作用のためだけに実行されます。その戻り値はモックされたものに置き換えられます。

@RunWith(MockitoJUnitRunner.class) public class CalculateAreaTest { @Spy RectangleService rectangleService; @Mock SquareService squareService; @Mock CircleService circleService; @InjectMocks CalculateArea calculateArea; @Test public void calculateRectangleAreaTest() { Mockito.doCallRealMethod().when(rectangleService).log(); Mockito.when(rectangleService.area(5.0d,4.0d)).thenReturn(20d); Double calculatedArea = this.calculateArea.calculateArea(Type.RECTANGLE, 5.0d, 4.0d); Assert.assertEquals(new Double(20d),calculatedArea); } }

Controllerのテストについてどのように進めますかまたはRequestHandler?

上で学んだことから、この例のコントローラーのテストコードは次のようになります。

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class AreaController { @Autowired CalculateArea calculateArea; @RequestMapping(value = 'api/area', method = RequestMethod.GET) @ResponseBody public ResponseEntity calculateArea( @RequestParam('type') String type, @RequestParam('param1') String param1, @RequestParam(value = 'param2', required = false) String param2 ) { try { Double area = calculateArea.calculateArea( Type.valueOf(type), Double.parseDouble(param1), Double.parseDouble(param2) ); return new ResponseEntity(area, HttpStatus.OK); } catch (Exception e) { return new ResponseEntity(e.getCause(), HttpStatus.INTERNAL_SERVER_ERROR); } } } import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @RunWith(MockitoJUnitRunner.class) public class AreaControllerTest { @Mock CalculateArea calculateArea; @InjectMocks AreaController areaController; @Test public void calculateAreaTest() { Mockito .when(calculateArea.calculateArea(Type.RECTANGLE,5.0d, 4.0d)) .thenReturn(20d); ResponseEntity responseEntity = areaController.calculateArea('RECTANGLE', '5', '4'); Assert.assertEquals(HttpStatus.OK,responseEntity.getStatusCode()); Assert.assertEquals(20d,responseEntity.getBody()); } }

上記のコントローラーテストコードを見ると、正常に機能しますが、基本的な問題が1つあります。それは、実際のAPI呼び出しではなく、メソッド呼び出しのみをテストすることです。 APIパラメータとAPI呼び出しのステータスをさまざまな入力についてテストする必要があるすべてのテストケースがありません。

このコードは優れています:

import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; @RunWith(SpringJUnit4ClassRunner.class) public class AreaControllerTest { @Mock CalculateArea calculateArea; @InjectMocks AreaController areaController; MockMvc mockMvc; @Before public void init() { mockMvc = standaloneSetup(areaController).build(); } @Test public void calculateAreaTest() throws Exception { Mockito .when(calculateArea.calculateArea(Type.RECTANGLE,5.0d, 4.0d)) .thenReturn(20d); mockMvc.perform( MockMvcRequestBuilders.get('/api/area?type=RECTANGLE¶m1=5¶m2=4') ) .andExpect(status().isOk()) .andExpect(content().string('20.0')); } }

ここでは、MockMvcの方法を確認できます。実際のAPI呼び出しを実行する仕事を引き受けます。 status()のような特別なマッチャーもありますおよびcontent()これにより、コンテンツの検証が容易になります。

JUnitとモックを使用したJava統合テスト

コードの個々のユニットが機能することがわかったので、期待どおりに相互作用することを確認しましょう。

まず、すべてのBeanをインスタンス化する必要があります。これは、アプリケーションの起動時にSpringコンテキストの初期化時に発生するのと同じことです。

このために、クラス内のすべてのBeanを定義します。たとえば、TestConfig.java:

import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class TestConfig { @Bean public AreaController areaController() { return new AreaController(); } @Bean public CalculateArea calculateArea() { return new CalculateArea(); } @Bean public RectangleService rectangleService() { return new RectangleService(); } @Bean public SquareService squareService() { return new SquareService(); } @Bean public CircleService circleService() { return new CircleService(); } }

それでは、このクラスをどのように使用し、統合テストを作成するかを見てみましょう。

import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {TestConfig.class}) public class AreaControllerIntegrationTest { @Autowired AreaController areaController; MockMvc mockMvc; @Before public void init() { mockMvc = standaloneSetup(areaController).build(); } @Test public void calculateAreaTest() throws Exception { mockMvc.perform( MockMvcRequestBuilders.get('/api/area?type=RECTANGLE¶m1=5¶m2=4') ) .andExpect(status().isOk()) .andExpect(content().string('20.0')); } }

ここでいくつかの変更があります。

  • @ContextConfiguration(classes = {TestConfig.class}) —これは、すべてのBean定義が存在するテストケースを示します。
  • @InjectMocksの代わりにを使用しております:
@Autowired AreaController areaController;

他のすべては同じままです。テストをデバッグすると、コードは実際にはarea()の最後の行まで実行されることがわかります。 RectangleServiceのメソッドここでreturn r*h計算されます。つまり、実際のビジネスロジックが実行されます。

これは、統合テストで使用可能なメソッド呼び出しまたはデータベース呼び出しのモックがないことを意味するものではありません。上記の例では、サードパーティのサービスやデータベースが使用されていないため、モックを使用する必要はありませんでした。実生活では、このようなアプリケーションはまれであり、データベースまたはサードパーティのAPI、あるいはその両方にアクセスすることがよくあります。その場合、TestConfigでBeanを作成するとクラスでは、実際のオブジェクトではなく、モックされたオブジェクトを作成し、必要に応じて使用します。

ボーナス:ラージオブジェクトテストデータの作成方法

多くの場合、バックエンド開発者がユニットテストや統合テストを作成するのを妨げるのは、すべてのテストのために準備する必要のあるテストデータです。

通常、データが十分に小さく、1つまたは2つの変数がある場合は、テストデータクラスのオブジェクトを作成していくつかの値を割り当てるだけで簡単です。

たとえば、モックされたオブジェクトが別のオブジェクトを返すことを期待している場合、モックされたオブジェクトで関数が呼び出されると、次のようになります。

Class1 object = new Class1(); object.setVariable1(1); object.setVariable2(2);

そして、このオブジェクトを使用するには、次のようにします。

コンサルティング率の計算方法
Mockito.when(service.method(arguments...)).thenReturn(object);

これは上記のJUnitの例では問題ありませんが、上記のメンバー変数がClass1の場合クラスは増え続け、個々のフィールドを設定するのはかなり苦痛になります。クラスに別の非プリミティブクラスメンバーが定義されている場合もあります。次に、そのクラスのオブジェクトを作成し、個々の必須フィールドを設定すると、定型文を作成するためだけに開発作業がさらに増加し​​ます。

解決策は、上記のクラスのJSONスキーマを生成し、対応するデータをJSONファイルに1回追加することです。 Class1を作成するテストクラスになります。オブジェクトの場合、オブジェクトを手動で作成する必要はありません。代わりに、JSONファイルを読み取り、ObjectMapperを使用して、必要なClass1にマップします。クラス:

ObjectMapper objectMapper = new ObjectMapper(); Class1 object = objectMapper.readValue( new String(Files.readAllBytes( Paths.get('src/test/resources/'+fileName)) ), Class1.class );

これは、JSONファイルを作成してそれに値を追加する1回限りの作業です。その後の新しいテストでは、新しいテストのニーズに応じてフィールドが変更されたJSONファイルのコピーを使用できます。

JUnitの基本:複数のアプローチと移転可能なスキル

Beanの注入方法に応じて、Javaユニットテストを作成する方法がたくさんあることは明らかです。残念ながら、このトピックに関するほとんどの記事は1つの方法しかないと想定している傾向があるため、特に別の想定で記述されたコードを操作する場合は、混乱しがちです。うまくいけば、ここでのアプローチにより、開発者がモックを作成する正しい方法と、使用するテストランナーを見つける時間を節約できます。

使用する言語やフレームワークに関係なく(おそらくSpringやJUnitの新しいバージョンでも)、概念のベースは上記のJUnitチュートリアルで説明したものと同じです。ハッピーテスト!

基本を理解する

Javaでユニットテストをどのように記述しますか?

JUnitは、Javaで単体テストを作成するための最も有名なフレームワークです。テストする実際のメソッドを呼び出すテストメソッドを作成します。テストケースは、渡されたパラメーターを指定して、期待値に対して戻り値をアサートすることにより、コードの動作を検証します。

Javaに最適なユニットテストフレームワークは何ですか?

Java開発者の大多数は、JUnitが最良のユニットテストフレームワークであることに同意しています。これは1997年以来デファクトスタンダードであり、他のJavaユニットテストフレームワークと比較して確かに最大のサポート量を持っています。

プログラミングの単体テストとは何ですか?

ユニットテストでは、個々のユニット(多くの場合、オブジェクトメソッドは「ユニット」と見なされます)が自動化された方法でテストされます。

なぜJUnitテストを使用するのですか?

JUnitテストは、作成したクラス内のメソッドの動作をテストするために使用されます。メソッドが期待どおりの結果をテストし、場合によっては例外がスローされるケースをテストします。メソッドが希望どおりに例外を処理できるかどうかを確認します。

JUnitの用途は何ですか?

JUnitは、単体テストを簡単に作成するためのさまざまなクラスとメソッドを提供するフレームワークです。

JUnitはオープンソースですか?

はい、JUnitはオープンソースプロジェクトであり、多くのアクティブな開発者によって維持されています。

JUnitが重要なのはなぜですか?

JUnitは、開発者が単体テストを作成するときに使用する必要のある定型文を減らします。

JUnitを発明したのは誰ですか?

KentBeckとErichGammaは最初にJUnitを作成しました。現在、オープンソースプロジェクトには10​​0人以上の貢献者がいます。

UXリサーチャー

その他

UXリサーチャー
ウォーレンバフェットの投資戦略と彼の過ちからの教訓

ウォーレンバフェットの投資戦略と彼の過ちからの教訓

財務プロセス

人気の投稿
SnapchatのIPO:ARPU、ダミーがすべて
SnapchatのIPO:ARPU、ダミーがすべて
やるべき仕事:顧客のニーズを製品ソリューションに変える
やるべき仕事:顧客のニーズを製品ソリューションに変える
リモートUXワークショップを完成させて活用する方法
リモートUXワークショップを完成させて活用する方法
米国の株式クラウドファンディング市場は期待に応えてきましたか?
米国の株式クラウドファンディング市場は期待に応えてきましたか?
WebVRとブラウザエッジコンピューティング革命
WebVRとブラウザエッジコンピューティング革命
 
製品Xの節約–デザイン思考のケーススタディ
製品Xの節約–デザイン思考のケーススタディ
回帰分析を使用した不動産評価–チュートリアル
回帰分析を使用した不動産評価–チュートリアル
フィンテックと銀行:銀行業界は混乱の脅威にどのように対応できますか?
フィンテックと銀行:銀行業界は混乱の脅威にどのように対応できますか?
5つの経済における負の金利の影響の分析
5つの経済における負の金利の影響の分析
クライアントエクスペリエンスの責任者
クライアントエクスペリエンスの責任者
人気の投稿
  • cは何に使用されますか
  • 機械学習の適用方法
  • s法人vsc法人
  • eスポーツ業界の規模
  • scorpとccorpの違い
カテゴリー
ライフスタイル 製品ライフサイクル 設計プロセス 収益と成長 Uxデザイン プロジェクト管理 分散チーム ブランドデザイン 製品の担当者とチーム 計画と予測

© 2021 | 全著作権所有

apeescape2.com