Issue#30 Add additional Test for missing field and exact copy of how the CodeKataLoginRemoteSourceTest should look

This commit is contained in:
Gergely Hegedus 2022-01-24 20:30:08 +02:00
parent 49d1fb822f
commit 7544c6b784
7 changed files with 206 additions and 12 deletions

View file

@ -45,6 +45,11 @@ class CodeKataLoginRemoteSourceTest {
fun invalidJsonMeansParsingException() {
}
@DisplayName("GIVEN json response with missing field WHEN request is fired THEN network exception is thrown")
@Test
fun missingFieldJsonMeansParsingException() {
}
@DisplayName("GIVEN malformed json response WHEN request is fired THEN network exception is thrown")
@Test
fun malformedJsonMeansParsingException() {
@ -66,5 +71,13 @@ class CodeKataLoginRemoteSourceTest {
} while (true)
}
}
internal fun getLoginBodyJson(username: String, password: String): String =
"""
{
"username": "$username",
"password": "$password"
}
""".trimIndent()
}
}

View file

@ -1,6 +1,8 @@
package org.fnives.test.showcase.network.auth
import com.squareup.moshi.JsonDataException
import kotlinx.coroutines.runBlocking
import okio.EOFException
import org.fnives.test.showcase.model.auth.LoginCredentials
import org.fnives.test.showcase.model.network.BaseUrl
import org.fnives.test.showcase.network.auth.model.LoginStatusResponses
@ -25,6 +27,7 @@ import org.koin.test.inject
import org.mockito.kotlin.mock
import org.skyscreamer.jsonassert.JSONAssert
import org.skyscreamer.jsonassert.JSONCompareMode
import retrofit2.HttpException
@Suppress("TestFunctionName")
class LoginRemoteSourceTest : KoinTest {
@ -103,9 +106,12 @@ class LoginRemoteSourceTest : KoinTest {
fun genericErrorMeansNetworkError() {
mockServerScenarioSetup.setScenario(AuthScenario.GenericError(username = "a", password = "b"), validateArguments = false)
Assertions.assertThrows(NetworkException::class.java) {
val actual = Assertions.assertThrows(NetworkException::class.java) {
runBlocking { sut.login(LoginCredentials(username = "a", password = "b")) }
}
Assertions.assertEquals("HTTP 500 Server Error", actual.message)
Assertions.assertTrue(actual.cause is HttpException)
}
@DisplayName("GIVEN invalid json response WHEN request is fired THEN network exception is thrown")
@ -114,9 +120,26 @@ class LoginRemoteSourceTest : KoinTest {
val response = AuthScenario.UnexpectedJsonAsSuccessResponse(username = "a", password = "b")
mockServerScenarioSetup.setScenario(response, validateArguments = false)
Assertions.assertThrows(ParsingException::class.java) {
val actual = Assertions.assertThrows(ParsingException::class.java) {
runBlocking { sut.login(LoginCredentials(username = "a", password = "b")) }
}
Assertions.assertEquals("Expected BEGIN_OBJECT but was BEGIN_ARRAY at path \$", actual.message)
Assertions.assertTrue(actual.cause is JsonDataException)
}
@DisplayName("GIVEN json response with missing field WHEN request is fired THEN network exception is thrown")
@Test
fun missingFieldJsonMeansParsingException() {
val response = AuthScenario.MissingFieldJson(username = "a", password = "b")
mockServerScenarioSetup.setScenario(response, validateArguments = false)
val actual = Assertions.assertThrows(ParsingException::class.java) {
runBlocking { sut.login(LoginCredentials(username = "a", password = "b")) }
}
Assertions.assertEquals("Required value 'accessToken' missing at \$", actual.message)
Assertions.assertTrue(actual.cause is JsonDataException)
}
@DisplayName("GIVEN malformed json response WHEN request is fired THEN network exception is thrown")
@ -125,8 +148,11 @@ class LoginRemoteSourceTest : KoinTest {
val response = AuthScenario.MalformedJsonAsSuccessResponse(username = "a", "b")
mockServerScenarioSetup.setScenario(response, validateArguments = false)
Assertions.assertThrows(ParsingException::class.java) {
val actual = Assertions.assertThrows(ParsingException::class.java) {
runBlocking { sut.login(LoginCredentials(username = "a", "b")) }
}
Assertions.assertEquals("End of input", actual.message)
Assertions.assertTrue(actual.cause is EOFException)
}
}

View file

@ -0,0 +1,155 @@
package org.fnives.test.showcase.network.auth
import com.squareup.moshi.JsonDataException
import kotlinx.coroutines.runBlocking
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import okio.EOFException
import org.fnives.test.showcase.model.auth.LoginCredentials
import org.fnives.test.showcase.model.network.BaseUrl
import org.fnives.test.showcase.model.session.Session
import org.fnives.test.showcase.network.auth.CodeKataLoginRemoteSourceTest.Companion.getLoginBodyJson
import org.fnives.test.showcase.network.auth.CodeKataLoginRemoteSourceTest.Companion.readResourceFile
import org.fnives.test.showcase.network.auth.model.LoginStatusResponses
import org.fnives.test.showcase.network.di.createNetworkModules
import org.fnives.test.showcase.network.shared.exceptions.NetworkException
import org.fnives.test.showcase.network.shared.exceptions.ParsingException
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.koin.core.context.GlobalContext.stopKoin
import org.koin.core.context.startKoin
import org.koin.test.KoinTest
import org.koin.test.inject
import org.mockito.kotlin.mock
import org.skyscreamer.jsonassert.JSONAssert
import org.skyscreamer.jsonassert.JSONCompareMode
import retrofit2.HttpException
class PlainLoginRemoteSourceTest : KoinTest {
private val sut by inject<LoginRemoteSource>()
private lateinit var mockWebServer: MockWebServer
@BeforeEach
fun setUp() {
mockWebServer = MockWebServer()
mockWebServer.start()
startKoin {
modules(
createNetworkModules(
baseUrl = BaseUrl(mockWebServer.url("mockserver/").toString()),
enableLogging = true,
networkSessionExpirationListenerProvider = { mock() },
networkSessionLocalStorageProvider = { mock() }
).toList()
)
}
}
@AfterEach
fun tearDown() {
stopKoin()
mockWebServer.shutdown()
}
@DisplayName("GIVEN successful response WHEN request is fired THEN login status success is returned")
@Test
fun successResponseIsParsedProperly() = runBlocking {
mockWebServer.enqueue(MockResponse().setResponseCode(200).setBody(readResourceFile("success_response_login.json")))
val session = Session(accessToken = "login-access", refreshToken = "login-refresh")
val expected = LoginStatusResponses.Success(session = session)
val actual = sut.login(LoginCredentials(username = "alma", password = "banan"))
Assertions.assertEquals(expected, actual)
}
@DisplayName("GIVEN successful response WHEN request is fired THEN the request is setup properly")
@Test
fun requestProperlySetup() = runBlocking {
mockWebServer.enqueue(MockResponse().setResponseCode(200).setBody(readResourceFile("success_response_login.json")))
sut.login(LoginCredentials(username = "alma", password = "banan"))
val request = mockWebServer.takeRequest()
Assertions.assertEquals("POST", request.method)
Assertions.assertEquals("Android", request.getHeader("Platform"))
Assertions.assertEquals(null, request.getHeader("Authorization"))
Assertions.assertEquals("/mockserver/login", request.path)
val loginRequestBody = getLoginBodyJson(username = "alma", password = "banan")
JSONAssert.assertEquals(
loginRequestBody,
request.body.readUtf8(),
JSONCompareMode.NON_EXTENSIBLE
)
}
@DisplayName("GIVEN bad request response WHEN request is fired THEN login status invalid credentials is returned")
@Test
fun badRequestMeansInvalidCredentials() = runBlocking {
mockWebServer.enqueue(MockResponse().setResponseCode(400).setBody("{}"))
val expected = LoginStatusResponses.InvalidCredentials
val actual = sut.login(LoginCredentials(username = "a", password = "b"))
Assertions.assertEquals(expected, actual)
}
@DisplayName("GIVEN internal error response WHEN request is fired THEN network exception is thrown")
@Test
fun genericErrorMeansNetworkError() {
mockWebServer.enqueue(MockResponse().setResponseCode(500).setBody("{}"))
val actual = Assertions.assertThrows(NetworkException::class.java) {
runBlocking { sut.login(LoginCredentials(username = "a", password = "b")) }
}
Assertions.assertEquals("HTTP 500 Server Error", actual.message)
Assertions.assertTrue(actual.cause is HttpException)
}
@DisplayName("GIVEN invalid json response WHEN request is fired THEN network exception is thrown")
@Test
fun invalidJsonMeansParsingException() {
mockWebServer.enqueue(MockResponse().setResponseCode(200).setBody("[]"))
val actual = Assertions.assertThrows(ParsingException::class.java) {
runBlocking { sut.login(LoginCredentials(username = "a", password = "b")) }
}
Assertions.assertEquals("Expected BEGIN_OBJECT but was BEGIN_ARRAY at path \$", actual.message)
Assertions.assertTrue(actual.cause is JsonDataException)
}
@DisplayName("GIVEN json response with missing field WHEN request is fired THEN network exception is thrown")
@Test
fun missingFieldJsonMeansParsingException() {
mockWebServer.enqueue(MockResponse().setResponseCode(200).setBody("{}"))
val actual = Assertions.assertThrows(ParsingException::class.java) {
runBlocking { sut.login(LoginCredentials(username = "a", password = "b")) }
}
Assertions.assertEquals("Required value 'accessToken' missing at \$", actual.message)
Assertions.assertTrue(actual.cause is JsonDataException)
}
@DisplayName("GIVEN malformed json response WHEN request is fired THEN network exception is thrown")
@Test
fun malformedJsonMeansParsingException() {
mockWebServer.enqueue(MockResponse().setResponseCode(200).setBody("{"))
val actual = Assertions.assertThrows(ParsingException::class.java) {
runBlocking { sut.login(LoginCredentials(username = "a", password = "b")) }
}
Assertions.assertEquals("End of input", actual.message)
Assertions.assertTrue(actual.cause is EOFException)
}
}