[ソフトウェアテスト]JUnitユニットテスト

序文

今学期はソフトウェアテストの授業があり、いくつかのテストツールについて実験報告書を書くのがコースワークです。これは確かに良い機会です。結局のところ、フルスタックエンジニアとして、どうしてテストについてさえ知ることができないのでしょうか(笑と涙)。

この記事では、単体テストとJavaの単一テストツールjunit、およびmokitoフレームワークの簡単な使用法について簡単に説明します。さらに、このシリーズには他のテスト関連の記事も含まれます。

1.ユニットテスト

1.ユニットテストとは何ですか?

Baidu百科事典の言葉を借りるには、

単体テストとは、ソフトウェアのテスト可能な最小単位の検査と検証を指します。ユニットテストでのユニットの意味については、一般的に、実際の状況に応じて具体的な意味を判断する必要があります。たとえば、C言語のユニットは関数を指し、Javaではユニットはクラスを指します。グラフィカルソフトウェアはウィンドウまたはメニューを参照できます。お待ちください。一般に、ユニットは、テスト対象の最小の人間定義の機能モジュールです。単体テストは、ソフトウェア開発中に実行されるテストアクティビティの最低レベルであり、ソフトウェアの個々のユニットがプログラムの他の部分から分離してテストされます。

ここにいくつかの重要なポイントがあります:

  • ユニットは人工です
  • ユニットテストは、他の部分から分離された独立したユニットです。

2.なぜユニットテストが必要なのですか?

ここで自分の気持ちをお話ししましょう。私に関しては、単体テストの習慣はありません。単体テストは非常に時間がかかると思います。同時に、時間をかける価値はないと思います。 。すべてが単純なクラッドプロジェクトです。
しかし、私が開発するプロジェクトが大きくなり、要件が複雑になるにつれて、私が行うプロジェクトの品質はますます不安定になり、奇妙なバグが発生することがよくあります。バグが発生すると、問題を特定しなければならないことがよくあります。たとえば、少し前に一般的なIoTプラットフォームを作成しました。デモビデオを録画するときに、補正式(4つの算術演算をサポート)を入力し、当時は他のデバイスでも正常に動作していましたが、当時は異常でした。幸い、プロジェクト全体が自分で作ったもので、実装の詳細もわかっていたのですが、しばらくデバッグしてみると、アルゴリズムの問​​題だと気づきました。その時、書いたときに、アルゴリズムに気づいたのです。実装は負の数をサポートしていません。負の数の演算は「(0-x)」の形式になり、そのデバイスによってアップロードされたデータはたまたま負の数であるため、問題があります。

幸い、フルスタック開発であり、すべて自分で行っています。このプロジェクトをチームで開発した場合、バグの発見にかかる時間は飛躍的に増えると思います。

統合テストなどの大規模なテストでは、バグを見つけるのに時間がかかりすぎるため、各小さなモジュールの正確さを確認するために単体テストが必要です。時間がかかりますが、バグやバグ修正に費やす時間と比較すると、時間の価値があります。

プロジェクトを開発する過程で、多くの場合、以前のバグの遺産を解決することです。
ここに画像の説明を挿入

私はインターネットで関連する要約を見ました、そしてそれらは共有するために非常によく書かれています-ユニットテストとは正確には何ですか?何をすべきですか?

  • ユニットテストは、当社の製品品質にとって非常に重要です。
  • 単体テストは、すべてのテストの中で最も低いタイプのテストです。これは、最初で最も重要な部分です。100%のコードカバレッジを達成することが保証されている唯一のテストです。これは、ソフトウェアテストプロセス全体の基盤および前提です。、単体テストは、バグが多すぎるために開発が遅れて制御不能になるのを防ぎ、単体テストのコストパフォーマンスは最高です。
  • 統計によると、エラーの約80%はソフトウェア設計段階で発生し、ソフトウェアエラーの修正コストはソフトウェアライフサイクルの進行とともに上昇します。バグが発見されるのが遅いほど、それを修正するのに費用がかかり、指数関数的に増加します。
  • ユニットテストのコーダーおよびメインパフォーマーとして、彼は欠陥のないプログラムを作成できる唯一の人です。他の誰もこれを行うことはできません。コードの仕様、最適化、およびテスト容易性。
  • 自信を持ってリファクタリング
  • 実行を3000回自動化する

2. Junit

1.junitとは

JUnitは、Java言語のユニットテストフレームワークです。KentBeckとErichGammaによって設立され、KentBeckから派生したsUnitのxUnitファミリーの中で最も成功するまでに成長しました。JUnitには、JUnit拡張の独自のエコシステムがあります。
現在、junitはjunit5に発展しており、junit4と比べて大きく変化しています。JUnit5は、3つの異なるサブプロジェクトからのいくつかの異なるモジュールで構成されています。
JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

詳しくは公式サイトをご覧ください

注:junitは基本的にJavaユニットテストの主流であり、今日のほとんどのJavaプロジェクトにはjunitがあります

2.JUnitの概念-アサーション

ユニットテストに触れたばかりの学生は、junitを学習するときに、assertメソッドが何を意味し、アサーションとは何であるかを間違いなく疑問に思うでしょう。私が最初に接触したとき、それはその主張が何のためにあるのか疑問に思って、このようでした。

実際、アサーションは実際にはいくつかのヘルパー関数であり、テスト対象のメソッドが期待どおりに機能するかどうかを判断するために使用されます。通常、これらのヘルパー関数はアサーションと呼ばれます。

3.Junitの簡単な使用

次のデモはMavenプロジェクトです

①依存関係をインポートする

<dependencies>
    <!-- ... -->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>5.8.1</version>
        <scope>test</scope>
    </dependency>
    <!-- ... -->
</dependencies>
<build>
    <plugins>
        <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.22.2</version>
        </plugin>
        <plugin>
            <artifactId>maven-failsafe-plugin</artifactId>
            <version>2.22.2</version>
        </plugin>
    </plugins>
</build>

②テストケースを書く

こちらが公式サイトの例です

import static org.junit.jupiter.api.Assertions.assertEquals;

import example.util.Calculator;

import org.junit.jupiter.api.Test;

class MyFirstJUnitJupiterTests {
    
    

    private final Calculator calculator = new Calculator();

    @Test
    void addition() {
    
    
        assertEquals(2, calculator.add(1, 1));
    }

}

上記の例では、calculator.add(1、1)の戻り値が2に等しくなることを示しています。

実行アイコンをクリックするだけで、アイデア内でテストを実行すると非常に便利です。
ここに画像の説明を挿入
アイデア内にない場合は、mian関数を追加して実行するだけです。

実行中のアサーションが正しい場合、プログラムは次のようになります。
ここに画像の説明を挿入
アサーションが間違っている場合、junitはAssertionFailedError例外をスローし、何が問題だったかを通知します。
ここに画像の説明を挿入

4.SpringBoot環境でのjunitの使用

もちろん、SpringBoot環境などの実際の開発では、多くのビジネスコードクラスがSpringコンテナに注入され、注入された他のクラス間に依存関係があります。以前のようにテストオブジェクトを作成することは明らかに非現実的です。では、この問題の解決策は何ですか?
SpringBoot+SSMプロジェクトでのjunitの使用法を紹介します。

①依存関係をインポートする

SpringBootに依存関係を統合します。関連する依存関係をテストする必要がある場合は、対応するテストモジュールを導入するだけで済みます。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
    </exclusions>
</dependency>

②テストケースを書く

テストオブジェクトクラス

package com.example.demo.service;

import org.springframework.stereotype.Component;

@Component
public class Junit5Test {
    
    
    public int add(int i,int j){
    
    
        System.out.println("-----------add被执行了---------------");
        return i+j;
    }
    public int doAdd(int i,int j){
    
    
        System.out.println("------------doAdd被执行了--------------");
        //被mock的函数会先执行,且只会执行一次
        System.out.println(add(i,j));
        return add(i,j);
    }
}

テストケース

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.mock.mockito.SpyBean;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;

//初始化一个spring的上下文,使其可以使用一些注入(junit5)。junit4会用runwith
@SpringBootTest
class Junit5TestTest {
    
    
    @Autowired
    Junit5Test junit5Test;
    //会初始化一次
    @BeforeAll
    static void init(){
    
    
        System.out.println("init");
    }
    //所有测试方法前都会执行一遍
    @BeforeEach
    void each(){
    
    
        System.out.println("each");
    }

    @Test
    void getDeviceStatistic() {
    
    
        Assertions.assertEquals(2,spyJunit5Test.doAdd(1,1));
    }
}

SpringBootコンテキストが必要な場合は、@ SpringBootTestアノテーションを追加するだけで済みます。もちろん、古いプロジェクトでは、@ RunWith(SpringRunner.class)が表示される場合があります。前者はjunit5の書き込み方法であり、後者はjunit4の書き込み方法です。

