mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 04:13:53 +01:00
Test/2819 add campaigns api tests (#6529)
Some checks are pending
Android CI / Run tests and generate APK (push) Waiting to run
Some checks are pending
Android CI / Run tests and generate APK (push) Waiting to run
* test:add mock JSON resource files for campaigns API responses * feat:make campaign model fields mutable to allow for correct deserialization * test:implement unit tests for fetching campaigns and fix DTO mocking logic * test:implement unit tests for fetching campaigns and fix DTO mocking logic --------- Co-authored-by: Nicolas Raoul <nicolas.raoul@gmail.com>
This commit is contained in:
parent
3a55583460
commit
def33552f9
5 changed files with 182 additions and 28 deletions
|
|
@ -7,8 +7,8 @@ import com.google.gson.annotations.SerializedName
|
||||||
*/
|
*/
|
||||||
class CampaignConfig {
|
class CampaignConfig {
|
||||||
@SerializedName("showOnlyLiveCampaigns")
|
@SerializedName("showOnlyLiveCampaigns")
|
||||||
private val showOnlyLiveCampaigns = false
|
var showOnlyLiveCampaigns = false
|
||||||
|
|
||||||
@SerializedName("sortBy")
|
@SerializedName("sortBy")
|
||||||
private val sortBy: String? = null
|
var sortBy: String? = null
|
||||||
}
|
}
|
||||||
|
|
@ -8,8 +8,8 @@ import fr.free.nrw.commons.campaigns.models.Campaign
|
||||||
*/
|
*/
|
||||||
class CampaignResponseDTO {
|
class CampaignResponseDTO {
|
||||||
@SerializedName("config")
|
@SerializedName("config")
|
||||||
val campaignConfig: CampaignConfig? = null
|
var campaignConfig: CampaignConfig? = null
|
||||||
|
|
||||||
@SerializedName("campaigns")
|
@SerializedName("campaigns")
|
||||||
val campaigns: List<Campaign>? = null
|
var campaigns: List<Campaign>? = null
|
||||||
}
|
}
|
||||||
|
|
@ -3,6 +3,10 @@ package fr.free.nrw.commons
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.nhaarman.mockitokotlin2.any
|
import com.nhaarman.mockitokotlin2.any
|
||||||
import com.nhaarman.mockitokotlin2.verify
|
import com.nhaarman.mockitokotlin2.verify
|
||||||
|
import com.nhaarman.mockitokotlin2.times
|
||||||
|
import fr.free.nrw.commons.campaigns.CampaignResponseDTO
|
||||||
|
import fr.free.nrw.commons.campaigns.CampaignConfig
|
||||||
|
import fr.free.nrw.commons.campaigns.models.Campaign
|
||||||
import fr.free.nrw.commons.explore.depictions.DepictsClient
|
import fr.free.nrw.commons.explore.depictions.DepictsClient
|
||||||
import fr.free.nrw.commons.location.LatLng
|
import fr.free.nrw.commons.location.LatLng
|
||||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient
|
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient
|
||||||
|
|
@ -10,13 +14,22 @@ import fr.free.nrw.commons.nearby.model.NearbyQueryParams
|
||||||
import okhttp3.Call
|
import okhttp3.Call
|
||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
|
import okhttp3.ResponseBody
|
||||||
|
import okhttp3.mockwebserver.MockResponse
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertFalse
|
||||||
|
import org.junit.Assert.assertNotNull
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
import org.mockito.Mockito
|
import org.mockito.Mockito
|
||||||
import org.mockito.Mockito.times
|
import org.mockito.Mockito.eq
|
||||||
import org.mockito.MockitoAnnotations
|
import org.mockito.MockitoAnnotations
|
||||||
|
import java.io.BufferedReader
|
||||||
|
import java.io.InputStreamReader
|
||||||
import java.lang.Exception
|
import java.lang.Exception
|
||||||
|
|
||||||
class OkHttpJsonApiClientTests {
|
class OkHttpJsonApiClientTests {
|
||||||
|
|
@ -45,34 +58,43 @@ class OkHttpJsonApiClientTests {
|
||||||
@Mock
|
@Mock
|
||||||
lateinit var response: Response
|
lateinit var response: Response
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
lateinit var responseBody: ResponseBody
|
||||||
|
|
||||||
|
private lateinit var mockWebServer: TestWebServer
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
MockitoAnnotations.openMocks(this)
|
MockitoAnnotations.openMocks(this)
|
||||||
okHttpJsonApiClient =
|
mockWebServer = TestWebServer()
|
||||||
OkHttpJsonApiClient(
|
mockWebServer.setUp()
|
||||||
okhttpClient,
|
okHttpJsonApiClient = OkHttpJsonApiClient(
|
||||||
depictsClient,
|
okhttpClient,
|
||||||
wikiMediaToolforgeUrl,
|
depictsClient,
|
||||||
sparqlQueryUrl,
|
wikiMediaToolforgeUrl,
|
||||||
campaignsUrl,
|
sparqlQueryUrl,
|
||||||
gson,
|
mockWebServer.getUrl(), //use the mock server for the campaignsUrl
|
||||||
)
|
gson
|
||||||
|
)
|
||||||
Mockito.`when`(okhttpClient.newCall(any())).thenReturn(call)
|
Mockito.`when`(okhttpClient.newCall(any())).thenReturn(call)
|
||||||
Mockito.`when`(call.execute()).thenReturn(response)
|
Mockito.`when`(call.execute()).thenReturn(response)
|
||||||
|
Mockito.`when`(response.isSuccessful).thenReturn(false)
|
||||||
|
Mockito.`when`(response.message).thenReturn("test")
|
||||||
|
Mockito.`when`(response.body).thenReturn(responseBody)
|
||||||
|
Mockito.`when`(responseBody.string()).thenReturn("{\"error\": \"test\"}")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testGetNearbyPlacesCustomQuery() {
|
fun testGetNearbyPlacesCustomQuery() {
|
||||||
Mockito.`when`(response.message).thenReturn("test")
|
|
||||||
try {
|
try {
|
||||||
okHttpJsonApiClient.getNearbyPlaces(latLng, "test", 10.0, "test")
|
okHttpJsonApiClient.getNearbyPlaces(latLng, "test", 10.0, "test")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
assert(e.message.equals("test"))
|
assertEquals("test", e.message)
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
okHttpJsonApiClient.getNearbyPlaces(NearbyQueryParams.Rectangular(latLng, latLng), "test", true, "test")
|
okHttpJsonApiClient.getNearbyPlaces(NearbyQueryParams.Rectangular(latLng, latLng), "test", true, "test")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
assert(e.message.equals("test"))
|
assertEquals("test", e.message)
|
||||||
}
|
}
|
||||||
verify(okhttpClient, times(2)).newCall(any())
|
verify(okhttpClient, times(2)).newCall(any())
|
||||||
verify(call, times(2)).execute()
|
verify(call, times(2)).execute()
|
||||||
|
|
@ -80,11 +102,10 @@ class OkHttpJsonApiClientTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testGetNearbyPlaces() {
|
fun testGetNearbyPlaces() {
|
||||||
Mockito.`when`(response.message).thenReturn("test")
|
|
||||||
try {
|
try {
|
||||||
okHttpJsonApiClient.getNearbyPlaces(latLng, "test", 10.0, null)
|
okHttpJsonApiClient.getNearbyPlaces(latLng, "test", 10.0, null)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
assert(e.message.equals("test"))
|
assertEquals("test", e.message)
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
okHttpJsonApiClient.getNearbyPlaces(
|
okHttpJsonApiClient.getNearbyPlaces(
|
||||||
|
|
@ -93,9 +114,8 @@ class OkHttpJsonApiClientTests {
|
||||||
true,
|
true,
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
assert(e.message.equals("test"))
|
assertEquals("test", e.message)
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
okHttpJsonApiClient.getNearbyPlaces(
|
okHttpJsonApiClient.getNearbyPlaces(
|
||||||
|
|
@ -105,7 +125,7 @@ class OkHttpJsonApiClientTests {
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
assert(e.message.equals("test"))
|
assertEquals("test", e.message)
|
||||||
}
|
}
|
||||||
verify(okhttpClient, times(3)).newCall(any())
|
verify(okhttpClient, times(3)).newCall(any())
|
||||||
verify(call, times(3)).execute()
|
verify(call, times(3)).execute()
|
||||||
|
|
@ -113,18 +133,121 @@ class OkHttpJsonApiClientTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testGetNearbyItemCount() {
|
fun testGetNearbyItemCount() {
|
||||||
Mockito.`when`(response.message).thenReturn("test")
|
|
||||||
try {
|
try {
|
||||||
okHttpJsonApiClient.getNearbyItemCount(NearbyQueryParams.Radial(latLng, 10f))
|
okHttpJsonApiClient.getNearbyItemCount(NearbyQueryParams.Radial(latLng, 10f))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
assert(e.message.equals("test"))
|
assertEquals("test", e.message)
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
okHttpJsonApiClient.getNearbyItemCount(NearbyQueryParams.Rectangular(latLng, latLng))
|
okHttpJsonApiClient.getNearbyItemCount(NearbyQueryParams.Rectangular(latLng, latLng))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
assert(e.message.equals("test"))
|
assertEquals("test", e.message)
|
||||||
}
|
}
|
||||||
verify(okhttpClient, times(2)).newCall(any())
|
verify(okhttpClient, times(2)).newCall(any())
|
||||||
verify(call, times(2)).execute()
|
verify(call, times(2)).execute()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@Test
|
||||||
|
fun testGetCampaignsWithData() {
|
||||||
|
//loads the json response from resources
|
||||||
|
val jsonResponse = loadJsonFromResource("campaigns_response_with_data.json")
|
||||||
|
|
||||||
|
//mocks the succesfull response chain
|
||||||
|
Mockito.`when`(response.isSuccessful).thenReturn(true)
|
||||||
|
Mockito.`when`(response.message).thenReturn("OK")
|
||||||
|
Mockito.`when`(response.body).thenReturn(responseBody)
|
||||||
|
Mockito.`when`(responseBody.string()).thenReturn(jsonResponse)
|
||||||
|
|
||||||
|
val campaignResponse = CampaignResponseDTO().apply {
|
||||||
|
campaignConfig = CampaignConfig().apply {
|
||||||
|
showOnlyLiveCampaigns = false
|
||||||
|
sortBy = "startDate"
|
||||||
|
}
|
||||||
|
campaigns = listOf(
|
||||||
|
Campaign().apply {
|
||||||
|
title = "Wiki Loves Monuments"
|
||||||
|
isWLMCampaign = true
|
||||||
|
},
|
||||||
|
Campaign().apply {
|
||||||
|
title = "Wiki Loves Nature"
|
||||||
|
isWLMCampaign = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
//any() for the string argument and eq() for the class argument.
|
||||||
|
Mockito.`when`(
|
||||||
|
gson.fromJson(
|
||||||
|
any<String>(),
|
||||||
|
eq(CampaignResponseDTO::class.java)
|
||||||
|
)
|
||||||
|
).thenReturn(campaignResponse)
|
||||||
|
|
||||||
|
//call the getCampaigns
|
||||||
|
val result: CampaignResponseDTO? = okHttpJsonApiClient.getCampaigns().blockingGet()
|
||||||
|
|
||||||
|
//verify the results
|
||||||
|
assertNotNull(result)
|
||||||
|
assertNotNull(result?.campaigns)
|
||||||
|
assertEquals(2, result?.campaigns!!.size)
|
||||||
|
assertEquals("Wiki Loves Monuments", result.campaigns!![0].title)
|
||||||
|
assertTrue(result.campaigns!![0].isWLMCampaign)
|
||||||
|
assertEquals("Wiki Loves Nature", result.campaigns!![1].title)
|
||||||
|
assertEquals(false, result.campaigns!![1].isWLMCampaign)
|
||||||
|
assertNotNull(result.campaignConfig)
|
||||||
|
assertFalse(result.campaignConfig!!.showOnlyLiveCampaigns)
|
||||||
|
assertEquals("startDate", result.campaignConfig!!.sortBy)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testGetCampaignsEmpty() {
|
||||||
|
//loads the empty json response
|
||||||
|
val jsonResponse = loadJsonFromResource("campaigns_response_empty.json")
|
||||||
|
|
||||||
|
//mocks the successful response chain
|
||||||
|
Mockito.`when`(response.isSuccessful).thenReturn(true)
|
||||||
|
Mockito.`when`(response.message).thenReturn("OK")
|
||||||
|
Mockito.`when`(response.body).thenReturn(responseBody)
|
||||||
|
Mockito.`when`(responseBody.string()).thenReturn(jsonResponse)
|
||||||
|
|
||||||
|
val campaignResponse = CampaignResponseDTO().apply {
|
||||||
|
campaignConfig = CampaignConfig().apply {
|
||||||
|
showOnlyLiveCampaigns = false
|
||||||
|
sortBy = "startDate"
|
||||||
|
}
|
||||||
|
campaigns = emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
//use any() for the string argument and eq() for the class argument.
|
||||||
|
Mockito.`when`(
|
||||||
|
gson.fromJson(
|
||||||
|
any<String>(),
|
||||||
|
eq(CampaignResponseDTO::class.java)
|
||||||
|
)
|
||||||
|
).thenReturn(campaignResponse)
|
||||||
|
|
||||||
|
//calls getCampaigns
|
||||||
|
val result: CampaignResponseDTO? = okHttpJsonApiClient.getCampaigns().blockingGet()
|
||||||
|
|
||||||
|
//verify the results
|
||||||
|
assertNotNull(result)
|
||||||
|
assertNotNull(result?.campaigns)
|
||||||
|
assertTrue(result?.campaigns!!.isEmpty())
|
||||||
|
assertNotNull(result.campaignConfig)
|
||||||
|
assertFalse(result.campaignConfig!!.showOnlyLiveCampaigns)
|
||||||
|
assertEquals("startDate", result.campaignConfig!!.sortBy)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadJsonFromResource(fileName: String): String {
|
||||||
|
val resourcePath = "raw/$fileName"
|
||||||
|
//uses the classloader to find the resource in the test environment
|
||||||
|
val inputStream = javaClass.classLoader?.getResourceAsStream(resourcePath)
|
||||||
|
|
||||||
|
if (inputStream != null) {
|
||||||
|
//reads the entire stream content
|
||||||
|
return BufferedReader(InputStreamReader(inputStream)).use { it.readText() }
|
||||||
|
}
|
||||||
|
//throws an exception with the correct expected path
|
||||||
|
throw IllegalArgumentException("Resource $fileName not found. Please ensure the file is located in app/src/test/resources/raw/")
|
||||||
|
}
|
||||||
|
}
|
||||||
7
app/src/test/resources/raw/campaigns_response_empty.json
Normal file
7
app/src/test/resources/raw/campaigns_response_empty.json
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"showOnlyLiveCampaigns": false,
|
||||||
|
"sortBy": "startDate"
|
||||||
|
},
|
||||||
|
"campaigns": []
|
||||||
|
}
|
||||||
24
app/src/test/resources/raw/campaigns_response_with_data.json
Normal file
24
app/src/test/resources/raw/campaigns_response_with_data.json
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"showOnlyLiveCampaigns": false,
|
||||||
|
"sortBy": "startDate"
|
||||||
|
},
|
||||||
|
"campaigns": [
|
||||||
|
{
|
||||||
|
"title": "Wiki Loves Monuments",
|
||||||
|
"description": "A campaign to photograph monuments",
|
||||||
|
"startDate": "2025-09-01",
|
||||||
|
"endDate": "2025-09-30",
|
||||||
|
"link": "https://commons.wikimedia.org/wiki/Campaign:Wiki_Loves_Monuments",
|
||||||
|
"isWLMCampaign": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Wiki Loves Nature",
|
||||||
|
"description": "A campaign to photograph nature",
|
||||||
|
"startDate": "2025-06-01",
|
||||||
|
"endDate": "2025-06-30",
|
||||||
|
"link": "https://commons.wikimedia.org/wiki/Campaign:Wiki_Loves_Nature",
|
||||||
|
"isWLMCampaign": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue