Spring Boot 5 多个 JUnit JPA 测试文件接线

标签 spring spring-boot junit spring-data-jpa

我的项目中有两个测试文件。 一种是直接测试我的持久层:

@RunWith(SpringRunner.class)
@DataJpaTest
public class UserTests {

    @Autowired
    private TestEntityManager entityManager;

    @Autowired
    private UserRepository repository;

    @Test
    public void whenFindByEmail_thenReturnUser() {
        // given
        User user = new User("<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="b5d0d8d4dcd9f5d0d8d4dcd99bd6dad8" rel="noreferrer noopener nofollow">[email protected]</a>", "12345678", "Some Name");
        entityManager.persist(user);
        entityManager.flush();

        // when
        User found = repository.findByEmail(user.getEmail());

        // then
        assertThat(found.getName()).isEqualTo(user.getName());
        assertThat(found.getEmail()).isEqualTo(user.getEmail());
        assertThat(found.getPasswordHash()).isEqualTo(user.getPasswordHash());
    }
}

另一个是使用持久层测试服务:

@RunWith(SpringRunner.class)
@DataJpaTest
public class UserServiceTests {

    @Autowired
    private UserService service;

    @Test
    public void testSuccessfullUserCreation() {
        UserCreationResult res = service.createUser("<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="63020d1a170b0a0d0423020d1a170b0a0d044d000c0e" rel="noreferrer noopener nofollow">[email protected]</a>", "1234567890", "Test");
        assertThat(res).isEqualTo(UserCreationResult.OK);
    }

    @Test
    public void testWrongEmailUserCreation() {
        UserCreationResult res = service.createUser("anything@anything", "1234567890", "Test");
        assertThat(res).isEqualTo(UserCreationResult.INVALID_EMAIL);
    }

    @Test
    public void testTooShortPasswordUserCreation() {
        String shortPassword =
                String.join("", Collections.nCopies(UserService.minPasswordLength - 1, "0"));
        UserCreationResult res = service.createUser("<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="3f5e51464b575651587f5e51464b57565158115c5052" rel="noreferrer noopener nofollow">[email protected]</a>", shortPassword, "Test");
        assertThat(res).isEqualTo(UserCreationResult.WRONG_PASSWORD_LENGTH);
    }

    @Test
    public void testTooLongPasswordUserCreation() {
        String longPassword =
                String.join("", Collections.nCopies(UserService.maxPasswordLength + 1, "0"));
        UserCreationResult res = service.createUser("<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="25444b5c514d4c4b4265444b5c514d4c4b420b464a48" rel="noreferrer noopener nofollow">[email protected]</a>", longPassword, "Test");
        assertThat(res).isEqualTo(UserCreationResult.WRONG_PASSWORD_LENGTH);
    }

    @Test
    public void testMaxLengthPasswordUserCreation() {
        String maxPassword =
                String.join("", Collections.nCopies(UserService.maxPasswordLength, "0"));
        UserCreationResult res = service.createUser("<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="3d5c5344495554535a7d5c5344495554535a135e5250" rel="noreferrer noopener nofollow">[email protected]</a>", maxPassword, "Test");
        assertThat(res).isEqualTo(UserCreationResult.OK);
    }

    @Test
    public void testMinLengthPasswordUserCreation() {
        String minPassword =
                String.join("", Collections.nCopies(UserService.minPasswordLength, "0"));
        UserCreationResult res = service.createUser("<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="65040b1c110d0c0b0225040b1c110d0c0b024b060a08" rel="noreferrer noopener nofollow">[email protected]</a>", minPassword, "Test");
        assertThat(res).isEqualTo(UserCreationResult.OK);
    }

    @Test
    public void testReservedEmailUserCreation() {
        String email = "<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="6a0f070b03062a0f070b030644090507" rel="noreferrer noopener nofollow">[email protected]</a>";
        UserCreationResult res = service.createUser(email, "1234567890", "Test");
        assertThat(res).isEqualTo(UserCreationResult.OK);
        res = service.createUser(email, "1234567890", "Test");
        assertThat(res).isEqualTo(UserCreationResult.RESERVED_EMAIL);
    }
}

