Skip to content

Commit

Permalink
Add "Learn from this Repo" markdown files
Browse files Browse the repository at this point in the history
  • Loading branch information
oudaykhaled committed Dec 17, 2023
1 parent bbe92ca commit 01f2b69
Show file tree
Hide file tree
Showing 17 changed files with 378 additions and 91 deletions.
35 changes: 35 additions & 0 deletions Android_Instrumented_Testing_Guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@

# Guide to Android Instrumented Tests

## Introduction to Android Instrumented Tests
Android Instrumented Tests are tests that run on Android hardware or emulators, allowing developers to test the application's interface and functionality as it would operate in a real-world scenario.

## Purpose of Android Instrumented Tests
- **Real Environment Testing**: They provide a platform for testing the app in an environment similar to that of the end users.
- **UI and Integration Testing**: Ideal for testing user interfaces and the integration between different components of the application.

## Libraries Used in Android Instrumented Tests
- **Espresso**: For UI testing, providing APIs to simulate user interactions and test the UI.
- **JUnit**: For structuring tests and assertions.
- **Mockito**: For creating mocks and stubs in tests.

## Example from Provided Files
### `BillsScreenTest.kt`
- **Purpose**: Tests the Bills screen UI and interactions.
- **Key Components**:
- Using Espresso to simulate user interactions.
- JUnit for assertions and test structure.

### `CustomTestRunner.kt`
- **Purpose**: Custom runner to set up the testing environment.
- **Key Components**:
- Extends AndroidJUnitRunner for custom configurations.

## Best Practices for Android Instrumented Tests
- **Keep Tests Isolated**: Each test should be independent of others.
- **Use Mocks and Stubs**: To simulate complex objects and interactions.
- **Focus on User Experience**: Test from a user's perspective, ensuring UI and functionality meet expectations.

## Conclusion
Android Instrumented Tests are crucial for ensuring that your app behaves correctly in a real-world environment, especially for UI and integration aspects.

28 changes: 28 additions & 0 deletions Android_Unit_Testing_Guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

# Android Unit Testing Guide

## Introduction to Unit Testing in Android
Unit testing is a fundamental aspect of Android development, enabling developers to validate individual units of source code. These tests help ensure that code behaves as expected, leading to more robust and reliable applications.

## How Unit Testing Works in Android
In Android, unit tests typically focus on the smallest unit of testable software in an application, such as classes and methods. These tests are isolated from dependencies, often utilizing mock objects to simulate real-world scenarios.

## Libraries and Tools Used
- **JUnit**: A framework for writing and running unit tests in Java and Kotlin.
- **Mockito**: A mocking framework used to isolate units of code for testing by creating mock objects.
- **Kotlin Coroutines**: Provides a way to handle asynchronous operations in testing.
- **Retrofit**: A type-safe HTTP client used for testing network interactions.
- **AndroidX**: Provides the `InstantTaskExecutorRule` for managing background tasks in testing.

## Best Practices for Unit Testing
- **Structure Your Tests Clearly**: Organize tests logically and name them descriptively.
- **Use Mocks Effectively**: Utilize Mockito to create mock objects and simulate real-world scenarios.
- **Handle Asynchronous Operations**: Use Kotlin Coroutines for testing asynchronous code effectively.
- **Maintain Code Readability**: Write tests that are easy to read and understand.

## Case Study: Analysis of Provided Kotlin Files
A detailed examination of `CreditCardRepositoryImpTest.kt`, `CreditCardUseCaseImpTest.kt`, and `CreditCardViewModelTest.kt` reveals practical applications of these practices in a real-world scenario.

## Conclusion
Effective unit testing is crucial for developing reliable Android applications. By following best practices and utilizing the right tools, developers can ensure their code meets quality standards.

Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Jetpack Compose Essentials in the Wallet Sample Project

## Introduction

Welcome to the "Jetpack Compose Essentials in the Wallet Sample Project" guide! This document is crafted for Android developers, especially those transitioning from traditional XML-based UI development to Jetpack Compose. Jetpack Compose is Android's modern toolkit for building native UIs. It simplifies and accelerates UI development on Android with less code, powerful tools, and intuitive Kotlin APIs.

