/IheNkiri

This project demonstrates latest tools and trends in Android application development using TMDB and a free figma design. A personal playground for Android and Latest Android Trends.

Primary LanguageKotlinMIT LicenseMIT

IheNkiri

CI Build codecov Maintainability

This project demonstrates the latest tools and trends in Modern Android application development using The Movie Db API and Free Figma Design Template.

Features

  • Generates and upload Coverage report CodeCov using Jacoco Coverage report plugin
  • Enforcing code coverage metrics using Jacoco Coverage Verification

Screenshots

Development Environment

Build

IheNkiri app contains the usual debug and release build variants.

It also contains the androidTest module with the android test plugin: com.android.test used to run all the Instrumented tests, or tests under the androidTest folder for all the modules. It targets the the app module and other library module. The advantage is that we get to run all the android tests from a single entry point and avoiding stating the emulator each time we change a module which can be very slow. This is very optional, we are already considering running all the UI tests using robolectric instead of a real device or an emulator

Built with

  • Kotlin - Official programming language for Android development.
  • Jetpack Compose - Modern UI toolkit for Android.
  • Splash Screen API - Lets Ihenkiri app launch wit a nice and customisable animation.
  • Coroutines - For asynchronous or non-blocking programming.
    • Flow An asynchronous data stream that sequentially emits values and completes normally or with an exception. This is used to communicate b/w the Date layers and the UI layers.
    • StateFlow and SharedFlow - StateFlow and SharedFlow are Flow APIs that enable flows to optimally emit state updates and emit values to multiple consumers. StateFlow or SharedFlow is used together with ViewModel to realize an observable StateHolder that is not directly coupled with the UI. The UI layer observes changes in the StateHolder (ViewModel) and react to them.
  • Android Architecture Components - Collection of libraries that help you design robust, testable, and maintainable apps.
    • ViewModel - Stores UI-related data that isn't destroyed on UI changes.
  • Dagger-Hilt Dependency Injection -
    • Hilt-Dagger - Standard library to incorporate Dagger dependency injection into an Android application.
    • Hilt-ViewModel - DI for injecting ViewModel.
  • GSON - A modern JSON library for Kotlin and Java.
  • Retrofit - A type-safe HTTP client for Android and Java.
  • Coil - An image loading library for Android backed by Kotlin Coroutines.
  • Desugar - Allows us to use Java 8 language features and APIs.
  • Android Jetpack - A suite of libraries to help developers follow best practices, reduce boilerplate code, and write code that works consistently across Android versions and devices so that developers can focus on the code they care about.
    • Browser - Helps us webpages in the user's default browser. Used primarily to complete the OAuth 2.0 sign in flow using the Movies Db Api.
  • [Accompanist] Accompanist is a group of libraries that aim to supplement Jetpack Compose with features that are commonly required by developers but not yet available
    • System UI Controller - used to go edge-to-edge in your activity and change the system bar colors and system bar icon colors, use the new
    • Test Harness - A library providing a test harness for UI components.

Testing Libraries

  • TestParameterInjector - A simple yet powerful parameterized test runner for Java.
  • Roborazzi - helps to validate the app's appearance and functionality
  • Robolectric - a framework that brings fast and reliable unit tests to Android
  • Robolectric Shadows - use shadow objects to simulate Android behavior in a non-Android environment.

Performance and Qaulity

  • ProfileInstaller - Enables libraries to prepopulate ahead of time compilation traces to be read by ART
  • JankStats Library - The JankStats library helps you track and analyze performance problems in your applications

Modularization

IheNkiri is completely modularised based on the approach using in Now in Android repo. We have mainly core and feature packages holding the core and feature of the app. In addition, we have the androidTest and screenshotTest modules for running Instrumented Test and Screenshot test.

For more information, Check Here

Code Quality

To guarantee code quality across pull request and/or from different contributors, we have integrated detekt and ktlint.

./gradlew detekt ktlint 

Alternatively, if you have ktlint installed locally, you can also run the following command:

ktlint -F

Testing

For efficient testing of all components, IheNkiri uses Hilt to inject test components during tests.

Most of the the data layer components are defined as interfaces with various concrete implementations which provides these interfaces during tests e.g (DefaultXRepository,TestXRepository) -> Repository

During testing, we have a mix of Mock and Fake. Mocks are used when convenient and we are considering removing all mocks so that the testing would depended only on Fakes. We use the Hilt Testing APIs to replace modules/components during testing as shown below:

@Module
@TestInstallIn(
    components = [SingletonComponent::class],
    replaces = [DispatchersModule::class],
)
object TestDispatchersModule {}

We also use manual constructor injection for testing ViewModels when necessary.

Using Test Doubles (Fake) is generally encouraged as better than mocks as they implement the same interface as the production code and also provides simplified (but still realistic) implementation with additional testing hooks. This results in less brittle tests that may exercise more production code quality, instead of just verifying specific calls against mocks.

[!IMPORTANT]

Test naming convention

IheNkiri uses the thingUnderTest_TriggerOfTest_ResultOfTest format to name the test function name:

thingUnderTest e.g moviesViewModel

TriggerOfTest e.g OnEvent

ResultOfTest e.g CorrectFilterIsSet

Example: moviesViewModel_OnEvent_CorrectFilterIsSet()

Generate Lint baseline

./gradlew lintDebug -Dlint.baselines.continue=true

We have the following tests on IheNkiri

  • Unit tests - Runs all the local JVM tests, this can be invoked as follows:
./gradlew testDebugUnitTest
  • Instrumentation tests - Runs all the instrumentation tests (tests that requires physical device or emulators). Currently, we have all these test in the androidTest module and can eb run using the following command
./gradlew :androidTest:connectedDebugAndroidTest
  • Screenshot tests (Coming soon) - This is a new form of tests that takes screenshots of Design System components and other components, save them, and subsequently verifies them against new records.This helps detect obvious changes in the Pixels which might indicate a deviation in the core dependencies or something the developer needs to take care of. This has been said to tbe fast and better than doing multiple assertions for a given UI component. The screenshot tests lives in the screenshotTest module and can be invoked using the following command:

Compose

Compose debugging

./gradlew assembleDebugRelease -PcomposeCompilerReports=true

Generate screenshots

./gradlew recordRoborazziDebug

Compare screenshots

./gradlew compareRoborazziDebug

Verify screenshots

./gradlew verifyRoborazziDebug

Verify & record screenshots

./gradlew verifyAndRecordRoborazziDebug

We use Roborazzi to achieve this screenshot testing.

Performance & Quality

App uses Jank Stats and ProfilerInstaller for performance checks When delivering through Google Play, the baseline profile is compiled during installation. In this case you will see the correct state logged without any further action necessary. To verify baseline profile installation locally, you need to manually trigger baseline profile installation.

For immediate compilation, call:

adb shell cmd package compile -f -m speed-profile me.jerryokafor.ihenkiri

You can also trigger background optimizations:

adb shell pm bg-dexopt-job

Both jobs run asynchronously and might take some time complete. To see quick turnaround of the ProfileVerifier, we recommend using speed-profile. If you don't do either of these steps, you might only see the profile status reported as "enqueued for compilation" when running the sample locally.

Generate the Baseline Profile

./gradlew :app:generateBaselineProfile

Architecture

Todo

  • Performance (Baseline Profile + R8)
  • Different Screen Sizes

References

License

IheNkiri is didtributed under the terms of the MIT License. See the License for more details.