main メソッドを使用してテストするのもやめましょう 低! これは、プロフェッショナルな SpringBoot プロジェクトのテスト方法です。

モール プロジェクトを Spring Boot 2.7にアップグレードしたとき、以前のテスト メソッドの多くが使用できなくなりました.Spring Boot Test が JUnit 5 をサポートするようにアップグレードされていることがわかりました。今日は、Spring Boot Test の新しいバージョンの使用について説明しましょう. これにより、メイン メソッドを使用してテストする必要がなくなりました!

SpringBoot 実際の e コマース プロジェクト モール (50k+star) アドレス: github.com/macrozheng/…

JUnitの紹介

JUnit は、ほとんどの Java 開発環境で既にサポートされている Java 言語の単体テスト フレームワークです。JUnit テストはホワイト ボックス テストとも呼ばれ、プログラマーがプログラムの内部ロジックを知っていることを前提に実行されるテストであり、JUnit を使用すると単体テストを迅速に完了することができます。Spring Boot Test は、JUnit を他のテスト フレームワークと組み合わせて、便利で効率的なテスト方法を提供します. 現在、Spring Boot 2.7 は JUnit 5 を使用しています.

共通の注意事項

Spring Boot Test を使用する前に、一般的に使用されるアノテーションを理解しておきましょう.これは、Spring Boot Test を使用する上で非常に役立ちます.詳細については、次の表を参照してください!

注釈 効果
@SpringBootTest Spring Boot Test を有効にするテスト クラスを指定するために使用されます。デフォルトでモック環境が提供されます。
@ExtendWith 簡単なテストのために Spring 環境を有効にするだけで、Spring Boot 環境を有効にしたくない場合は、拡張機能を次のように構成できます。
@テスト メソッドをテスト メソッドとして指定します
@TestMethodOrder テストクラスのメソッドの実行順序戦略を設定するために使用され、OrderAnnotation が設定されている場合、@Order の順序で実行されます。
@注文 メソッドの実行順序を構成するために使用され、数値が小さいほど実行順序が高くなります
@表示名 テスト クラスとテスト メソッドを指定するために使用されるエイリアス
@BeforeAll テスト クラスのすべてのテスト メソッドの前に 1 回実行され、グローバルな初期化に使用できます
@結局 在测试类的所有测试方法后执行一次,可用于全局销毁资源
@BeforeEach 在测试类的每个测试方法前都执行一次
@AfterEach 在测试类的每个测试方法后都执行一次
@Disabled 禁用测试方法
@RepeatedTest 指定测试方法重复执行
@ParameterizedTest 指定参数化测试方法,类似重复执行,从@ValueSource中获取参数
@ValueSource 用于参数化测试指定参数
@AutoConfigureMockMvc 启用MockMvc的自动配置,可用于测试接口

基本使用

下面我们来聊聊这些注解的基本使用,通过它们可以实现一些基本的单元测试。

集成Spring Boot Test

如果你想在项目中集成Spring Boot Test的话,需要先在pom.xml中添加如下依赖。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
复制代码

最简单的测试

  • 我们先来一个最简单的单元测试,使用@SpringBootTest注解启用单元测试,使用@Test指定测试方法,使用Assertions类的方法来断言结果是否符合预期,具体代码如下。
/**
 * JUnit基本测试
 * Created by macro on 2022/10/11.
 */
@SpringBootTest
public class FirstTest {
    @Test
    public void test() {
        int a=1;
        Assertions.assertEquals(1,a);
    }
}
复制代码
  • 然后点击测试方法左侧按钮即可进行测试。

  • 执行完成后我们在IDEA的执行窗口中就可以看到方法测试通过了,由于使用@SpringBootTest启用了Spring Boot环境,日志中会输出Spring Boot的banner。

指定测试方法顺序

  • 我们可以通过@TestMethodOrder注解和@Order注解来指定所有测试方法的执行顺序,具体代码如下。
/**
 * JUnit指定方法测试顺序
 * Created by macro on 2022/10/10.
 */
