Spring 5 新特性之 测试

Posted by NotGeek on December 5, 2018

Spring 5 测试相关的问题

议题

  • 单元测试
  • Spring 5 单元测试
  • Spring 5 集成测试

单元测试

JUnit 5

  • JUnit Platform: 启动测试框架的基础
  • JUnit Jumpiter: 测试框架的编程模型和扩展模型
  • JUnit Vintage: JUnit 3、JUnit4 测试引擎

测试驱动,接口驱动,在编译工具中,有些插件,

JUnit 没有高并发的测试。

Spring 会扩展 JUnit

JUnit JUpiter

  • @BeforeEach@AfterEach
  • @BeforeAll@AfterAll
  • @RepeatedTest
  • @ParameterizedTest
1
	HTTP/2 最好要了解一下。和 WebSocket 很像。,http 短链接并不是它有多好,还是因为设备的问题。我们以前用 C/S 模式,现在都是 PTP 模式,密集型计算,还是 服务器强一点。

​ 单元测试,很重要的结果要单一,你的依赖要单一,你的职责要单一,你的结果要单一,如果你每次输出的结果都不一样,那就会变得很麻烦了。

​ 单元测试要保证 幂等性

集成测试可能会不同,可能你的数据库的结果会累加,然后一直变化。集成测试,外部依赖更强。

1
2
3
4
@SpringJunitconfig(TestConfig.class)
class configurationClassJUitJupiterSpringTests{
    // .....
}

和 Spring Boot 有一定的相似

Spring 4:

org.junit.Test @Test

Spring 5:

org.junit.jupiter.api.Test @Test

Spring 5 Spring 4
@BeforeEach @Before
@AfterEach @After
@BeforeAll @BeforeClass
@AfterAll @AfterClass

Spring 5 依赖了 jupiter

但是并没有完整的依赖。

Spring 5 整合了测试的新特性

Spring 5 单元测试

  • MOCK 对象
    • Environment
    • Servlet API
    • JNDI
    • Protlet API

我需要对象是摸拟的 API

javax.servlet.http.HttpServletRequest 接口,一种是动态,一种是静态,模拟它,

Mock 就是动态代理你的接口,

动态代理之后,我们可以有很多事情去做,

1

当你要怎么去取舍,当你需要 Environment API

org.springframework.core.env.Environment
1
2
3
4
5
6
7
8
9
10
11
public interface Environment extends PropertyResolver {
    String[] getActiveProfiles();

    String[] getDefaultProfiles();

    /** @deprecated */
    @Deprecated
    boolean acceptsProfiles(String... var1);

    boolean acceptsProfiles(Profiles var1);
}

org.springframework.core.env.PropertyResolver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public interface PropertyResolver {
    boolean containsProperty(String var1);

    @Nullable
    String getProperty(String var1);

    String getProperty(String var1, String var2);

    @Nullable
    <T> T getProperty(String var1, Class<T> var2);

    <T> T getProperty(String var1, Class<T> var2, T var3);

    String getRequiredProperty(String var1) throws IllegalStateException;

    <T> T getRequiredProperty(String var1, Class<T> var2) throws IllegalStateException;

    String resolvePlaceholders(String var1);

    String resolveRequiredPlaceholders(String var1) throws IllegalArgumentException;
}

就像我们 System 中的 #getProperty(String key)

JNDI 用到很少了,很少环境用 EJB ,Tomcat 和 JDNI 了, JNDI 可以获取数据库的源信息,Spring 中借鉴了 J2EE 很多东西,

org.springframework.context.ApplicationContext

​ IOC 容器,为了 反转控制 , USER 对象,控制的是对象的来源,User 对象,在你的上下文里边可以去取,可以去注入, IOC 反转控制是一个规则,它是一个原则,

​ DI 依赖注入是它的一个实现,依赖注入有两个事情,一个是注入,@Autowriredget()@Set() , 依赖查找, BeanFacotry#GetBean(Name) ,他就是一个 Bean 的名称,

​ Spring 有时候欺负人没有文化, JDNI 里边叫做, Context#lookup(Name) ,返回的就是你的对象,无非是你的名字的命名规则不一样,Context 就是为了获取上下文, Context#bind(name, bean) 注入。

Protlet API 门户的小程序

Protlet 是 IBM 的一个东西。

Spring 5 集成测试

  • Spring TextContext Framework
    • @ContextConfiguratin
    • @SpringJUnitConfig
  • Spring WebMVC Test Framework
    • @SpringJUnitWebConfig
    • @RestTemplate
  • Mockito 整合

