Mockito test does not trigger unique constraint violation in H2

35 Views Asked by At

I'm trying to write tests to make sure that my service layer is working as expected. However, when I try to violate a unique constraint and expect to see a DataIntegrityViolationException, it seems that this is ignored instead.

Here's my schema at the moment:

CREATE TABLE IF NOT EXISTS table_users(
    uuid UUID PRIMARY KEY UNIQUE,
    username VARCHAR UNIQUE,
    password VARCHAR NOT NULL,
    first_name VARCHAR NOT NULL,
    last_name VARCHAR NOT NULL,
    is_deleted BOOLEAN NOT NULL DEFAULT FALSE
);

As you can see, my username is unique.

@Entity(name = "table_users")
data class User(
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    @Column(name = "uuid")
    val uuid: UUID? = null,

    @Column(name = "username", unique = true)
    @NotNull(message = "Please provide a username") @NotBlank
    val username: String? = null,

    @Column(name = "password")
    @NotNull @NotBlank
    var password: String? = null,

    @Column(name = "first_name")
    @NotNull(message = "Please provide a first name.") @NotBlank
    var firstName: String? = null,

    @Column(name = "last_name")
    @NotNull @NotBlank
    var lastName: String? = null,

    @Column(name = "is_deleted")
    @NotNull
    var isDeleted: Boolean = false
) {
    // Omitted builder class for brevity
}

Here's my saveUser method so far:

override fun saveUser(user: User): DatabaseResult<User> {
        return try {
            if(user.firstName.isNullOrBlank()) {
                return DatabaseResult(
                    code = ResultCode.CREATION_FAILURE,
                    errorMessage = UserService.ErrorMessages.FIRST_NAME_NULL_OR_BLANK
                )
            }

            if(user.lastName.isNullOrBlank()) {
                return DatabaseResult(
                    code = ResultCode.CREATION_FAILURE,
                    errorMessage = UserService.ErrorMessages.LAST_NAME_NULL_OR_BLANK
                )
            }

            if(user.username.isNullOrBlank()) {
                return DatabaseResult(
                    code = ResultCode.CREATION_FAILURE,
                    errorMessage = UserService.ErrorMessages.USERNAME_NULL_OR_BLANK
                )
            }

            val savedUser = userRepository.save(user)

            DatabaseResult(
                code = ResultCode.CREATION_SUCCESS,
                entity = savedUser
            )
        } catch(exception: DataAccessException) {
            DatabaseResult(
                code = ResultCode.CREATION_FAILURE,
                errorMessage = exception.message.toString()
            )
        } catch(exception: DataIntegrityViolationException) {
            DatabaseResult(
                code = ResultCode.CREATION_FAILURE,
                errorMessage = exception.message.toString()
            )
        } catch(exception: ConcurrencyFailureException) {
            DatabaseResult(
                code = ResultCode.CREATION_FAILURE,
                errorMessage = exception.message.toString()
            )
        } catch(exception: OptimisticLockingFailureException) {
            DatabaseResult(
                code = ResultCode.CREATION_FAILURE,
                errorMessage = exception.message.toString()
            )
        } catch(exception: Exception) {
            DatabaseResult(
                code = ResultCode.CREATION_FAILURE,
                errorMessage = exception.message.toString()
            )
        }
    }

As you can see, I'm expecting a few different exceptions to be raised by the database and my repository code.

    @Test
    fun givenExistingUsername_whenSaved_thenReturnFailureResult() {
        // Given
        val existingUser = User.Builder()
            .firstName("John")
            .lastName("Doe")
            .username("existing_username")
            .password("password")
            .isDeleted(false)
            .build()

        userService.saveUser(existingUser)

        val userWithExistingUsername = User.Builder()
            .firstName("Jane")
            .lastName("Doe")
            .username("existing_username")  // Use the same existing username
            .password("password")
            .isDeleted(false)
            .build()

        // When
        val savedTest = userService.saveUser(userWithExistingUsername)

        // Then
        Mockito.verify(repository, Mockito.never()).save(userWithExistingUsername)  // Verify that the save method is never called again
        assertEquals(ResultCode.CREATION_FAILURE, savedTest.code)
        assertNull(savedTest.entity)
        assertNotNull(savedTest.errorMessage)
        // Adjust the error message verification according to your implementation
        assertEquals(UserService.ErrorMessages.USERNAME_NOT_UNIQUE, 
savedTest.errorMessage)
    }

As previously mentioned, when I try to run the above test code, regardless of what I do, it still seems to return a second object for the second user despite it having an identical username between the both of them.

It's as if the unique constraint is entirely ignored.

Also, please explain like I'm 5 as I'm quite new to testing code.

Thanks!

0

There are 0 best solutions below