Skip to content

Commit

Permalink
Part 9: Unit Tests with Mockito + RxJava TestSubscriber
Browse files Browse the repository at this point in the history
  • Loading branch information
juanchosaravia committed May 28, 2016
1 parent b7b4994 commit 323edfd
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 19 deletions.
3 changes: 3 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ dependencies {
// Retrofit
compile "com.squareup.retrofit2:retrofit:2.0.0"
compile "com.squareup.retrofit2:converter-moshi:2.0.0"

// Tests
testCompile "org.mockito:mockito-core:1.+"
}
buildscript {
ext.kotlin_version = '1.0.2'
Expand Down
12 changes: 12 additions & 0 deletions app/src/main/java/com/droidcba/kedditbysteps/api/NewsAPI.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.droidcba.kedditbysteps.api

import retrofit2.Call

/**
* News API
*
* @author juancho.
*/
interface NewsAPI {
fun getNews(after: String, limit: String): Call<RedditNewsResponse>
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import retrofit2.Call
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory

class RestAPI() {
class NewsRestAPI() : NewsAPI {

private val redditApi: RedditApi

Expand All @@ -17,7 +17,7 @@ class RestAPI() {
redditApi = retrofit.create(RedditApi::class.java)
}

fun getNews(after: String, limit: String): Call<RedditNewsResponse> {
override fun getNews(after: String, limit: String): Call<RedditNewsResponse> {
return redditApi.getTop(after, limit)
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.droidcba.kedditbysteps.features.news

import com.droidcba.kedditbysteps.api.RestAPI
import com.droidcba.kedditbysteps.api.NewsAPI
import com.droidcba.kedditbysteps.api.NewsRestAPI
import com.droidcba.kedditbysteps.commons.RedditNews
import com.droidcba.kedditbysteps.commons.RedditNewsItem
import rx.Observable
Expand All @@ -10,7 +11,7 @@ import rx.Observable
*
* @author juancho
*/
class NewsManager(private val api: RestAPI = RestAPI()) {
class NewsManager(private val api: NewsAPI = NewsRestAPI()) {

/**
*
Expand Down
15 changes: 0 additions & 15 deletions app/src/test/java/com/droidcba/kedditbysteps/ExampleUnitTest.java

This file was deleted.

102 changes: 102 additions & 0 deletions app/src/test/java/com/droidcba/kedditbysteps/NewsManagerTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package com.droidcba.kedditbysteps

import com.droidcba.kedditbysteps.api.*
import com.droidcba.kedditbysteps.commons.RedditNews
import com.droidcba.kedditbysteps.features.news.NewsManager
import okhttp3.MediaType
import okhttp3.ResponseBody
import org.junit.Before
import org.junit.Test
import org.mockito.Mockito
import org.mockito.Mockito.`when`
import org.mockito.Mockito.anyString
import retrofit2.Call
import retrofit2.Response
import rx.observers.TestSubscriber

This comment has been minimized.

Copy link
@juanchosaravia

juanchosaravia Mar 23, 2018

Author Owner

Moving from Rx1 to Rx2 involves in different changes. To be honest I didn't research about this as I already changed the code to replace Rx with Coroutines

This comment has been minimized.

Copy link
@yerdnavolutab

yerdnavolutab Jun 16, 2018

for Rx2 just use io.reactivex.observers.TestObserver

This comment has been minimized.

Copy link
@juanchosaravia

juanchosaravia Jun 16, 2018

Author Owner

I end up using Coroutines, please see latest changes

import java.util.*

/**
* Unit Tests for NewsManager
*
* @author juancho.
*/
class NewsManagerTest {

var testSub = TestSubscriber<RedditNews>()
var apiMock = mock<NewsAPI>()
var callMock = mock<Call<RedditNewsResponse>>()

@Before
fun setup() {
testSub = TestSubscriber<RedditNews>()
apiMock = mock<NewsAPI>()
callMock = mock<Call<RedditNewsResponse>>()
`when`(apiMock.getNews(anyString(), anyString())).thenReturn(callMock)
}

@Test
fun testSuccess_basic() {
// prepare
val redditNewsResponse = RedditNewsResponse(RedditDataResponse(listOf(), null, null))
val response = Response.success(redditNewsResponse)

`when`(callMock.execute()).thenReturn(response)

// call
val newsManager = NewsManager(apiMock)
newsManager.getNews("").subscribe(testSub)

// assert
testSub.assertNoErrors()
testSub.assertValueCount(1)
testSub.assertCompleted()

This comment has been minimized.

Copy link
@brun0xon

brun0xon Jun 10, 2017

testSub.assertComplete() - correction

This comment has been minimized.

Copy link
@pboos

pboos Aug 10, 2017

It is actually assertCompleted() in this code. Because he is using rxjava1.
In rxjava2 it is assertComplete() as you say. (but then you have to adjust other code as well in this repo)

}

@Test
fun testSuccess_checkOneNews() {
// prepare
val newsData = RedditNewsDataResponse(
"author",
"title",
10,
Date().time,
"thumbnail",
"url"
)
val newsResponse = RedditChildrenResponse(newsData)
val redditNewsResponse = RedditNewsResponse(RedditDataResponse(listOf(newsResponse), null, null))
val response = Response.success(redditNewsResponse)

`when`(callMock.execute()).thenReturn(response)

// call
val newsManager = NewsManager(apiMock)
newsManager.getNews("").subscribe(testSub)

// assert
testSub.assertNoErrors()
testSub.assertValueCount(1)
testSub.assertCompleted()

assert(testSub.onNextEvents[0].news[0].author == newsData.author)
assert(testSub.onNextEvents[0].news[0].title == newsData.title)
}

@Test
fun testError() {
// prepare
val responseError = Response.error<RedditNewsResponse>(500,
ResponseBody.create(MediaType.parse("application/json"), ""))

`when`(callMock.execute()).thenReturn(responseError)

// call
val newsManager = NewsManager(apiMock)
newsManager.getNews("").subscribe(testSub)

// assert
assert(testSub.onErrorEvents.size == 1)

This comment has been minimized.

Copy link
@pboos

pboos Aug 10, 2017

Not sure why, but on my machine, this assert didn't work. I could change 1 to 2 and anything else and it did succeed.
Had to change it to: Assert.assertEquals(1, testSub.errors().size)

Same as well for all the other asserts. And the benefit of assertEquals is as well that it tells you what the difference was. Not just that it fails, but what it was expecting, and what the actual value was.

This comment has been minimized.

Copy link
@juanchosaravia

juanchosaravia Aug 12, 2017

Author Owner

oh interesting, thanks for the comment! I updated the unit tests

}
}

inline fun <reified T : Any> mock(): T = Mockito.mock(T::class.java)

0 comments on commit 323edfd

Please sign in to comment.