スプリングコンテナにテストオブジェクトが必要な場合は、通常どおりに注入します。

@Autowired
Junit5Test junit5Test;

3.モックデータ-mockitoフレームワークの使用

1.モック

実際の開発と単一のテストでは、テストオブジェクトがネットワークデータを要求したり、データベースを変更したりする必要がある場合がありますが、変更したくない場合は、mockitoフレームワークを使用してデータをモックできます。

いわゆるモックとは、作成するコードが一部のオブジェクトに依存していて、これらのオブジェクトを手動で作成するのが難しい場合(つまり、HttpRequestやその他のオブジェクトなどの初期化方法がわからない場合)、仮想を使用することを意味します。テストするオブジェクト。クラスファイルを渡すため、静的コードブロックは引き続き実行されますが、コンストラクターとインスタンスコードブロックは実行されません

2.杭打ちスタブ

いわゆるスタブスタブは、テストに必要なテストデータを提供するために使用されます。これはモックオブジェクトであるため、一部のメソッドは戻り値を認識しない場合があるため、戻り値を想定する必要があります。対応する応答は、さまざまな相互作用に対して設定できます。つまり、when(…).thenReturn(…)およびdoReturn(…)。when(…)を使用して、メソッド呼び出しの戻り値を設定できます。

例えば:


//You can mock concrete classes, not only interfaces
 LinkedList mockedList = mock(LinkedList.class);
 
 //stubbing
 when(mockedList.get(0)).thenReturn("first");
 when(mockedList.get(1)).thenThrow(new RuntimeException());
  • doReturn()。when()には副作用はありません。積み上げ中はメソッドは実行されません。
  • when()。thenReturn()には副作用があります。これは、積み上げ中にメソッドが最初に実行されることを意味し、特定の副作用を引き起こす可能性があります。

3.@MockBeanおよび@SpyBean

もちろん、SpringBoot環境では、@ SpyBeanアノテーションと@MockBeanアノテーションを直接使用して、@ Autowiredの挿入されたオブジェクトを置き換え、仮想オブジェクトを作成することもできます。

@MockBean @MockBean
のみを使用する場合、変更されたオブジェクトはモックされるため、Junit5Testのadd()メソッドは特定の詳細を実行しなくなりますが、MockBeanはターゲットオブジェクトのすべてのメソッドをモックするため、テストを行うことはできません。実際に実行されました。、テストできません。

@SpyBean
であり、場合によっては実際のメソッドを実行する必要があります。一部のメソッドをモックするだけで、@SpyBeanを使用できます。
@SpyBeanで装飾されたspyJunit5Testは、(spyJunit5Test.add(1,1))。thenReturn(2);の場合にのみ実際のオブジェクトであり、addメソッドはスタブされ、他のメソッドは引き続き呼び出されます。

これが例です

package com.example.demo.service;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.mock.mockito.SpyBean;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;

//初始化一个spring的上下文,使其可以使用一些注入(junit5)。junit4会用runwith
@SpringBootTest
class Junit5TestTest {
    
    
//    @Autowired
//    Junit5Test junit5Test;
    //介于@Autowired和@MockBean之间的注解,当配置了when时使用mock,没有进行打桩则走正常方法
    @SpyBean
    Junit5Test spyJunit5Test;
    //完全使用mock方法,方法都不会去真正执行。当调用mockBean修饰的方法时,不会去真正执行该方法,只会返回打桩后的值,如果没有打桩时会返回默认值,比如int就返回0。
//    @MockBean
//    Junit5Test mockJunit5Test;
    //会初始化一次
    @BeforeAll
    static void init(){
    
    
        System.out.println("init");
    }
    //所有测试方法前都会执行一遍
    @BeforeEach
    void each(){
    
    
        System.out.println("each");
    }

    @Test
    void getDeviceStatistic() {
    
    
        Assertions.assertEquals(1,1);
    }
    @Test
    void testDeviceStatisticByMock() {
    
    

        //配置mock
        when(spyJunit5Test.add(1,1)).thenReturn(2);
        Assertions.assertEquals(2,spyJunit5Test.doAdd(1,1));
    }
}

最後に、皆さんにハッピープログラマーの日をお祈りします!

おすすめ

転載: blog.csdn.net/qq_46101869/article/details/120942569