首先,我的服务 Autowiring 不起作用(UnsatisfiedDependencyException),所以我必须添加: 测试类的 @ComponentScan("my.service.package") 注释。

这使得 UserServiceTests 的测试在独立运行时可以工作(使用 eclipse 仅运行此类)。

但是当运行我的应用程序的所有测试时(在 Eclipse 中或使用旧的 mvn clean test),我在同一个测试类上遇到了相同的错误。

我尝试将相同的组件扫描注释添加到其他测试类 (UserTests),然后一切正常。

我从 UserServiceTests 中删除了组件扫描注释,它仍然有效。

我显然推断出测试执行的顺序很重要。

这是我的 3 个问题,真正的问题是最后一个:

  1. 首先,即使我的类已正确注释 @Service (因此应该被检测为 bean),为什么我必须添加此组件扫描注释?
  2. 测试顺序有何影响?
  3. 如何拥有多个可以通过正确的依赖项注入(inject)独立运行的 JPA 测试文件?

这是我的服务类别:

@Service
public class UserService {

    @Autowired
    private UserRepository repository;

    public static final int minPasswordLength = 8;
    public static final int maxPasswordLength = 50;

    public static enum UserCreationResult {
        OK, INVALID_EMAIL, RESERVED_EMAIL, WRONG_PASSWORD_LENGTH, UNKNOWN_ERROR
    }

    @Transactional
    public UserCreationResult createUser(String email, String password, String name) {
        if (password.length() < minPasswordLength || password.length() > maxPasswordLength) {
            return UserCreationResult.WRONG_PASSWORD_LENGTH;
        }
        if (!EmailValidator.getInstance().isValid(email)) {
            return UserCreationResult.INVALID_EMAIL;
        }
        final User existingUser = repository.findByEmail(email);
        if (existingUser != null) {
            return UserCreationResult.RESERVED_EMAIL;
        }
        final User user = repository.save(new User(email, password, name));
        return user == null ? UserCreationResult.UNKNOWN_ERROR : UserCreationResult.OK;
    }
}

这是我的 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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.somedomain</groupId>
    <artifactId>ws-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>My App</name>
    <description>My app's description</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>commons-validator</groupId>
            <artifactId>commons-validator</artifactId>
            <version>1.6</version>
        </dependency>
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

最佳答案

多亏了 question and answer,我才能够通过正确的配置进行测试。

在我的 UserServiceTests 中,服务基本上没有 Autowiring ,因为这是 @DataJpaTest 的预期行为:它不扫描常规 bean。

所以我在这个类中使用了@SpringBootTest,并删除了两个测试类中的组件扫描。

之后,我的一些服务测试失败了,因为使用 @SpringBootTest 时,每次测试后数据库都不会重置。

我在每次服务测试后添加了一个简单的存储库清理,一切正常。

这个问题仍然存在:

How is it that the order of tests matters ?

这是我的新测试类(class):

