Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Have you met Test?

Have you met Test?

Talk from GDG DevFest Yangon 2018

Aung Kyaw Paing

October 29, 2018
Tweet

More Decks by Aung Kyaw Paing

Other Decks in Programming

Transcript

  1. Have you met Test?
    nexlabs

    View Slide

  2. What’s Testing?
    /

    View Slide

  3. What’s Testing?
    /
    take measures to check the quality, performance, or reliability of
    (something), especially before putting it into widespread use or
    practice.

    View Slide

  4. What’s Testing?
    Making sure something works as intended
    /

    View Slide

  5. /
    GDG
    Yangon
    DevFest
    GDG,Yangon,DevFest

    View Slide

  6. /
    import org.junit.Test
    class StringUtilsConversionTest {
    @Test
    fun testConversion() {
    }
    }

    View Slide

  7. /
    import org.junit.Test
    class StringUtilsConversionTest {
    @Test
    fun testConversion() {
    //Declare necessary inputs
    val stringList = listOf("GDG", "Yangon", "DevFest")
    }
    }

    View Slide

  8. /
    import org.junit.Test
    class StringUtilsConversionTest {
    @Test
    fun testConversion() {
    //Declare necessary inputs
    val stringList = listOf("GDG", "Yangon", "DevFest")
    //Declare expected value
    val expected = "GDG,Yangon,DevFest"
    }
    }

    View Slide

  9. /
    @Test
    fun testConversion() {
    //Declare necessary inputs
    val stringList = listOf("GDG", "Yangon", "DevFest")
    //Declare expected value
    val expected = "GDG,Yangon,DevFest"
    //Declare actual
    val actual = StringUtils.convertStringListToString(stringList)
    }

    View Slide

  10. /
    object StringUtils {
    fun convertStringListToString(stringList: List): String {
    TODO("not implemented")
    }
    }

    View Slide

  11. /
    @Test
    fun testConversion() {
    //Declare necessary inputs
    val stringList = listOf
    //Check if two values are equal("GDG", "Yangon", "DevFest")
    //Declare expected value
    val expected = "GDG,Yangon,DevFest"
    //Declare actual
    val actual = StringUtils.convertStringListToString(stringList)
    //Check if two values are equal
    org.junit.Assert.assertEquals(expected, actual)
    }

    View Slide

  12. /

    View Slide

  13. /
    fun convertStringListToString(stringList: List): String {
    TODO("not implemented")
    }

    View Slide

  14. /
    fun convertStringListToString(stringList: List): String {
    return "GDG,Yangon,DevFest"
    }

    View Slide

  15. /

    View Slide

  16. /
    @Test
    fun testWithFourStrings() {
    //Declare necessary inputs
    val stringList = listOf("GDG", "Yangon", "DevFest", "Android")
    //Declare expected value
    val expected = "GDG,Yangon,DevFest,Android"
    //Declare actual
    val actual = StringUtils.convertStringListToString(stringList)
    //Check if two values are equal
    org.junit.Assert.assertEquals(expected, actual)
    }

    View Slide

  17. /

    View Slide

  18. /
    fun convertStringListToString(stringList: List): String {
    if (stringList.size == 4) {
    return "GDG,Yangon,DevFest,Android"
    }
    return "GDG,Yangon,DevFest"
    }

    View Slide

  19. /

    View Slide

  20. /
    @Test
    fun testWithThreeStringCaseTwo() {
    //Declare necessary inputs
    val stringList = listOf("GDG", "Yangon", "Best")
    //Declare expected value
    val expected = "GDG,Yangon,Best"
    //Declare actual
    val actual = StringUtils.convertStringListToString(stringList)
    //Check if two values are equal
    org.junit.Assert.assertEquals(expected, actual)
    }

    View Slide

  21. /

    View Slide

  22. /
    fun convertStringListToString(stringList: List): String {
    val stringBuilder = StringBuilder()
    stringList.forEachIndexed { index, string ->
    stringBuilder.append(string)
    //If Index is last, we don't append delimiter
    if (index != stringList.lastIndex) {
    stringBuilder.append(",")
    }
    }
    return stringBuilder.toString()
    }

    View Slide

  23. /

    View Slide

  24. Strategies to green-light
    • Fake It till you make it
    • Use Obvious Implementation
    /

    View Slide

  25. Strategies to green-light
    • Fake It till you make it
    • “Return a constant and gradually replace constants with variables
    until you have the real code”
    • Use Obvious Implementation
    /

    View Slide

  26. /
    fun convertStringListToString(stringList: List): String {
    return "GDG,Yangon,DevFest"
    }

    View Slide

  27. /
    fun convertStringListToString(stringList: List): String {
    if (stringList.size == 4) {
    return "GDG,Yangon,DevFest,Android"
    }
    return "GDG,Yangon,DevFest"
    }

    View Slide

  28. /
    fun convertStringListToString(stringList: List): String {
    val stringBuilder = StringBuilder()
    stringList.forEachIndexed { index, string ->
    stringBuilder.append(string)
    //If Index is last, we don't append delimiter
    if (index != stringList.lastIndex) {
    stringBuilder.append(",")
    }
    }
    return stringBuilder.toString()
    }

    View Slide

  29. Strategies to green-light
    • Fake It till you make it
    • “Return a constant and gradually replace constants with variables
    until you have the real code”
    • Use Obvious Implementation
    • “Type in the real implementation.”
    /

    View Slide

  30. /
    GDG
    Yangon
    DevFest
    GDG|Yangon|DevFest

    View Slide

  31. /
    @Test
    fun testWithDifferentDelimiter() {
    //Declare necessary inputs
    val stringList = listOf("GDG", "Yangon", "Best")
    //Declare expected value
    val expected = "GDG|Yangon|Best"
    //Declare actual
    val actual = StringUtils.convertStringListToString(stringList)
    //Check if two values are equal
    org.junit.Assert.assertEquals(expected, actual)
    }

    View Slide

  32. /
    fun convertStringListToString(stringList: List): String {
    val stringBuilder = StringBuilder()
    stringList.forEachIndexed { index, string ->
    stringBuilder.append(string)
    //If Index is last, we don't append delimiter
    if (index != stringList.lastIndex) {
    stringBuilder.append(",")
    }
    }
    return stringBuilder.toString()
    }

    View Slide

  33. /
    fun convertStringListToString(
    stringList: List,
    delimiter: String
    ): String {
    val stringBuilder = StringBuilder()
    stringList.forEachIndexed { index, string ->
    stringBuilder.append(string)
    //If Index is last, we don't append delimiter
    if (index != stringList.lastIndex) {
    stringBuilder.append(delimiter)
    }
    }
    return stringBuilder.toString()
    }

    View Slide

  34. /
    fun convertStringListToString(
    stringList: List,
    delimiter: String
    ): String {
    val stringBuilder = StringBuilder()
    stringList.forEachIndexed { index, string ->
    stringBuilder.append(string)
    //If Index is last, we don't append delimiter
    if (index != stringList.lastIndex) {
    stringBuilder.append(delimiter)
    }
    }
    return stringBuilder.toString()
    }

    View Slide

  35. /
    val actual = StringUtils.convertStringListToString(stringList, ",")

    View Slide

  36. /
    val actual = StringUtils.convertStringListToString(stringList, “|")

    View Slide

  37. /

    View Slide

  38. /
    val stringBuilder = StringBuilder()
    stringList.forEachIndexed { index, string ->
    stringBuilder.append(string)
    //If Index is last, we don't append delimiter
    if (index != stringList.lastIndex) {
    stringBuilder.append(delimiter)
    }
    }
    return stringBuilder.toString()

    View Slide

  39. As test get more specific, real code get more generic
    /

    View Slide

  40. As test get more specific, real code get more generic
    /
    Triangulation Method

    View Slide

  41. Testing in Android
    /

    View Slide

  42. Testing in Android
    /
    70%

    View Slide

  43. Testing in Android
    /
    70%
    20%

    View Slide

  44. Testing in Android
    /
    70%
    20%
    10%

    View Slide

  45. Mocking
    /

    View Slide

  46. /
    3
    True
    False
    32
    3 x 2

    View Slide

  47. /
    interface DataSource {
    fun getValue(): Int
    fun isSomething(): Boolean
    }

    View Slide

  48. /
    class RealDataSource : DataSource {
    override fun getValue(): Int {
    //Do Network call
    TODO()
    }
    override fun isSomething(): Boolean {
    //Do Network call
    TODO()
    }
    }

    View Slide

  49. /
    class GetCalculatedValue {
    fun execute(): Int {
    TODO()
    }
    }

    View Slide

  50. /
    class GetCalculatedValueTest {
    @Test
    fun testTrueCase() {
    TODO("not implemented")
    }
    @Test
    fun testFalseCase() {
    TODO("not implemented")
    }
    }

    View Slide

  51. /
    fun testTrueCase() {
    val fakeDataSource = object : DataSource {
    override fun getValue(): Int {
    return 3
    }
    override fun isSomething(): Boolean {
    return true
    }
    }
    val expected = 9
    val getCalculatedValue = GetCalculatedValue()
    val actual = getCalculatedValue.execute()
    assertEquals(expected, actual)
    }

    View Slide

  52. /
    fun testTrueCase() {
    val fakeDataSource = object : DataSource {
    override fun getValue(): Int {
    return 3
    }
    override fun isSomething(): Boolean {
    return true
    }
    }
    val expected = 9
    val getCalculatedValue = GetCalculatedValue()
    val actual = getCalculatedValue.execute(fakeDataSource)
    assertEquals(expected, actual)
    }

    View Slide

  53. /
    class GetCalculatedValue {
    fun execute(): Int {
    TODO()
    }
    }

    View Slide

  54. /
    class GetCalculatedValue {
    fun execute(dataSource: DataSource): Int {
    TODO()
    }
    }

    View Slide

  55. /
    class GetCalculatedValue {
    fun execute(dataSource: DataSource): Int {
    return dataSource.getValue() * dataSource.getValue()
    }
    }

    View Slide

  56. /
    fun testFalseCase() {
    val fakeDataSource = object : DataSource {
    override fun getValue(): Int {
    return 3
    }
    override fun isSomething(): Boolean {
    return false
    }
    }
    val expected = 6
    val getCalculatedValue = GetCalculatedValue()
    val actual = getCalculatedValue.execute(fakeDataSource)
    assertEquals(expected, actual)
    }

    View Slide

  57. /
    val getCalculatedValue = GetCalculatedValue()
    val actual = getCalculatedValue.execute(fakeDataSource)

    View Slide

  58. /
    val getCalculatedValue = GetCalculatedValue(fakeDataSource)
    val actual = getCalculatedValue.execute()
    class GetCalculatedValue constructor(val dataSource: DataSource) {
    }

    View Slide

  59. /
    fun execute(): Int {
    return if (dataSource.isSomething()) {
    dataSource.getValue() * dataSource.getValue()
    } else {
    dataSource.getValue() * 2
    }
    }

    View Slide

  60. Mockito
    /

    View Slide

  61. Mockito
    /
    org.mockito:mockito-core
    org.mockito:mockito-inline
    com.nhaarman:mockito-kotlin

    View Slide

  62. /
    val fakeDataSource = object : DataSource {
    override fun getValue(): Int {
    return 3
    }
    override fun isSomething(): Boolean {
    return true
    }
    }
    val getCalculatedValue = GetCalculatedValue(fakeDataSource)
    val actual = getCalculatedValue.execute()

    View Slide

  63. /
    class GetCalculatedValueTest {
    @Mock lateinit var fakeDataSource: DataSource
    lateinit var getCalculatedValue: GetCalculatedValue
    }

    View Slide

  64. /
    @Before
    fun setUp() {
    }

    View Slide

  65. /
    @Before
    fun setUp() {
    }
    @After
    fun tearDown() {
    }

    View Slide

  66. /
    @Mock lateinit var fakeDataSource: DataSource
    lateinit var getCalculatedValue: GetCalculatedValue
    @Before
    fun setUp() {
    MockitoAnnotations.initMocks(this)
    getCalculatedValue = GetCalculatedValue(fakeDataSource)
    }

    View Slide

  67. /
    @Test
    fun testTrueCase() {
    whenever(fakeDataSource.isSomething()).thenReturn(true)
    whenever(fakeDataSource.getValue()).thenReturn(3)
    val expected = 9
    val actual = getCalculatedValue.execute()
    assertEquals(expected, actual)
    }

    View Slide

  68. /
    @Test
    fun testTrueCase() {
    whenever(fakeDataSource.isSomething()).thenReturn(true)
    whenever(fakeDataSource.getValue()).thenReturn(3)
    val expected = 9
    val actual = getCalculatedValue.execute()
    assertEquals(expected, actual)
    }

    View Slide

  69. /
    .thenThrow(//Throwable)
    .thenAnswer {
    //Do Stuffs
    }
    .thenReturn(//Return value)

    View Slide

  70. /
    fun testTrueCase() {
    whenever(fakeDataSource.isSomething()).thenReturn(true)
    whenever(fakeDataSource.getValue()).thenReturn(3)
    val expected = 9
    val actual = getCalculatedValue.execute()
    verify(fakeDataSource).isSomething()
    verify(fakeDataSource).getValue()
    assertEquals(expected, actual)
    }

    View Slide

  71. /
    fun testTrueCase() {
    whenever(fakeDataSource.isSomething()).thenReturn(true)
    whenever(fakeDataSource.getValue()).thenReturn(3)
    val expected = 9
    val actual = getCalculatedValue.execute()
    verify(fakeDataSource).isSomething()
    verify(fakeDataSource).getValue()
    assertEquals(expected, actual)
    }

    View Slide

  72. /
    fun testTrueCase() {
    whenever(fakeDataSource.isSomething()).thenReturn(true)
    whenever(fakeDataSource.getValue()).thenReturn(3)
    val expected = 9
    val actual = getCalculatedValue.execute()
    verify(fakeDataSource, times(1)).isSomething()
    verify(fakeDataSource, times(1)).getValue()
    assertEquals(expected, actual)
    }

    View Slide

  73. /
    fun execute(): Int {
    return if (dataSource.isSomething()) {
    dataSource.getValue() * dataSource.getValue()
    } else {
    dataSource.getValue() * 2
    }
    }

    View Slide

  74. /
    fun execute(): Int {
    val value = dataSource.getValue()
    return if (dataSource.isSomething()) {
    value * value
    } else {
    value * 2
    }
    }

    View Slide

  75. /
    fun testTrueCase() {
    whenever(fakeDataSource.isSomething()).thenReturn(true)
    whenever(fakeDataSource.getValue()).thenReturn(3)
    val expected = 9
    val getCalculatedValue = GetCalculatedValue(fakeDataSource)
    val actual = getCalculatedValue.execute()
    verify(fakeDataSource, times(1)).isSomething()
    verify(fakeDataSource, times(1)).getValue()
    assertEquals(expected, actual)
    }

    View Slide

  76. Robolectric
    /

    View Slide

  77. /
    - Run instrumentation test on JVM
    - Allow running integration tests without android device
    Robolectric

    View Slide

  78. Espresso (UI Testing)
    /

    View Slide

  79. /
    Espresso (UI Testing)
    - Not recommend to run on physical device

    View Slide

  80. /
    @RunWith(AndroidJUnit4::class)
    @LargeTest
    class MainActivityTest {
    }

    View Slide

  81. /
    @Rule
    @JvmField
    val activityTestRule = ActivityTestRule(
    MainActivity::class.java,
    false,
    false
    )

    View Slide

  82. /
    Espresso Idling Resource

    View Slide

  83. /
    public String getName();

    View Slide

  84. /
    public boolean isIdleNow();

    View Slide

  85. /
    public void registerIdleTransitionCallback(ResourceCallback callback);
    public interface ResourceCallback {
    public void onTransitionToIdle();
    }

    View Slide

  86. /
    @Override
    public String getName() {
    return "ViewPagerIdlingResource";
    }
    @Override
    public boolean isIdleNow() {
    return viewPager == null || isIdle;
    }
    @Override
    public void onPageScrollStateChanged(int state) {
    this.isIdle = state != ViewPager.SCROLL_STATE_SETTLING;
    if (this.isIdle) {
    this.resourceCallback.onTransitionToIdle();
    }
    }

    View Slide

  87. /
    Espresso + Dagger

    View Slide

  88. /
    Espresso + Dagger
    @Module
    class TestApplicationModule
    @Component(modules = [TestApplicationModule:class]
    interface TestApplicationComponent
    class TestApplication : Application

    View Slide

  89. /
    Espresso + Dagger
    @Provides fun sharedPref(context: Context): SharedPreferences {
    val pref = PreferenceManager.getDefaultSharedPreferences(context);
    pref.edit {
    clear()
    }
    return pref
    }

    View Slide

  90. /
    Espresso + Dagger
    @Provides @Singleton
    fun userRepository(): UserRepository {
    return mock()
    }

    View Slide

  91. /
    Espresso + Dagger
    interface TestAppComponent {
    //...
    fun userRepository(): UserRepository
    //...
    }

    View Slide

  92. /
    Espresso + Dagger
    class TestApplication {
    companion object {
    lateinit var appComponent: TestAppComponent
    }
    override fun onCreate() {
    super.onCreate()
    appComponent = //...
    }
    }

    View Slide

  93. /
    Espresso + Dagger
    val appComponent = TestApplication.appComponent
    whenever(appComponent.userRepository().login()).thenReturn(true)

    View Slide

  94. /
    Espresso + Dagger
    @Rule
    @JvmField
    val activityTestRule = ActivityTestRule(
    MainActivity::class.java,
    false,
    false
    )

    View Slide

  95. /
    Espresso + Dagger
    @Rule
    @JvmField
    val activityTestRule = ActivityTestRule(
    MainActivity::class.java,
    false,
    false
    )
    val activity = activityTestRule.launchActivity(null)

    View Slide

  96. Static Analysis
    /
    • Lint
    • PMD
    • Findbugs
    • Checkstyle

    View Slide

  97. Where do I start?
    /

    View Slide

  98. /
    • Write a test whenever app crash
    Where do I start?

    View Slide

  99. /
    • Write a test whenever app crash
    • Start writing in complex parts
    Where do I start?

    View Slide

  100. /
    • Write a test whenever app crash
    • Start writing in complex parts
    • Learn tools in free time
    Where do I start?

    View Slide

  101. Continuous Integration/Deployment
    /

    View Slide

  102. Recap
    /

    View Slide

  103. Have you met Test?
    nexlabs

    View Slide