zoukankan      html  css  js  c++  java
  • 单元测试

    单元测试 - SpringBoot2+Mockito实战

    在真实的开发中,我们通常是使用SpringBoot的,目前SpringBoot是v2.4.x的版本(SpringBoot 2.2.2.RELEASE之前默认是使用 JUnit4,之后版本默认使用Junit5);所以我们写个基于SpringBoot2.4+H2的内存库的简单例子,同时加点必要的单元测试。@pdai

    SpringBoot对单测试的差异

    SpringBoot 2.2.2.RELEASE之前默认是使用 JUnit4,之后版本默认使用Junit5。

    pringboot+junit4:

    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class SpringBootQuickStartApplicationTests {
    
        private MockMvc mvc;
    
        @Before
        public void setUp() throws Exception {
            mvc = MockMvcBuilders.standaloneSetup(new UserController()).build();
        }
    
        @Test
        public void contextLoads() throws Exception {
            RequestBuilder request = null;
           
            request = MockMvcRequestBuilders.get("/")
                    .contentType(MediaType.APPLICATION_JSON);
            mvc.perform(request)
                    .andExpect(MockMvcResultMatchers.status().isOk())
                    .andDo(MockMvcResultHandlers.print())
                    .andReturn();
       }
    }
    

    springboot+junit5:

    @SpringBootTest
    // 使用spring的测试框架
    @ExtendWith(SpringExtension.class)
    class SpringbootQuickStartApplicationTests {
    
        private MockMvc mockMvc;
    
        @BeforeEach // 类似于junit4的@Before
        public void setUp() throws Exception {
            mockMvc = MockMvcBuilders.standaloneSetup(new UserController()).build();
        }
    
        @Test
        void contextLoads() throws Exception {
            RequestBuilder request = null;
    
            request = MockMvcRequestBuilders.get("/")
                    .contentType(MediaType.APPLICATION_JSON);
            mockMvc.perform(request)
                    .andExpect(MockMvcResultMatchers.status().isOk())
                    .andDo(MockMvcResultHandlers.print())
                    .andReturn();
        }
    }
    

    项目实践

    Spring Boot 2.4.2 + H2 + Lombok + Spring Boot Test (默认包含了 Junit5 + Mockito)。

    Demo程序准备

    • pom.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.4.2</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>tech.pdai</groupId>
        <artifactId>java-springboot-unit5</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>java-springboot-unit5</name>
        <description>java-springboot-unit5</description>
        <properties>
            <java.version>1.8</java.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jpa</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>com.h2database</groupId>
                <artifactId>h2</artifactId>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-configuration-processor</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <excludes>
                            <exclude>
                                <groupId>org.projectlombok</groupId>
                                <artifactId>lombok</artifactId>
                            </exclude>
                        </excludes>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
    
    • application.yml
    spring:
      datasource:
        platform: h2
        driverClassName: org.h2.Driver
        url: jdbc:h2:mem:testdb;MODE=MYSQL;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false
        username: pdai
        password: pdai
        schema: classpath:db/schema/user-schema.sql
        data: classpath:db/data/user-data.sql
        initialization-mode: always
      h2:
        console:
          settings:
            trace: true
            web-allow-others: true
          enabled: true
          path: /h2-console
      jpa:
        show-sql: true
        hibernate:
          ddl-auto: update
        generate-ddl: false
        open-in-view: false
    
    • 数据库文件准备

    schema

    DROP TABLE IF EXISTS user;
    CREATE TABLE user (
        id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
        name varchar(35),
        phone varchar(35)
    );
    

    data

    insert into user(id,name,phone) values(1,'pdai','123456');
    insert into user(id,name,phone) values(2,'zhangsan','123456');
    
    • entity
    package tech.pdai.springboot2unit5.entity;
    
    import lombok.AllArgsConstructor;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    import lombok.Setter;
    
    import javax.persistence.*;
    
    /**
     * User.
     */
    @Entity
    @Getter
    @Setter
    @NoArgsConstructor
    @AllArgsConstructor
    @Table(name = "user")
    public class User {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer id;
    
        @Column
        private String name;
    
        @Column
        private String phone;
    }
    
    • dao
    package tech.pdai.springboot2unit5.dao;
    
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.stereotype.Repository;
    import tech.pdai.springboot2unit5.entity.User;
    
    /**
     * user dao.
     */
    @Repository
    public interface UserRepository extends JpaRepository<User, Integer> {
    }
    
    • service
    package tech.pdai.springboot2unit5.service;
    
    import tech.pdai.springboot2unit5.entity.User;
    
    import java.util.List;
    
    /**
     * user service.
     */
    public interface IUserService {
    
        /**
         * find all user.
         *
         * @return list
         */
        List<User> findAll();
    }
    
    package tech.pdai.springboot2unit5.service.impl;
    
    import org.springframework.stereotype.Service;
    import tech.pdai.springboot2unit5.dao.UserRepository;
    import tech.pdai.springboot2unit5.entity.User;
    import tech.pdai.springboot2unit5.service.IUserService;
    
    import java.util.List;
    
    /**
     * User service impl.
     */
    @Service
    public class UserServiceImpl implements IUserService {
    
        /**
         * user dao.
         */
        private final UserRepository userRepository;
    
        /**
         * init.
         *
         * @param userRepository2 user dao
         */
        public UserServiceImpl(final UserRepository userRepository2) {
            this.userRepository = userRepository2;
        }
    
        /**
         * find all user.
         *
         * @return list
         */
        @Override
        public List<User> findAll() {
            return userRepository.findAll();
        }
    }
    
    • Controller
    package tech.pdai.springboot2unit5.controller;
    
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    import tech.pdai.springboot2unit5.entity.User;
    import tech.pdai.springboot2unit5.service.IUserService;
    
    import java.util.List;
    
    /**
     * User controller.
     */
    @RestController
    public class UserController {
    
        /**
         * user service.
         */
        private final IUserService userService;
    
        /**
         * init.
         *
         * @param userService2 user service
         */
        public UserController(final IUserService userService2) {
            this.userService = userService2;
        }
    
        /**
         * find user list.
         *
         * @return list
         */
        @GetMapping("user/list")
        public ResponseEntity<List<User>> list() {
            return ResponseEntity.ok(userService.findAll());
        }
    }
    

    测试类

    在实际的项目中可以使用profile来区分测试ut,使用test profile(包含H2内存库),真实环境使用MySQL或其它。

    • main
    package tech.pdai.springboot2unit5;
    
    import org.junit.jupiter.api.DisplayName;
    import org.junit.jupiter.api.Test;
    import org.junit.jupiter.api.extension.ExtendWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.context.annotation.Profile;
    import org.springframework.test.context.junit.jupiter.SpringExtension;
    import tech.pdai.springboot2unit5.service.IUserService;
    
    import static org.junit.jupiter.api.Assertions.assertEquals;
    import static org.junit.jupiter.api.Assertions.assertFalse;
    
    /**
     * A way to test from H2.
     * <p>
     * Just a demo, and change profile to 'test' for H2, and 'product' for MySQL.
     */
    @Profile("default")
    @ExtendWith(SpringExtension.class)
    @SpringBootTest
    class JavaSpringbootUnit5ApplicationTests {
    
        @Autowired
        IUserService userService;
    
        @Test
        @DisplayName("Integration test")
        void contextLoads() {
            assertFalse(userService.findAll().isEmpty());
            assertEquals("pdai", userService.findAll().get(0).getName());
        }
    
    }
    
    • controller

    mockMvc

    package tech.pdai.springboot2unit5.controller;
    
    import org.junit.jupiter.api.DisplayName;
    import org.junit.jupiter.api.Test;
    import org.junit.jupiter.api.extension.ExtendWith;
    import org.mockito.Mockito;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
    import org.springframework.boot.test.mock.mockito.MockBean;
    import org.springframework.http.MediaType;
    import org.springframework.test.context.junit.jupiter.SpringExtension;
    import org.springframework.test.web.servlet.MockMvc;
    import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
    import tech.pdai.springboot2unit5.entity.User;
    import tech.pdai.springboot2unit5.service.IUserService;
    
    import java.util.Collections;
    
    import static org.mockito.Mockito.times;
    import static org.mockito.Mockito.verify;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
    
    /**
     * user controller test - use mockito.
     */
    @ExtendWith(SpringExtension.class)
    @WebMvcTest(value = UserController.class)
    class UserControllerTest {
    
        @Autowired
        private MockMvc mockMvc;
    
        @MockBean
        private IUserService userService;
    
        /**
         * test find all user.
         *
         * @throws Exception exception
         */
        @Test
        @DisplayName("Test findAll()")
        public void list() throws Exception {
            Mockito.when(userService.findAll()).thenReturn(
                    Collections.singletonList(new User(1, "pdai.tech", "1221111")));
    
            mockMvc.perform(MockMvcRequestBuilders.get("/user/list")
                    .accept(MediaType.APPLICATION_JSON))
                    .andExpect(status().isOk());
    
            verify(userService, times(1)).findAll();
        }
    }
    
    • service
    
    package tech.pdai.springboot2unit5.service.impl;
    
    import org.junit.jupiter.api.Test;
    import org.junit.jupiter.api.extension.ExtendWith;
    import org.mockito.Mock;
    import org.mockito.Mockito;
    import org.springframework.test.context.junit.jupiter.SpringExtension;
    import tech.pdai.springboot2unit5.entity.User;
    
    import java.util.Collections;
    import java.util.List;
    
    import static org.junit.jupiter.api.Assertions.assertFalse;
    import static org.mockito.Mockito.times;
    import static org.mockito.Mockito.verify;
    
    @ExtendWith(SpringExtension.class)
    class UserServiceImplTest {
    
        @Mock
        private UserServiceImpl userService;
    
        @Test
        public void findAll() {
            //Given
            Mockito.when(userService.findAll()).thenReturn(
                    Collections.singletonList(new User(1, "pdai.tech", "1221111")));
    
            //When
            List<User> userDtoList = userService.findAll();
    
            //Then
            assertFalse(userDtoList.isEmpty());
            verify(userService, times(1)).findAll();
        }
    }
    
    • 测试结果

    Util测试

    如果包含静态util的测试还可以加PowerMokito.

    具体可以参考:测试结合powermock支持静态方法

    相关文章

    B1. 开发之单元测试:单元测试是从开发侧保证软件质量的第一步,本章将具体介绍单元测试相关的内容和工具,比如Junit,Mockito等。

    • 单元测试:浅谈单元测试
      • 单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。这是基础,所以围绕着单元测试,我从网上搜集和总结了相关的概念,以助你完善体系。
    • 单元测试:Junit4 详解
      • JUint是Java编程语言的单元测试框架,用于编写和运行可重复的自动化测试。本文主要针对Junit4要点进行梳理总结。
    • 单元测试:Junit5 详解
      • JUnit 5是JUnit的下一代。目标是为JVM上的开发人员端测试创建一个最新的基础。这包括专注于Java 8及更高版本,以及启用许多不同风格的测试。
    • 单元测试:Mockito 详解
      • Mock通常是指,在测试一个对象A时,我们构造一些假的对象来模拟与A之间的交互,而这些Mock对象的行为是我们事先设定且符合预期。通过这些Mock对象来测试A在正常逻辑,异常逻辑或压力情况下工作是否正常。而Mockito是最流行的Java mock框架之一。
    • 单元测试:IDEA下单元测试详解
      • 工欲善其事必先利其器,我们在写单元测试一定要使用工具,这将能大幅度提升编码的效率。本文以IDEA为例,看看如何利用插件提升效率。
    • 单元测试 - SpringBoot2+Mockito实战
      • 在真实的开发中,我们通常是使用SpringBoot的,目前SpringBoot是v2.4.x的版本(SpringBoot 2.2.2.RELEASE之前默认是使用 JUnit4,之后版本默认使用Junit5);所以我们写个基于SpringBoot2.4+H2的内存库的简单例子,同时加点必要的单元测试。

    B2. 开发之代码质量:项目的代码通常是一个团队共同完成的,要保障代码质量的首要前提就是统一代码的风格,命名规范,静态代码检查等等。

    更多文章请参考 [Java 全栈知识体系](https://pdai.tech)
  • 相关阅读:
    5 Things Every Manager Should Know about Microsoft SharePoint 关于微软SharePoint每个经理应该知道的五件事
    Microsoft SharePoint 2010, is it a true Document Management System? 微软SharePoint 2010,它是真正的文档管理系统吗?
    You think you use SharePoint but you really don't 你认为你使用了SharePoint,但是实际上不是
    Introducing Document Management in SharePoint 2010 介绍SharePoint 2010中的文档管理
    Creating Your Own Document Management System With SharePoint 使用SharePoint创建你自己的文档管理系统
    MVP模式介绍
    权重初始化的选择
    机器学习中线性模型和非线性的区别
    神经网络激励函数的作用是什么
    深度学习中,交叉熵损失函数为什么优于均方差损失函数
  • 原文地址:https://www.cnblogs.com/pengdai/p/14521971.html
Copyright © 2011-2022 走看看