简体   繁体   中英

Spring integration jpa repository tests don't work as it should

I have something really strange. I want to test my repository level and I need to test if I can save 2 users with the same username(username field is unique in the database). And there is my db configuration.

spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url= jdbc:postgresql://localhost:5432/tutorial_test
spring.datasource.username=postgres
spring.datasource.password=root

# General JPA properties
spring.jpa.show-sql=false

#Note: The last two properties on the code snippet above were added to suppress an annoying exception
# that occurs when JPA (Hibernate) tries to verify PostgreSQL CLOB feature.
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL9Dialect
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults = false

# Hibernate Specific properties
spring.jpa.properties.hibernate.format_sql=false
spring.jpa.hibernate.ddl-auto=create

My entity class User:

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "users")
public class User {

    @Id
    @SequenceGenerator(name = "user_id_seq_gen", sequenceName = "user_id_seq", allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_id_seq_gen")
    private Long id;
    @Column(nullable = false, unique = true, length = 50)
    @NotNull
    @Length(min = 4, max = 50)
    private String username;
    @Column(nullable = false, length = 50)
    @NotNull
    @Length(min = 6, max = 50)
    private String password;
    @Column(nullable = false, unique = true, length = 100)
    @NotNull
    @Length(min = 6, max = 50)
    private String email;
}

UserRepository:

public interface UserRepository extends JpaRepository<User, Long> {

    boolean existsByUsername(String username);
    boolean existsByEmail(String email);
    User findByUsername(String username);
}

And finally my test class:

@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@TestPropertySource("/application-test.properties")
public class UserRepositoryTest {

    @Autowired
    private UserRepository userRepository;

    @Before
    public void insertData() {
        user = new User(null, "Daimon", "123123", "krikkk998@mail.ru");
        userRepository.save(user);
    }

    @After
    public void cleanData(){
        user = null;
        userRepository.deleteAll();
    }

    @Test(expected = DataIntegrityViolationException.class)
    public void registerUserWithExistingUsername() {
        val user = new User(null, "Daimon", "123123", "glass999@mail.ru");
        userRepository.save(user);
    }

}

This test classes behavior is very strange. It throws an exception that it can't save another entity because the username is unique only after userRepository.deleteAll(); Why?? Ok, if I delete @After method, it doesn't throw the exception at all...And again, if I add System.out.println(userRepository.findAll()); after saving second user, it thowrs exception... What is going on here?? These all method works well when I start an application. But in these integration repositories tests something wrong. I have another repository tests class where I can save parent object with a set of UNSAVED childs, and when I select parent object from the database, it gives me set of childs with NULL ids... I don't know what is the problem, but I think maybe in configuration?? Because I repeat, there is no problem when I start application and all works very well. I would be glad if you help me.

What you see is a side effect of how JPA gets implemented. When you persist or merge an entity most people think of it as saving the entity. And that's why the Repository method which calls these methods is called save .

But there is no insert to the database happening at that stage (1). Instead the entity is just tracked and it will get flushed on certain events:

  1. A commit of the transaction.
  2. An explicit call of flush.
  3. Depending on your configuration, possibly before a query (which seems to be the case here). That is why you get the exception on calling findAll . The implementation of deleteAll actually involves a findAll so it triggers the exception as well.

(1) actually, an insert might happen depending on your ID generation strategy.

@Before runs before your test is run. Here it inserts a User with the Username 'Daimon'

@Test is your test. It tries to insert a user with the username 'Daimon' which already exists (because it was inserted in @Before . This is why the exception is thrown.

@After runs after your test. It deletes all users.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM