Skip to content

Commit 5be5c3e

Browse files
Merge pull request #98 from CodandoTV/cover/test
[Auto] Add unit tests for craftd-core
2 parents 6cc1d0e + 9adcb67 commit 5be5c3e

19 files changed

Lines changed: 4697 additions & 0 deletions

.claude/skills/android-testing/SKILL.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,42 @@ description: Testing strategies for Android/KMP. Use when creating or reviewing
2020
- Path: `src/test/java/...` espelhando o pacote do arquivo original
2121
- Todo novo componente deve ter teste unitário cobrindo: construção, defaults, `copy()`, `equals/hashCode`, e o `craft()` do builder
2222

23+
## Testes gerados por IA (CI / Claude API)
24+
25+
O workflow de CI pode gerar testes automaticamente via Claude API. Esses testes **frequentemente falham ao compilar ou rodar** e precisam de revisão. Checklist de problemas recorrentes:
26+
27+
### Erros de compilação
28+
29+
| Problema | Sintoma | Fix |
30+
|---|---|---|
31+
| Code fence markdown | Arquivo começa com ` ```kotlin ` | Remover ` ```kotlin ` e ` ``` ` do início/fim |
32+
| MockK: `throws()` solto | `mockk<Foo> { throws(...) }` | Usar `mockk<Foo>(); every { mock.prop } throws ...` |
33+
| `apply { val = }` em data class | `copy().apply { valProp = ... }` | `val` não é mutável — usar `copy(prop = ...)` |
34+
| Enum inexistente | `CraftDAlign.START`, `CraftDTextStyle.REGULAR` | Verificar o enum real e substituir |
35+
| AbstractMap entries incompat. | Override de `entries` com tipo errado | Remover o objeto anônimo se for código morto |
36+
| `assertNotEquals` para ref. | `assertNotEquals(a as Any, b as Any)` quando `a == b` | Usar `assertTrue(a !== b)` |
37+
| Type inference: `assertNotEquals` | Dois tipos diferentes sem parâmetro explícito | `assertNotEquals<Any?>(a, b)` |
38+
39+
### Erros de dependências em `build.gradle.kts`
40+
41+
Para o sourceSet `androidUnitTest` (path `src/test/java/`), garantir:
42+
```kotlin
43+
androidUnitTest.dependencies {
44+
implementation(libs.junit)
45+
implementation(libs.mockk)
46+
implementation(kotlin("test-junit"))
47+
}
48+
```
49+
50+
### Erros de runtime
51+
52+
| Problema | Sintoma | Fix |
53+
|---|---|---|
54+
| Anotações `@Stable`/`@Immutable`/`@Serializable` | `assertTrue(isStable)` falha em runtime | Retenção BINARY — não visível via reflection. Remover o teste |
55+
| Mock de extension function | `MockKException: Missing mocked calls` em `every { stream.bufferedReader() }` | Extension functions Kotlin precisam de `mockkStatic`. Alternativa: usar `ByteArrayInputStream` |
56+
| Jackson `convertValue` com String | `ClassCastException` ao tentar `jsonString.convertToVO<Foo>()` | `convertValue` não parseia JSON string — remover o teste ou usar `readValue` |
57+
| Jackson `convertValue` com List genérica | Retorna `List<LinkedHashMap>` em vez de `List<Foo>` | Genéricos apagados em runtime — remover o teste |
58+
2359
## Screenshot tests (Roborazzi)
2460

2561
```bash