In this guide, you'll learn about various Jetpack Compose components used in the Wallet Sample project. We will cover each file, highlighting the native Jetpack Compose elements, their purpose, and where they are used in the project. For each element, a link to the official documentation is provided for deeper understanding.

## File Breakdowns

### 1. BillOptionsComposable.kt

This file defines the UI components for displaying bill options.

- **Jetpack Compose Elements:**
- `Column`: Used for vertical layouts. [Documentation](https://developer.android.com/jetpack/compose/layout#column)
- `Text`: Displays text. [Documentation](https://developer.android.com/jetpack/compose/text)
- `Button`: Represents a clickable button. [Documentation](https://developer.android.com/jetpack/compose/button)

### 2. BottomNavigationBar.kt

Handles the bottom navigation bar of the app.

- **Jetpack Compose Elements:**
- `BottomNavigation`: Container for the bottom navigation bar. [Documentation](https://developer.android.com/jetpack/compose/navigation#bottom-nav)
- `BottomNavigationItem`: Individual items in the navigation bar. [Documentation](https://developer.android.com/jetpack/compose/navigation#items)

### 3. CreditCardComposable.kt

Manages the UI for displaying credit card information.

- **Jetpack Compose Elements:**
- `Box`: A container for stacking elements. [Documentation](https://developer.android.com/jetpack/compose/layout#box)
- `Image`: Displays images. [Documentation](https://developer.android.com/jetpack/compose/images)

### 4. HomeHeaderComposable.kt

Defines the header section of the home screen.

- **Jetpack Compose Elements:**
- `Row`: Used for horizontal layouts. [Documentation](https://developer.android.com/jetpack/compose/layout#row)
- `Icon`: Displays icons. [Documentation](https://developer.android.com/jetpack/compose/icons)

### 5. LoanCardComposable.kt

Creates the UI components for loan card details.

- **Jetpack Compose Elements:**
- `Card`: A card layout. [Documentation](https://developer.android.com/jetpack/compose/material#card)
- `Divider`: A horizontal dividing line. [Documentation](https://developer.android.com/jetpack/compose/material#divider)

### 6. HomeScreen.kt

The main UI for the home screen of the app.

- **Jetpack Compose Elements:**
- `Scaffold`: A layout structure for Material Design. [Documentation](https://developer.android.com/jetpack/compose/layouts/material#scaffold)

### 7. MainScreen.kt

Manages the primary screen of the app.

- **Jetpack Compose Elements:**
- `NavHost`: Manages navigation within the app. [Documentation](https://developer.android.com/jetpack/compose/navigation#navhost)

### 8. HomeViewModel.kt

Handles the logic and data for the home screen.

- **Note:** This file primarily deals with ViewModel logic and may not directly involve Jetpack Compose UI components.

## Conclusion

By going through this guide, you should have gained a fundamental understanding of various Jetpack Compose elements and their applications in a real-world project. The Wallet Sample project serves as a practical example to see these components in action. Remember, the official documentation is an invaluable resource for further exploration and deepening your understanding of Jetpack Compose.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ This project is intentionally designed to facilitate a smooth transition from tr
- 💾 **Moshi**: Seamless JSON parsing.
-**Kotlin Coroutines**: Manage background tasks effortlessly.


## Learn from this Repo

- **Jetpack Compose Unveiled**: Explore our in-depth guide on the Wallet Sample Project, available in this repository. [Jetpack Compose Unveiled Guide](./Jetpack_Compose_Unveiled_A_Practical_Guide_through_the_Wallet_Sample_Project.md)
- **Understanding Clean Architecture in Android**: Discover architectural insights used in this project, shared right here. [Clean Architecture Guide](./understanding_clean_architecture_in_android.md)
- **Understanding Style and MDS3**: Dive into modern design systems and styles, all within this repo. [Style and MDS3 Guide](./understanding_style_and_mds3.md)
- **Android Unit Testing Guide**: This file is a guide on Android unit testing, covering best practices and analysis of Kotlin test files. [Android Unit Testing Guide](./Android_Unit_Testing_Guide.md)
- **Guide to Android Instrumented Tests Guide**: This file is a guide on Android Instrumented testing, covering best practices and analysis of Kotlin test files. [Guide to Android Instrumented Tests Guide](./Android_Instrumented_Testing_Guide.md)


## Getting Started

1. **Clone the Repository**: `git clone https://github.com/your-repo-link`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ class BillsScreenTest {
advanceUntilIdle()
advanceTimeBy(1000)

composeTestRule.onNodeWithText("Date: 2023-12-01").assertIsDisplayed()
composeTestRule.onNodeWithText("Amount: 100.0").assertIsDisplayed()

}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.ouday.cryptowalletsample
package com.ouday.cryptowalletsample.bills

import com.ouday.cryptowalletsample.bills.data.model.Bill
import com.ouday.cryptowalletsample.bills.data.model.History
Expand All @@ -9,8 +9,7 @@ import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
import retrofit2.Response

Expand All @@ -24,15 +23,15 @@ class BillRepositoryImpTest {
@Before
fun setUp() = runTest {
MockitoAnnotations.openMocks(this)
apiService = mock(BillApiService::class.java)
apiService = Mockito.mock(BillApiService::class.java)
billRepository = BillRepositoryImp(apiService)
billRepository = BillRepositoryImp(apiService)

val history = listOf(
History("2022-02-24", 313.08, true),
)
val bills = listOf(Bill(1, "Electricity Bill", "electricity.svg", history))
`when`(apiService.getCreditCards()).thenReturn(Response.success(bills))
Mockito.`when`(apiService.getCreditCards()).thenReturn(Response.success(bills))
}

@Test
Expand Down Expand Up @@ -62,8 +61,8 @@ class BillRepositoryImpTest {

@Test(expected = Exception::class)
fun `getBills throws exception on API failure`() = runTest {
`when`(apiService.getCreditCards()).thenThrow(RuntimeException("Network Error"))
Mockito.`when`(apiService.getCreditCards()).thenThrow(RuntimeException("Network Error"))

billRepository.getBills().toList()
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.ouday.cryptowalletsample
package com.ouday.cryptowalletsample.bills

import com.ouday.cryptowalletsample.bills.data.model.Bill
import com.ouday.cryptowalletsample.bills.data.repository.BillRepository
Expand All @@ -9,7 +9,7 @@ import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito
import org.mockito.MockitoAnnotations

class BillUseCaseImpTest {
Expand All @@ -28,9 +28,9 @@ class BillUseCaseImpTest {
@Test
fun `getBills returns list of bills`() = runTest {
val bills = listOf(Bill(1, "Electricity", "icon_url", emptyList()))
`when`(billRepository.getBills()).thenReturn(flowOf(bills))
Mockito.`when`(billRepository.getBills()).thenReturn(flowOf(bills))

val result = billUseCase.getBills().toList()
assert(result[0] == bills)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package com.ouday.cryptowalletsample
package com.ouday.cryptowalletsample.bills

import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.ouday.cryptowalletsample.bills.data.model.Bill
import com.ouday.cryptowalletsample.bills.data.model.History
import com.ouday.cryptowalletsample.bills.domain.usecase.BillUseCase
import com.ouday.cryptowalletsample.bills.ui.viewmodel.BillViewModel
import com.ouday.cryptowalletsample.common.FlowState
import junit.framework.TestCase.assertTrue
import junit.framework.TestCase
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.take
Expand All @@ -26,7 +25,7 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito
import org.mockito.MockitoAnnotations

@ExperimentalCoroutinesApi
Expand Down Expand Up @@ -56,7 +55,7 @@ class BillViewModelTest {
History("2022-02-24", 313.08, true),
)
val bills = listOf(Bill(1, "Electricity Bill", "electricity.svg", history))
`when`(billUseCase.getBills()).thenReturn(flowOf(bills))
Mockito.`when`(billUseCase.getBills()).thenReturn(flowOf(bills))

val job = launch {
viewModel.triggerFetchBills()
Expand All @@ -81,7 +80,7 @@ class BillViewModelTest {
throw Exception(errorMessage)
}

`when`(billUseCase.getBills()).thenReturn(errorFlow)
Mockito.`when`(billUseCase.getBills()).thenReturn(errorFlow)

val job = launch {
viewModel.triggerFetchBills()
Expand All @@ -91,9 +90,12 @@ class BillViewModelTest {
advanceUntilIdle()
advanceTimeBy(1000)

assertTrue("First state should be Loading", states[0] is FlowState.Loading)
assertTrue("Second state should be Error", states[1] is FlowState.Error && (states[1] as FlowState.Error).message == errorMessage)
TestCase.assertTrue("First state should be Loading", states[0] is FlowState.Loading)
TestCase.assertTrue(
"Second state should be Error",
states[1] is FlowState.Error && (states[1] as FlowState.Error).message == errorMessage
)
job.cancel()
}

}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.ouday.cryptowalletsample
package com.ouday.cryptowalletsample.creditcards

import com.ouday.cryptowalletsample.creditcards.data.model.CreditCardInfo
import com.ouday.cryptowalletsample.creditcards.data.remote.CreditCardApiService
Expand All @@ -8,8 +8,7 @@ import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
import retrofit2.Response

Expand All @@ -23,15 +22,16 @@ class CreditCardRepositoryImpTest {
@Before
fun setUp() = runTest {
MockitoAnnotations.openMocks(this)
creditCardApiService = mock(CreditCardApiService::class.java)
creditCardApiService = Mockito.mock(CreditCardApiService::class.java)
creditCardRepository = CreditCardRepositoryImp(creditCardApiService)

val creditCards = listOf(
CreditCardInfo("VISA", "**** **** **** 3854", "10th Oct", "$5,001.86", "EARLY"),
CreditCardInfo("MASTERCARD", "**** **** **** 1234", "15th Nov", "$3,500.00", "ON TIME"),
CreditCardInfo("AMEX", "**** **** **** 5678", "20th Dec", "$7,250.99", "LATE")
)
`when`(creditCardApiService.getCreditCards()).thenReturn(Response.success(creditCards))
Mockito.`when`(creditCardApiService.getCreditCards())
.thenReturn(Response.success(creditCards))
}

@Test
Expand All @@ -47,8 +47,9 @@ class CreditCardRepositoryImpTest {

@Test(expected = Exception::class)
fun `getCreditCards throws exception on API failure`() = runTest {
`when`(creditCardApiService.getCreditCards()).thenThrow(RuntimeException("Network Error"))
Mockito.`when`(creditCardApiService.getCreditCards())
.thenThrow(RuntimeException("Network Error"))

creditCardRepository.getCreditCards().toList()
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.ouday.cryptowalletsample
package com.ouday.cryptowalletsample.creditcards

import com.ouday.cryptowalletsample.creditcards.data.model.CreditCardInfo
import com.ouday.cryptowalletsample.creditcards.data.repository.CreditCardRepository
Expand All @@ -9,7 +9,7 @@ import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito
import org.mockito.MockitoAnnotations

class CreditCardUseCaseImpTest {
Expand All @@ -27,10 +27,11 @@ class CreditCardUseCaseImpTest {

@Test
fun `getCreditCards returns list of credit cards`() = runTest {
val creditCards = listOf(CreditCardInfo("VISA", "**** **** **** 1234", "10/22", "$1,000", "Paid"))
`when`(creditCardRepository.getCreditCards()).thenReturn(flowOf(creditCards))
val creditCards =
listOf(CreditCardInfo("VISA", "**** **** **** 1234", "10/22", "$1,000", "Paid"))
Mockito.`when`(creditCardRepository.getCreditCards()).thenReturn(flowOf(creditCards))

val result = creditCardUseCase.getCreditCards().toList()
assert(result[0] == creditCards)
}
}
}
Loading

0 comments on commit 01f2b69

Please sign in to comment.