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!