​ 学习方法很重要,

TextContext 测试上下文,

  • @Before 上文
  • @Test @After 下文
org.springframework.test.context.TestContext

​ 有时候,我可能需要这个类元信息的一个表达,我这个方法叫什么名字,是通过上下文来进行表达的。关联的 Spring的上下文, ApplicationContext 帮助你去获取上下文, #getTestClass() 获取 测试类 是什么名字, #getTestInstance() 获取测试实例是什么,就是创建这个对象的时候这个实例是什么,那么,一个 ,一个是 实例 ,大多数情况,实例没有太大的用处, #getTestMethod() 测试方法是什么。

org.springframework.test.context.TestContext#getTestMethod

java.lang.reflect.Method

  • java.lang.reflect.Method#getName
  • java.lang.reflect.Method#getReturnType
  • java.lang.reflect.Method#getGenericReturnType
  • java.lang.reflect.Method#getParameterTypes

各种元信息。

org.springframework.test.context.TestContext#getTestException

异常情况是怎样的。

整合 SpringApplicationContext 上下文 和 JUnit 的上下文的整合。

但是 TestContext 用的不是很多,

@ContextConfiguration 是 Spring 一直推崇的东西 Since 2.5

@Autowired 也是 Since 2.5

org.springframework.test.context.ContextConfiguration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface ContextConfiguration {
    
    @AliasFor("locations")
    String[] value() default {};

    /**
     * 要去取你的 XML 文件,以前的 Spring 是用 XML 来做的,后来都是用 Annotation 来做的,
     * XML 的有支持,Annotation 肯定也支持
     */
    @AliasFor("value")
    String[] locations() default {};

    /***
     * Class 的支持
     */
    Class<?>[] classes() default {};

    Class<? extends ApplicationContextInitializer<?>>[] initializers() default {};

    boolean inheritLocations() default true;

    boolean inheritInitializers() default true;

    Class<? extends ContextLoader> loader() default ContextLoader.class;

    String name() default "";
}

java.util.Map#put

1
2
3
4
5
/**
 * 如果这个 key 有了,就会返回以前那个对象
 * 如果这个 key 不存在,那么就会返回 null
 */
V put(K key, V value);
1
2
3
4
@ExtendWith({SpringExtension.class})
@ContextConfiguration
public @interface SpringJUnitConfig {
}

SpringJUnitConfig = @ExtendWith + @Contextconfiguration

1
2
3
4
5
6
7
8
9
10
11
@ExtendWith(SpringExtension.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
class OrderRepositoryTests { }

@ExtendWith(SpringExtension.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
class UserRepositoryTests { }

Junit 版本变了,

jupiter 的 API 已经不兼容我以前的 @RunWith(SpringRunner.class) 了,

Spring 4

1
2
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = UserServiceInMemoryImpl.class)

Spring 5

1
2
@ExtendWith({SpringExtension.class})
@ContextConfiguration(classes = UserServiceInMemoryImpl.class)

或者用 @SpringJUnitConfig

叫做 派生性

Spring MVC

​ 我配置了一个 Bean 然后,看它的预期是否和我们的一样。

​ 关于 Spring 测试的方式, @Transactional 还支持事务,@ManagedBean

3.4.2. Standard Annotation Support

The following annotations are supported with standard semantics for all configurations of the Spring TestContext Framework. Note that these annotations are not specific to tests and can be used anywhere in the Spring Framework.

  • @Autowired
  • @Qualifier
  • @Resource (javax.annotation) if JSR-250 is present
  • @ManagedBean (javax.annotation) if JSR-250 is present
  • @Inject (javax.inject) if JSR-330 is present
  • @Named (javax.inject) if JSR-330 is present
  • @PersistenceContext (javax.persistence) if JPA is present
  • @PersistenceUnit (javax.persistence) if JPA is present
  • @Required
  • @Transactional

​ 支持的注解,DBUnit ,检查 SQL 是否是标准的, SQL 99 标准, SQL 93 标准。Mock 测试快一点,数据库更放心一点。如果你要测试事务的时候,

Maven 版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<build>
    <plugins>
        <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.22.0</version>
        </plugin>
    </plugins>
</build>
...
<dependencies>
    ...
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.3.2</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-engine</artifactId>
        <version>5.3.2</version>
        <scope>test</scope>
    </dependency>
    ...
</dependencies>
...