3. **Flat Assertions**: Avoid nesting `flow.test` inside other test flows to prevent variable type collision and compiler ambiguity. Check state properties directly via `viewModel.state.value` or in separate test cases.
Resources
6Install
npx skillscat add yusefellban/nutriscan Install via the SkillsCat registry.
SKILL.md
AI Agent Development Skills & Best Practices
This document defines non-negotiable coding and testing guidelines for all AI agents working on NutriScan AI.
1. Feature Planning Standard
Before writing any code or making workspace edits for a new feature:
- Conduct research on the requirements and codebase design.
- Create a comprehensive implementation plan inside the
docs/plans/folder.- Name the file using the pattern:
YYYY-MM-DD-feature-name.md. - The plan must detail Goal Description, User Review Required, Proposed Changes (broken down by modules), and a Verification Plan (both automated and manual).
- Name the file using the pattern:
2. Localization & String Safety
- No Hardcoded User-Facing Strings: Under no circumstances should hardcoded string literals be used in Composable elements (e.g.,
Text("Next")). - Standard Implementation:
- Add English string keys to
presentation/src/main/res/values/strings.xml. - Add Arabic translations to
presentation/src/main/res/values-ar/strings.xml. - Access strings in Composables via
stringResource(id = R.string.key). - Ensure dynamic strings utilize string formatting placeholders (e.g.
Hello, %1$s!).
- Add English string keys to
3. Theming & Light/Dark Mode Compatibility
- No Hardcoded Color Hexes: Under no circumstances should custom colors be hardcoded in UI components (e.g.,
Color(0xFF00FF00)). - Theme Coupling:
- Always use
AppTheme.colors.NameorMaterialTheme.colorScheme.namefor colors. - Using semantic names ensures elements render beautifully and remain readable during theme switching.
- Always use
4. ViewModel Unit Testing Pattern
All feature ViewModels must be thoroughly unit tested using JUnit 5, Turbine, and Kotlin Coroutines Test.
Standard Test Template
package iti.grad.nutriscan.presentation.feature
import app.cash.turbine.test
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestCoroutineScheduler
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@OptIn(ExperimentalCoroutinesApi::class)
class FeatureViewModelTest {
private val testScheduler = TestCoroutineScheduler()
private val testDispatcher = StandardTestDispatcher(testScheduler)
private lateinit var viewModel: FeatureViewModel
@BeforeEach
fun setUp() {
Dispatchers.setMain(testDispatcher)
viewModel = FeatureViewModel()
}
@AfterEach
fun tearDown() {
Dispatchers.resetMain()
}
@Test
fun `when action triggered, state is updated correctly`() = runTest(testDispatcher) {
viewModel.onEvent(FeatureEvent.Action)
assertEquals(ExpectedValue, viewModel.state.value.property)
}
@Test
fun `when action triggered, flow effect is emitted`() = runTest(testDispatcher) {
viewModel.effect.test {
viewModel.onEvent(FeatureEvent.Action)
assertEquals(FeatureEffect.ExpectedEffect, awaitItem())
}
}
}Guidelines for Writing Tests
- Dispatcher Scoping: Use
StandardTestDispatcher(testScheduler)and assign it as main dispatcher in@BeforeEachand reset in@AfterEach. - Turbine Flow Collection: Use
flow.test { ... }block to assert flow emission events. - Flat Assertions: Avoid nesting
flow.testinside other test flows to prevent variable type collision and compiler ambiguity. Check state properties directly viaviewModel.state.valueor in separate test cases.