Java开发 - Spring Test知多少?

更新时间:2023-01-29 10:45:41 点击次数:481次
Spring Test的作用
在普通测试环境下,我们在使用Spring的时候,需要手动加载Spring配置,手动从Spring容器中获取对象,前文中的使用全是如此,这也就违背了我们使用Spring框架的意愿:自动创建对象,自动管理对象。我们把这种用法叫自动装配。

Spring还有一个用处,使用@Sql注解,此注解可在测试类方法之前定义,提前或延后执行某段sql语句,在测试中也经常使用。

在项目中加入Spring Test
添加依赖
        <!--Spring Test依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.3.14</version>
        </dependency>
Spring Test无法单独工作,仍需配合其他测试依赖项和其他Spring依赖一起使用,可参照Mybatis一文中的依赖进行添加,亦可在原项目中直接操作。但要注意,要和其他Spring依赖项的版本保持一致,切记。

创建测试类
package cn.codingfire.mybatis;
 
public class MybatisTest {
}
此时需在测试类上添加@SpringJUnitConfig注解:

package cn.codingfire.mybatis;
 
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
 
@SpringJUnitConfig(SpringConfig.class)
public class MybatisTest {
}
接着我们可以在此类中添加Spring的配置类,这样,在此类中任何方法之前,都会先加载Spring的配置类,Spring容器中存在的类就都可以实现自动装配了。我们以环境变量为例:

package cn.codingfire.mybatis;
 
import cn.codingfire.mybatis.config.SpringConfig;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
 
@SpringJUnitConfig(SpringConfig.class)
public class MybatisTest {
    @Autowired
    Environment env;
 
    @Test
    public void testEnvironment() {
        System.out.println(env.getProperty("datasource.url"));
        System.out.println(env.getProperty("datasource.driver"));
        System.out.println(env.getProperty("datasource.username"));
        System.out.println(env.getProperty("datasource.password"));
    }
}

接着运行此测试方法,查看输出: 

已经成功输出了我们在properties文件中配置的信息。对比之前Mybatis中的测试方法如下:
    @Test
    public void loadBasicInfo() {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
        ConfigurableEnvironment environment = ac.getEnvironment();
        System.out.println(environment.getProperty("datasource.url"));
        System.out.println(environment.getProperty("datasource.driver"));
        System.out.println(environment.getProperty("datasource.username"));
        System.out.println(environment.getProperty("datasource.password"));
        ac.close();
    }
先获取ac,再获取environment,最后再关闭ac,简化了太多步骤。使用Spring Test,我们只需关注测试的内容本身,而不用去管环境的问题,效果要更好。再增删改查时也不需要再关注开头和结尾的那几段代码,这种重复性的操作被省略,也是自动装配的精髓之一。

关于@Autowired注解,就是自动装配的意思,我们可以尝试着给其他的对象添加此注解:

会看到AdminMapper报一个错,这里有个小知识点。这是因为编译器问题导致无法识别,解决办法是在AdminMapper的类中添加@Repository注解,回来后再看,正常来说报错会消失,但有的人的不会消失,可在注解里添加required属性为false:
 此时,问题已经解决了,它的意思是,能装配上就装,不能装配也不强求。有意思的是,即使你不管这个报错,方法也可正常运行,不存在任何影响。

Spring Test下的测试方法
我们以插入方法为例,做个前后对比。

未使用Spring Test的插入方法测试:
   @Test
    public void testInsert() {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
        AdminMapper adminMapper = ac.getBean(AdminMapper.class);
        Admin admin = new Admin();
        admin.setUsername("admin04");
        admin.setPassword("123456");
        adminMapper.insert(admin);
        ac.close();
    }
使用了Spring Test的插入方法测试 :

    @Test
    public void testInsert() {
        Admin admin = new Admin();
        admin.setUsername("admin04");
        admin.setPassword("123456");
        adminMapper.insert(admin);
    }
前后对比明显,自动装配后,adminMapper由系统创建管理,可在类中直接使用,简化了代码。 

@Sql注解
@Sql注解的作用和注意事项
Spring Test测试类中,还可以使用@Sql注解,他可以加载某些脚本.sql的脚本,可以在测试前后执行一些给定的sql语句。它的作用是可以在测试时进行反复测试,在Mybatis中,我们在测试时,有时为了使mapper的方法运行成功,需要运行插入的方法,这就很不友好了,增加了测试的成本,比如我删除某条数据后,表中没有数据,我要再执行删除操作前,必须要再插入一条数据,否则会报错,而我使用@Sql注解,就可以解决这个问题,使得每次测试不需要再关注其他的方法。

使用此注解要注意几个问题:

@Sql注解可以添加在单独的方法中,仅对此方法有效,也可添加在测试类上,对类中所有的方法有效。如果类和方法上都添加了相同的@Sql注解,仅方法上的生效
可方法前执行.sql脚本,也可方法后执行.sql脚本,通过executionPhase属性来管理
@Sql注解可添加多个
@Sql注解怎么用
首先,我们需要先创建一个.sql文件,选择file,创建一个truncate.sql:
在test下的resoutces文件中创建,在此文件中可以看到和再sql工具中一样,是有sql提醒的。 truncate的意思是截断,在sql中意味着清空整张表。我们知道,数据库表中不允许插入相同的两条数据,否则就会报重复的错误,使用此注解,在每次插入前都清空整张表就可以频繁测试,看代码:
   @Sql(scripts = {"classpath:truncate.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
    @Test
    public void testInsert() {
        Admin admin = new Admin();
        admin.setUsername("admin04");
        admin.setPassword("123456");
        adminMapper.insert(admin);
    }
可以在插入前清空整张表,以达到频繁插入的测试。

以删除为例,我们想删除的时候数据库表中一直有数据。此时,也可以使用此注解,接下来我们来说说怎么同时使用多个.sql脚本,首先创建一个插入的.sql脚本:
 看代码:

    @Sql(scripts = {"classpath:truncate.sql", "classpath:insert.sql"})
    @Test
    public void testDelete() {
        adminMapper.deleteById(1L);
    }
每次执行次方法都是成功的。如果你想在方法执行后再执行某些sql的话,可以设置@Sql的executionPhase属性为Sql.ExecutionPhase.AFTER_TEST_METHOD:

executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD
断言
我们通常在测试类中使用Assertions类的静态方法对测试结果进行预测,帮助我们发现代码中可能存在的问题,一旦不符合预测的正确结果就会报错,大大提高代码的正确性。常用的一些断言方法有:

assertEquals():断言匹配(相等)
assertNotEquals():断言不匹配(不相等)
assertTrue():断言为“真”
assertFalse():断言为“假”
assertNull():断言为null
assertNotNull():断言不为null
assertThrows():断言抛出异常
assertDoesNotThrow():断言不会抛出异常
其他
接下来我们挑几个在代码中来看使用效果。

assertEquals():
    @Sql(scripts = {"classpath:truncate.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
    @Test
    public void testInsert() {
        Admin admin = new Admin();
        admin.setUsername("admin04");
        admin.setPassword("123456");
        int index = adminMapper.insert(admin);
        System.out.println(index);
        Assertions.assertEquals(1,index);
    }
Assertions.assertEquals 有两个参数,第一个是expected,是期望的值,第二个是ectual,是实际的值,如果预测的不对,就会报错,正确则没有任何反应。

assertTrue()
还以插入为例,看代码:

    @Sql(scripts = {"classpath:truncate.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
    @Test
    public void testInsert() {
        Admin admin = new Admin();
        admin.setUsername("admin04");
        admin.setPassword("123456");
        int index = adminMapper.insert(admin);
        System.out.println(index);
        boolean isThanZero = index > 0 ? true : false;
        Assertions.assertTrue(isThanZero);
    }
断言isThanZero,即影响的行数大于0,则说明插入成功,否则将报异常。

assertNull()
以根据id获取数据为例:

    @Test
    public void getById() {
        Admin admin = adminMapper.getById(10L);
        Assertions.assertNull(admin);
    }
id为10的数据表中没有,所以语言admin为null,是正确的,符合我们的预期,不会报错。

assertThrows()
前面我们说过重复插入数据会把哦重复插入的异常,这时就不能在插入前清空表了,我们以此为例,看代码:

    @Test
    public void testInsert() {
        Admin admin = new Admin();
        admin.setUsername("admin04");
        admin.setPassword("123456");
        Assertions.assertThrows(DuplicateKeyException.class, () -> {
            adminMapper.insert(admin);
        });
    }
正常来说,连续执行两次,就会抛出重复插入的异常,但是我们做了断言后,就不会有任何输出,反而会在第一次执行时抛出下面这段异常:

org.opentest4j.AssertionFailedError: Expected org.springframework.dao.DuplicateKeyException to be thrown, but nothing was thrown.

意思是说,我们预测会抛出重复的异常,但是什么也没有抛出。这是正常的,因为第一次插入成功了。

到这里,断言就写完了,上面列出来的每一类中的一个都给出了案例 ,照葫芦画瓢,对另一个取反就是另一个,相信聪明如大家已经知道该怎么用了,不再赘述。

本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责,本站只提供参考并不构成任何投资及应用建议。本站是一个个人学习交流的平台,网站上部分文章为转载,并不用于任何商业目的,我们已经尽可能的对作者和来源进行了通告,但是能力有限或疏忽,造成漏登,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

回到顶部
嘿,我来帮您!