Hej,
Chcę nauczyć się testów i nie chce iść dalej z nauką póki tego jakkolwiek nie opanuje.
Czytam, czytam, próbuje i chciałbym zasięgnąć porady jak testować, poprosić o sprawdzone publikacje na ten temat bo mam trochę mętlik w głowie i wydaje mi się, że to słabo rozumiem.
Testuję implementacje Repozytorium, implementacja ma dwie zależności, ApiService oraz AuthConfig.
Problem mam z podejściem do mocków, w wielu artykułach są odradzane ale w tym wypadku trochę nie widzę dlaczego miałbym tych dependencies nie mockować, przecież one mają swoje własne testy w innej klasie, dlaczego powinienem(?) podawać konkretne implementacje, szczególnie, że robi się drabina dependencies, których w ogóle Repozytorium nie powinno obchodzić.
Proszę o porade jak to poprawnie rozwiązać i jak podejść do testowania.
Implementacja
class AuthRepositoryImpl(
private val jwtAuthConfig: AuthConfig,
private val userApiService: UserApiService
) : AuthRepository {
override suspend fun registerUserWithToken(input: AuthRequest?): RootResponse<Any> {
return input?.let { authRequest ->
if (!authRequest.isValidRequest()) {
return RootResponse.ErrorResponse(
HttpStatusCode.BadRequest,
INCORRECT_REQUEST
)
}
return when (checkIfUserExists(authRequest.email!!)) {
true -> {
RootResponse.ErrorResponse(
HttpStatusCode.Conflict,
USER_EXISTS
)
}
false -> {
val userIdFromEmail = getHashFromString(authRequest.email).toId<User>()
val user = User(
userIdFromEmail,
authRequest.firstName,
authRequest.lastName,
authRequest.email,
authRequest.password
)
val isAcknowledged = userApiService.insertUser(user)
when {
isAcknowledged -> {
val token = jwtAuthConfig.generateToken(user.id.toString())
RootResponse.SuccessResponse(
HttpStatusCode.Created,
token,
USER_CREATED
)
}
else ->
RootResponse.ErrorResponse(
HttpStatusCode.BadRequest,
INCORRECT_REQUEST
)
}
}
}
} ?: RootResponse.ErrorResponse(
HttpStatusCode.BadRequest,
INCORRECT_REQUEST
)
}
override suspend fun loginUserWithToken(input: LoginRequest?): RootResponse<Any> {
input?.let { loginRequest ->
if (!loginRequest.isValidRequest()) {
return RootResponse.ErrorResponse(
HttpStatusCode.BadRequest,
INCORRECT_REQUEST
)
}
val user = userApiService.findUserByEmail(loginRequest.email!!)
?: return RootResponse.ErrorResponse(
HttpStatusCode.Unauthorized,
WRONG_CREDENTIALS
)
val isPasswordMatch = isPasswordHashMatch(loginRequest.password!!, user.password!!)
return when {
isPasswordMatch -> {
val token = jwtAuthConfig.generateToken(user.id.toString())
RootResponse.SuccessResponse(
HttpStatusCode.OK,
token,
USER_LOGGED_IN
)
}
else -> {
RootResponse.ErrorResponse(
HttpStatusCode.Unauthorized,
WRONG_CREDENTIALS
)
}
}
} ?: return RootResponse.ErrorResponse(
HttpStatusCode.BadRequest,
INCORRECT_REQUEST
)
}
private suspend fun checkIfUserExists(email: String): Boolean {
return userApiService.findUserByEmail(email) != null
}
}
Test
class AuthRepositoryTest {
private val mockAuthConfig = mockk<AuthConfig>()
private val mockUserApiService = mockk<UserApiService>()
private val mockPasswordHash = mockkStatic(::isPasswordHashMatch)
private lateinit var authRepository: AuthRepository
@Before
fun setUp() {
authRepository = AuthRepositoryImpl(mockAuthConfig, mockUserApiService)
}
@Test
fun `given valid register data returns 201`() {
val validAuthRequest = fakeValidAuthRequest()
val validUser = fakeValidUser()
coEvery { mockUserApiService.findUserByEmail(validAuthRequest.email!!) } returns null
coEvery { mockUserApiService.insertUser(validUser) } returns true
coEvery { mockAuthConfig.generateToken(validUser.id.toString()) } returns fakeValidToken()
runBlocking {
val response = authRepository.registerUserWithToken(validAuthRequest)
assertThat(response.statusCode).isEqualTo(HttpStatusCode.Created)
}
}
@Test
fun `given null register data returns 400`() {
val nullData = null
runBlocking {
val response = authRepository.registerUserWithToken(nullData)
assertThat(response.statusCode).isEqualTo(HttpStatusCode.BadRequest)
}
}
@Test
fun `given existing user returns 409`() {
val validAuthRequest = fakeValidAuthRequest()
coEvery { mockUserApiService.findUserByEmail(validAuthRequest.email!!) } returns fakeValidUser()
runBlocking {
val response = authRepository.registerUserWithToken(validAuthRequest)
assertThat(response.statusCode).isEqualTo(HttpStatusCode.Conflict)
}
}
@Test
fun `not acknowledged insert returns 400`() {
val validAuthRequest = fakeValidAuthRequest()
val validUser = fakeValidUser()
coEvery { mockUserApiService.findUserByEmail(validAuthRequest.email!!) } returns null
coEvery { mockUserApiService.insertUser(validUser) } returns false
runBlocking {
val response = authRepository.registerUserWithToken(validAuthRequest)
assertThat(response.statusCode).isEqualTo(HttpStatusCode.BadRequest)
}
}
@Test
fun `given valid login data returns OK`() {
val validLoginRequest = fakeValidLoginRequest()
val validUser = fakeValidUser()
coEvery { mockUserApiService.findUserByEmail(validLoginRequest.email!!) } returns fakeValidUser()
coEvery { isPasswordHashMatch(validLoginRequest.password!!, validLoginRequest.password!!) } returns true
coEvery { mockAuthConfig.generateToken(validUser.id.toString()) } returns fakeValidToken()
runBlocking {
val response = authRepository.loginUserWithToken(validLoginRequest)
assertThat(response.statusCode).isEqualTo(HttpStatusCode.OK)
}
}
@Test
fun `given null login data returns 400`() {
val nullData = null
runBlocking {
val response = authRepository.loginUserWithToken(nullData)
assertThat(response.statusCode).isEqualTo(HttpStatusCode.BadRequest)
}
}
@Test
fun `given invalid login request returns 400`() {
val invalidRequest = fakeInvalidLoginRequest()
runBlocking {
val response = authRepository.loginUserWithToken(invalidRequest)
assertThat(response.statusCode).isEqualTo(HttpStatusCode.BadRequest)
}
}
@Test
fun `given invalid login email returns 401`() {
val invalidEmailRequest = fakeInvalidEmailLoginRequest()
coEvery { mockUserApiService.findUserByEmail(invalidEmailRequest.email!!) } returns null
runBlocking {
val response = authRepository.loginUserWithToken(invalidEmailRequest)
assertThat(response.statusCode).isEqualTo(HttpStatusCode.Unauthorized)
}
}
@Test
fun `given invalid login password returns 401`() {
val invalidPasswordRequest = fakeInvalidPasswordLoginRequest()
coEvery {
isPasswordHashMatch(
invalidPasswordRequest.password!!,
invalidPasswordRequest.password!!
)
} returns false
coEvery { mockUserApiService.findUserByEmail(invalidPasswordRequest.email!!) } returns fakeValidUser()
runBlocking {
val response = authRepository.loginUserWithToken(invalidPasswordRequest)
assertThat(response.statusCode).isEqualTo(HttpStatusCode.Unauthorized)
}
}
}