@ExtendWith(SpringExtension.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class MethodOrderTest {

    private static final Logger LOGGER = LoggerFactory.getLogger(MethodOrderTest.class);

    @Test
    @Order(1)
    @DisplayName("order为1的方法")
    void lowOrder(){
        LOGGER.info("lowOrder method");
    }

    @Test
    @Order(10)
    @DisplayName("order为10的方法")
    void highOrder(){
        LOGGER.info("highOrder method");
    }
}
复制代码
  • 点击类左侧测试按钮,可以直接运行该类中的所有测试方法。

  • 这里由于我们使用了@DisplayName注解给测试方法取了个别名,而且我们使用了@ExtendWith指定了运行环境为Spring而不是Spring Boot,所以日志中不会出现Spring Boot的banner,执行速度也更快。

生命周期测试

  • 我们还可以通过JUnit 5的生命周期注解来执行测试方法,比如在@BeforeAll注解指定的方法中做全局初始化,在@AfterAll注解指定的方法中做资源的销毁,具体代码如下。
/**
 * JUnit生命周期测试
 * Created by macro on 2022/10/10.
 */
@ExtendWith(SpringExtension.class)
public class LifecycleTest {

    private static final Logger LOGGER = LoggerFactory.getLogger(LifecycleTest.class);

    @BeforeAll
    static void allInit(){
        LOGGER.info("allInit():在所有方法前执行,只执行一次");
    }

    @BeforeEach
    void eachInit(){
        LOGGER.info("eachInit():在测试方法前执行,每个测试方法前都执行");
    }

    @Test
    void successTest() {
        LOGGER.info("successTest():方法执行成功");
    }

    @AfterEach
    void eachDown(){
        LOGGER.info("eachDown():在测试方法后执行,每个测试方法后都执行");
    }

    @AfterAll
    static void allDown(){
        LOGGER.info("allDown():在测试方法后执行,每个测试方法后都执行");
    }

}
复制代码
  • 测试完成后,控制台输出日志如下。

断言的使用

我们可以通过Assertions类中提供的断言API来断言测试结果。

  • 例如我们可以使用fail方法直接断言方法执行失败并输出提示信息。
/**
 * JUnit断言测试
 * Created by macro on 2022/10/11.
 */
@ExtendWith(SpringExtension.class)
public class AssertTest {

    @Test
    void failTest() {
        Assertions.fail("failTest():方法执行失败");
    }
}
复制代码
  • 测试方法执行后会直接抛出异常信息。

  • 还可以通过assertTrueassertNullassertEquals这类方法来断言结果是否符合预期。
/**
 * JUnit断言测试
 * Created by macro on 2022/10/11.
 */
@ExtendWith(SpringExtension.class)
public class AssertTest {

    @Test
    void failTest() {
        Assertions.fail("failTest():方法执行失败");
    }

    @Test
    void trueTest(){
        Assertions.assertTrue(1==1);
    }

    @Test
    void trueFalse(){
        Assertions.assertFalse(3<=2);
    }

    @Test
    void nullTest(){
        String str = null;
        Assertions.assertNull(str);
    }

    @Test
    void notNullTest(){
        String str = "test";
        Assertions.assertNotNull(str);
    }

    @Test
    void equalsTest(){
        String str1 = "test";
        String str2 = "test";
        Assertions.assertEquals(str1,str2);
    }

    @Test
    void notEqualsTest(){
        String str1 = "test";
        String str2 = "test";
        Assertions.assertNotEquals(str1,str2);
    }
}
复制代码
  • 也可以使用assertThrows方法来断言方法中抛出的异常。
/**
 * JUnit断言测试
 * Created by macro on 2022/10/11.
 */
@ExtendWith(SpringExtension.class)
public class AssertTest {

    private static final Logger LOGGER = LoggerFactory.getLogger(LifecycleTest.class);
    @Test
    void throwsTest(){
        Assertions.assertThrows(NullPointerException.class,()->{
            String str = null;
            LOGGER.info(str.toLowerCase());
        });
    }
}
复制代码
  • 还可通过assertTimeout方法断言方法的执行时间。
/**
 * JUnit断言测试
 * Created by macro on 2022/10/11.
 */
@ExtendWith(SpringExtension.class)
public class AssertTest {
    @Test
    void timeoutTest(){
        Assertions.assertTimeout(Duration.ofMillis(1000),()->{
            long sleepTime = 2000;
            ThreadUtil.sleep(sleepTime);
            LOGGER.info("timeoutTest():休眠{}毫秒",sleepTime);
        });
    }
}
复制代码
  • 或者通过assertAll方法将几个断言结合起来使用,Assertions类中提供的工具方法很多,具体可以参考它的代码。
/**
 * JUnit断言测试
 * Created by macro on 2022/10/11.
 */
@ExtendWith(SpringExtension.class)
public class AssertTest {
    @Test
    void assertAllTest(){
        Assertions.assertAll(()->{
            trueTest();
        },()->{
            nullTest();
        },()->{
            equalsTest();
        });
    }
}
复制代码

其他测试

  • Spring Boot Test除了上述测试功能,还可以使用@Disabled来禁用某个测试方法。
/**
 * JUnit其他测试
 * Created by macro on 2022/10/10.
 */
@ExtendWith(SpringExtension.class)
public class OtherTest {
    @Test
    @Disabled("用于测试@Disabled注解")
    void disabledTest() {
        LOGGER.info("disabledTest():方法被执行");
    }
}
复制代码
  • 也可以使用@RepeatedTest来实现循环测试。
/**
 * JUnit其他测试
 * Created by macro on 2022/10/10.
 */
@ExtendWith(SpringExtension.class)
public class OtherTest {

    private static final Logger LOGGER = LoggerFactory.getLogger(LifecycleTest.class);
    private static int count = 0;

    @RepeatedTest(3)
    void repeatedTest() {
        count++;
        LOGGER.info("repeatedTest():重复执行第{}次",count);
    }
}
复制代码
  • 还可以通过@ParameterizedTest来进行参数化测试。
/**
 * JUnit其他测试
 * Created by macro on 2022/10/10.
 */
@ExtendWith(SpringExtension.class)
public class OtherTest {

    private static final Logger LOGGER = LoggerFactory.getLogger(LifecycleTest.class);

    @ParameterizedTest
    @ValueSource(ints = {1,2,3})
    public void parameterizedTest(int a){
        LOGGER.info("parameterizedTest():a={}",a);
    }
}
复制代码
  • 运行以上测试方法后,具体测试结果如下。

项目实战

上面介绍了Spring Boot Test的基本使用,下面我们结合项目来使用下它。

Dao层测试

如果我们的项目需要对数据访问层Dao中的方法进行测试的话,直接注入Mapper接口,在测试方法中直接调用即可,这里对根据ID查询品牌的Mapper方法进行测试。

/**
 * Dao层方法测试
 * Created by macro on 2022/10/11.
 */
@SpringBootTest
public class MapperTest {

    private static final Logger LOGGER = LoggerFactory.getLogger(MapperTest.class);

    @Autowired
    private PmsBrandMapper brandMapper;

    @Test
    void testGetById(){
        long id = 6;
        PmsBrand pmsBrand = brandMapper.selectByPrimaryKey(id);
        LOGGER.info("brand name:{}",pmsBrand.getName());
        Assertions.assertEquals("小米",pmsBrand.getName());
    }
}
复制代码

Service层测试

对业务层Service中的方法测试也是一样的,直接注入Service接口,在测试方法中直接调用即可,这里对根据ID查询品牌的Service方法进行测试。

/**
 * Service层方法测试
 * Created by macro on 2022/10/11.
 */
@SpringBootTest
public class ServiceTest {
    private static final Logger LOGGER = LoggerFactory.getLogger(ServiceTest.class);
    @Autowired
    private PmsBrandService brandService;

    @Test
    void testGetById(){
        long id = 6;
        PmsBrand pmsBrand = brandService.getBrand(id);
        LOGGER.info("brand name:{}",pmsBrand.getName());
        Assertions.assertEquals("小米",pmsBrand.getName());
    }
}
复制代码

Controller层测试

コントローラー レイヤー メソッドのテストでは、要求をシミュレートする必要がある場合があります。MockMvc を使用するだけです。シミュレーション テストでのページング クエリ ブランド リストのインターフェイスは次のとおりです。

/**
 * Controller层方法测试
 * Created by macro on 2022/10/11.
 */
@SpringBootTest
@AutoConfigureMockMvc
public class ControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void mvcTest() throws Exception{
        //模拟发送一个请求访问分页查询品牌列表的接口
        mockMvc.perform(MockMvcRequestBuilders.get("/brand/list") //设置请求地址
                .param("pageNum","1") //设置请求参数
                .param("pageSize","5"))
                .andExpect(MockMvcResultMatchers.status().isOk()) //断言返回状态码为200
                .andDo(MockMvcResultHandlers.print()) //在控制台打印日志
                .andReturn(); //返回请求结果
    }
}
复制代码

コンソールにログを出力することを選択したため、コンソールには次の情報が出力されます。

要約する

今日は、Spring Boot Test を体験してもらいました.Spring Boot の公式テスト フレームワークとして、非常に強力です。主にJUnit 5をベースにしているため、JUnit 5の使い方は基本的に同じです。単体テストに使用してください。プロジェクト全体を開始する必要はありません。より速く、より優れています。

参考文献

JUnit 5 の公式ドキュメント: junit.org/junit5/docs…

プロジェクトのソース コード アドレス

github.com/macrozheng/…

おすすめ

転載: juejin.im/post/7158258490501234695