android_kmp/craftd-core/build.gradle.kts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ kotlin {
1010
publishLibraryVariants("release", "debug")
1111
}
1212
sourceSets {
13+
androidUnitTest.dependencies {
14+
implementation(libs.junit)
15+
implementation(libs.mockk)
16+
implementation(kotlin("test-junit"))
17+
}
18+
1319
androidMain.dependencies {
1420
implementation(libs.androidx.core)
1521
implementation(libs.androidx.appcompat)
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
package com.github.codandotv.craftd.androidcore
2+
3+
import androidx.recyclerview.widget.DiffUtil
4+
import com.github.codandotv.craftd.androidcore.data.model.base.SimpleProperties
5+
import io.mockk.every
6+
import io.mockk.mockk
7+
import kotlinx.serialization.json.JsonElement
8+
import kotlinx.serialization.json.JsonPrimitive
9+
import org.junit.Assert.assertFalse
10+
import org.junit.Assert.assertTrue
11+
import org.junit.Test
12+
import org.junit.runner.RunWith
13+
import org.junit.runners.JUnit4
14+
import java.util.AbstractMap
15+
16+
@RunWith(JUnit4::class)
17+
class CraftDSimplePropertiesDiffCallbackTest {
18+
19+
private val callback = CraftDSimplePropertiesItemCallback
20+
21+
@Test
22+
fun `given same key when areItemsTheSame then returns true`() {
23+
val oldItem = SimpleProperties(key = "test_key", value = JsonPrimitive("value1"))
24+
val newItem = SimpleProperties(key = "test_key", value = JsonPrimitive("value2"))
25+
26+
val result = callback.areItemsTheSame(oldItem, newItem)
27+
28+
assertTrue(result)
29+
}
30+
31+
@Test
32+
fun `given different key when areItemsTheSame then returns false`() {
33+
val oldItem = SimpleProperties(key = "key1", value = JsonPrimitive("value"))
34+
val newItem = SimpleProperties(key = "key2", value = JsonPrimitive("value"))
35+
36+
val result = callback.areItemsTheSame(oldItem, newItem)
37+
38+
assertFalse(result)
39+
}
40+
41+
@Test
42+
fun `given exception during key comparison when areItemsTheSame then returns false`() {
43+
val mockOldItem = mockk<SimpleProperties>()
44+
every { mockOldItem.key } throws RuntimeException("Test exception")
45+
val newItem = SimpleProperties(key = "key", value = JsonPrimitive("value"))
46+
47+
val result = callback.areItemsTheSame(mockOldItem, newItem)
48+
49+
assertFalse(result)
50+
}
51+
52+
@Test
53+
fun `given identical primitive values when areContentsTheSame then returns true`() {
54+
val value = JsonPrimitive("same_value")
55+
val oldItem = SimpleProperties(key = "key1", value = value)
56+
val newItem = SimpleProperties(key = "key1", value = value)
57+
58+
val result = callback.areContentsTheSame(oldItem, newItem)
59+
60+
assertTrue(result)
61+
}
62+
63+
@Test
64+
fun `given different primitive values when areContentsTheSame then returns false`() {
65+
val oldItem = SimpleProperties(key = "key1", value = JsonPrimitive("value1"))
66+
val newItem = SimpleProperties(key = "key1", value = JsonPrimitive("value2"))
67+
68+
val result = callback.areContentsTheSame(oldItem, newItem)
69+
70+
assertFalse(result)
71+
}
72+
73+
@Test
74+
fun `given identical AbstractMap values when areContentsTheSame then returns true`() {
75+
val value = JsonPrimitive("same_value")
76+
val oldItem = SimpleProperties(key = "key1", value = value)
77+
val newItem = SimpleProperties(key = "key1", value = value)
78+
79+
val result = callback.areContentsTheSame(oldItem, newItem)
80+
81+
assertTrue(result)
82+
}
83+
84+
@Test
85+
fun `given different AbstractMap values when areContentsTheSame then returns false`() {
86+
val oldItem = SimpleProperties(key = "key1", value = JsonPrimitive("value1"))
87+
val newItem = SimpleProperties(key = "key1", value = JsonPrimitive("value2"))
88+
89+
val result = callback.areContentsTheSame(oldItem, newItem)
90+
91+
assertFalse(result)
92+
}
93+
94+
@Test
95+
fun `given null values when areContentsTheSame then returns true`() {
96+
val oldItem = SimpleProperties(key = "key1", value = JsonPrimitive(null))
97+
val newItem = SimpleProperties(key = "key1", value = JsonPrimitive(null))
98+
99+
val result = callback.areContentsTheSame(oldItem, newItem)
100+
101+
assertTrue(result)
102+
}
103+
104+
@Test
105+
fun `given one null and one non-null value when areContentsTheSame then returns false`() {
106+
val oldItem = SimpleProperties(key = "key1", value = JsonPrimitive(null))
107+
val newItem = SimpleProperties(key = "key1", value = JsonPrimitive("value"))
108+
109+
val result = callback.areContentsTheSame(oldItem, newItem)
110+
111+
assertFalse(result)
112+
}
113+
114+
@Test
115+
fun `given numeric JsonElement values when areContentsTheSame then returns true`() {
116+
val value = JsonPrimitive(42)
117+
val oldItem = SimpleProperties(key = "key1", value = value)
118+
val newItem = SimpleProperties(key = "key1", value = value)
119+
120+
val result = callback.areContentsTheSame(oldItem, newItem)
121+
122+
assertTrue(result)
123+
}
124+
125+
@Test
126+
fun `given boolean JsonElement values when areContentsTheSame then returns true`() {
127+
val value = JsonPrimitive(true)
128+
val oldItem = SimpleProperties(key = "key1", value = value)
129+
val newItem = SimpleProperties(key = "key1", value = value)
130+
131+
val result = callback.areContentsTheSame(oldItem, newItem)
132+
133+
assertTrue(result)
134+
}
135+
136+
@Test
137+
fun `given callback is ItemCallback instance then callback is properly initialized`() {
138+
assertTrue(callback is DiffUtil.ItemCallback<SimpleProperties>)
139+
}
140+
141+
@Test
142+
fun `given different numeric values when areContentsTheSame then returns false`() {
143+
val oldItem = SimpleProperties(key = "key1", value = JsonPrimitive(42))
144+
val newItem = SimpleProperties(key = "key1", value = JsonPrimitive(43))
145+
146+
val result = callback.areContentsTheSame(oldItem, newItem)
147+
148+
assertFalse(result)
149+
}
150+
151+
@Test
152+
fun `given empty AbstractMap when areContentsTheSame with AbstractMap comparison then returns true`() {
153+
val oldItem = SimpleProperties(key = "key1", value = JsonPrimitive("{}"))
154+
val newItem = SimpleProperties(key = "key1", value = JsonPrimitive("{}"))
155+
156+
val result = callback.areContentsTheSame(oldItem, newItem)
157+
158+
assertTrue(result)
159+
}
160+
161+
@Test
162+
fun `given SimpleProperties with all parameters when constructing then all fields are set correctly`() {
163+
val key = "test_key"
164+
val value = JsonPrimitive("test_value")
165+
166+
val simpleProperties = SimpleProperties(key = key, value = value)
167+
168+
assertTrue(simpleProperties.key == key)
169+
assertTrue(simpleProperties.value == value)
170+
}
171+
172+
@Test
173+
fun `given SimpleProperties with copy when copying with different key then new instance has different key`() {
174+
val original = SimpleProperties(key = "original_key", value = JsonPrimitive("value"))
175+
val copied = original.copy(key = "new_key")
176+
177+
assertTrue(copied.key == "new_key")
178+
assertTrue(original.key == "original_key")
179+
}
180+
181+
@Test
182+
fun `given two SimpleProperties with same values when comparing with equals then returns true`() {
183+
val properties1 = SimpleProperties(key = "key", value = JsonPrimitive("value"))
184+
val properties2 = SimpleProperties(key = "key", value = JsonPrimitive("value"))
185+
186+
assertTrue(properties1 == properties2)
187+
}
188+
189+
@Test
190+
fun `given two SimpleProperties with different values when comparing with equals then returns false`() {
191+
val properties1 = SimpleProperties(key = "key1", value = JsonPrimitive("value"))
192+
val properties2 = SimpleProperties(key = "key2", value = JsonPrimitive("value"))
193+
194+
assertFalse(properties1 == properties2)
195+
}
196+
197+
@Test
198+
fun `given two SimpleProperties with same values when comparing hashCode then returns same hash`() {
199+
val properties1 = SimpleProperties(key = "key", value = JsonPrimitive("value"))
200+
val properties2 = SimpleProperties(key = "key", value = JsonPrimitive("value"))
201+
202+
assertTrue(properties1.hashCode() == properties2.hashCode())
203+
}
204+
205+
private class SimpleEntry<K, V>(
206+
override val key: K,
207+
override val value: V
208+
) : Map.Entry<K, V>
209+
210+
}

0 commit comments

Comments
 (0)