服务测试:

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTests {

    @Autowired
    private UserService service;

    @Autowired
    private UserRepository repository;

    @After
    public void cleanUsers() {
        repository.deleteAll();
    }

    @Test
    public void testSuccessfullUserCreation() {
        UserCreationResult res = service.createUser("<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="aacbc4d3dec2c3c4cdeacbc4d3dec2c3c4cd84c9c5c7" rel="noreferrer noopener nofollow">[email protected]</a>", "1234567890", "Test");
        assertThat(res).isEqualTo(UserCreationResult.OK);
    }

    @Test
    public void testWrongEmailUserCreation() {
        UserCreationResult res = service.createUser("anything@anything", "1234567890", "Test");
        assertThat(res).isEqualTo(UserCreationResult.INVALID_EMAIL);
    }

    @Test
    public void testTooShortPasswordUserCreation() {
        String shortPassword =
                String.join("", Collections.nCopies(UserService.minPasswordLength - 1, "0"));
        UserCreationResult res = service.createUser("<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="99f8f7e0edf1f0f7fed9f8f7e0edf1f0f7feb7faf6f4" rel="noreferrer noopener nofollow">[email protected]</a>", shortPassword, "Test");
        assertThat(res).isEqualTo(UserCreationResult.WRONG_PASSWORD_LENGTH);
    }

    @Test
    public void testTooLongPasswordUserCreation() {
        String longPassword =
                String.join("", Collections.nCopies(UserService.maxPasswordLength + 1, "0"));
        UserCreationResult res = service.createUser("<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="e5848b9c918d8c8b82a5848b9c918d8c8b82cb868a88" rel="noreferrer noopener nofollow">[email protected]</a>", longPassword, "Test");
        assertThat(res).isEqualTo(UserCreationResult.WRONG_PASSWORD_LENGTH);
    }

    @Test
    public void testMaxLengthPasswordUserCreation() {
        String maxPassword =
                String.join("", Collections.nCopies(UserService.maxPasswordLength, "0"));
        UserCreationResult res = service.createUser("<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="2e4f40575a464740496e4f40575a46474049004d4143" rel="noreferrer noopener nofollow">[email protected]</a>", maxPassword, "Test");
        assertThat(res).isEqualTo(UserCreationResult.OK);
    }

    @Test
    public void testMinLengthPasswordUserCreation() {
        String minPassword =
                String.join("", Collections.nCopies(UserService.minPasswordLength, "0"));
        UserCreationResult res = service.createUser("<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="bfded1c6cbd7d6d1d8ffded1c6cbd7d6d1d891dcd0d2" rel="noreferrer noopener nofollow">[email protected]</a>", minPassword, "Test");
        assertThat(res).isEqualTo(UserCreationResult.OK);
    }

    @Test
    public void testReservedEmailUserCreation() {
        String email = "<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="5e3b333f37321e3b333f3732703d3133" rel="noreferrer noopener nofollow">[email protected]</a>";
        UserCreationResult res = service.createUser(email, "1234567890", "Test");
        assertThat(res).isEqualTo(UserCreationResult.OK);
        res = service.createUser(email, "1234567890", "Test");
        assertThat(res).isEqualTo(UserCreationResult.RESERVED_EMAIL);
    }
}

JPA 测试:

@RunWith(SpringRunner.class)
@DataJpaTest
public class UserTests {

    @Autowired
    private TestEntityManager entityManager;

    @Autowired
    private UserRepository repository;

    @Test
    public void whenFindByEmail_thenReturnUser() {
        // given
        User user = new User("<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="314442544371444254431f525e5c" rel="noreferrer noopener nofollow">[email protected]</a>", "12345678", "Some Name");
        entityManager.persist(user);
        entityManager.flush();

        // when
        User found = repository.findByEmail(user.getEmail());

        // then
        assertThat(found.getName()).isEqualTo(user.getName());
        assertThat(found.getEmail()).isEqualTo(user.getEmail());
        assertThat(found.getPasswordHash()).isEqualTo(user.getPasswordHash());
    }
}

关于Spring Boot 5 多个 JUnit JPA 测试文件接线,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48362072/

相关文章:

java - Gradle使用不同的属性文件构建多个.war文件

java - 在多节点环境中仅触发一次调度作业

android - 如何运行协程作为单元测试的阻塞?

java - 如何在略有不同的 jar 列表上运行测试用例?

java - JUnit 测试因网络连接错误而结束

java - 使用 WireMock 和 Eureka 的 Spring Boot 集成测试失败,返回 "No instances available"

java - 如何在Spring中创建一个创建单例的工厂

java - 在 hibernate5.1 和 spring 4.1.6 集成中找不到 Hibernatetemplate 的 opensession() 异常

java - Hibernate Annotations(Spring),添加新 boolean 列时设置默认值,出错?

java.lang.IllegalStateException : Required identifier property not found