|
| 1 | +--- |
| 2 | +layout: default |
| 3 | +codename: Wordle |
| 4 | +title: Test-Driven Wordle klón fejlesztés MI segítségével |
| 5 | +tags: snippets mieset |
| 6 | +authors: Várhegyi Melinda |
| 7 | +--- |
| 8 | + |
| 9 | +# Test-Driven Wordle klón fejlesztés MI segítségével |
| 10 | + |
| 11 | +## A feladat leírása |
| 12 | + |
| 13 | +A feladat egy Wordle szójáték klón elkészítése volt .NET MAUI keretrendszerben, C# nyelven, |
| 14 | +Windows platformra célozva (`net8.0-windows10.0.19041.0`), Visual Studio 2022-ben. |
| 15 | +A Wordle játék lényege röviden: Egy 5 betűs szavakat tartalmazó szólistából sorsolunk egy feladványt, |
| 16 | +melyet a felhasználó megpróbál megfeljebb 6 próbálkozásból kitalálni. Minden beküldött tippjének |
| 17 | +betűit kiértéklejük aszerint, hogy a feladvány tartalmazza-e az adott betűt. |
| 18 | +(Ugyanazon a helyen tartalmazza - zöld, tartalmazza, de másik helyen - sárga, nem tartalmazza - szürke.) |
| 19 | + |
| 20 | +A fejlesztés során használt MI eszközök: |
| 21 | + |
| 22 | +- **GitHub Copilot Chat (Claude Haiku 4.5)** — a kód generálásához és a TDD ciklus végrehajtásához |
| 23 | +- **Claude Sonnet 4.6 (desktop alkalmazás)** — tervezéshez, architektúra döntésekhez, prompt íráshoz és |
| 24 | + hibakereséshez |
| 25 | + |
| 26 | +A projekt MVVM architektúrán alapul, a fejlesztés TDD (Test Driven Development) |
| 27 | +módszerrel történt, xUnit, Moq és FluentAssertions könyvtárak használatával. |
| 28 | + |
| 29 | +--- |
| 30 | + |
| 31 | +## Tanulságok |
| 32 | + |
| 33 | +- **A kontextus átadása kulcsfontosságú.** Minél részletesebb és pontosabb promptot kap a Copilot, |
| 34 | + annál jobb kódot generál. Az egyszerű, rövid kérések általában rossz vagy hiányos eredményt adnak. |
| 35 | +- **A TDD workflow jól kombinálható az MI-vel**, de egyes tesztek megírása emberi feladat marad — |
| 36 | + különösen az összetett edge case-ek esetén (pl. duplikált betűk kezelése a Wordle logikában). |
| 37 | +- **Copilot nem látja a renderelt UI-t**, így vizuális hibák debuggolásában (pl. gomb layout) |
| 38 | + korlátozott a segítsége. Az ilyen problémákat sajátkezűleg kell megoldani. |
| 39 | +- **A Copilot hajlamos hibás architektúra döntéseket javasolni** — pl. rossz target framework, |
| 40 | + felesleges `<RuntimeIdentifier>` a `.csproj`-ban, inkompatibilis NuGet csomagok. |
| 41 | + Ezeket mindig kritikusan kell értékelni. |
| 42 | +- **Az összetett logikai edge case-eket a Copilot nem kezeli helyesen** — a duplikált betűk |
| 43 | + Wordle szabály szerinti kiértékelési logikáját kézzel kellett megírni. |
| 44 | +- **A kontextus elvész** — ha a Copilot Chat ablak bezárul (pl. VS összeomlás vagy fájlok |
| 45 | + áthelyezése után), az összes korábbi kontextus elveszik. Érdemes a kontextus promptot |
| 46 | + külön fájlban tárolni és újra beadni. |
| 47 | +- **Claude Sonnet 4.6 és Copilot (Claude Haiku 4.5) jól kiegészítik egymást** — Claude |
| 48 | +erős volt a tervezésben, architektúrában és hibakeresésben, a Copilot pedig a kód generálásban. |
| 49 | +- **A mutációs tesztelés és az MI jól kombinálható** — a Stryker által talált túlélő |
| 50 | + mutánsokat Copilottal hatékonyan meg lehet ölni, de a promptot pontosan kell |
| 51 | + megfogalmazni: meg kell adni a mutáns pontos helyét és viselkedését. |
| 52 | +- **A Copilot törlési feladatoknál megbízhatatlan** — amikor kódot kellett törölni, |
| 53 | + Copilot váratlan módosításokat végzett más fájlokban is. Törlési feladatokat |
| 54 | + érdemes kézzel elvégezni. |
| 55 | + |
| 56 | +--- |
| 57 | + |
| 58 | +## A megvalósított rendszer |
| 59 | + |
| 60 | +### Projektstruktúra (4 projekt) |
| 61 | + |
| 62 | +``` |
| 63 | +WordleMAUI/ ← .NET MAUI alkalmazás — csak nézetek (XAML) |
| 64 | +WordleMAUI.Core/ ← Class Library — modellek és szolgáltatások (pure C#) |
| 65 | +WordleMAUI.ViewModels/ ← Class Library — GameViewModel (CommunityToolkit.Mvvm) |
| 66 | +WordleMAUI.Tests/ ← xUnit tesztprojekt |
| 67 | +``` |
| 68 | + |
| 69 | +**Projekt hivatkozások:** |
| 70 | +- `WordleMAUI` → `WordleMAUI.ViewModels` → `WordleMAUI.Core` |
| 71 | +- `WordleMAUI.Tests` → `WordleMAUI.ViewModels` és `WordleMAUI.Core` |
| 72 | +- `WordleMAUI.Tests` nem hivatkozik `WordleMAUI`-ra (WinRT inicializálási hiba miatt) |
| 73 | +*Ennek a részleteiről a 3. pontban lesz szó.* |
| 74 | + |
| 75 | +### Főbb komponensek |
| 76 | + |
| 77 | +| Komponens | Leírás | |
| 78 | +|---|---| |
| 79 | +| `LetterState` | enum: Correct, Misplaced, Absent | |
| 80 | +| `GuessResult` | record: char Letter, LetterState State | |
| 81 | +| `WordValidator` | 5 betűs, szótárban szereplő szavak ellenőrzése | |
| 82 | +| `GuessEvaluator` | Betűnkénti kiértékelés, duplikált betűk helyes kezelésével | |
| 83 | +| `GameStateManager` | Játékállapot kezelése (6 kísérlet, nyerés/vesztés) | |
| 84 | +| `WordSelector` | Véletlenszerű szókiválasztás | |
| 85 | +| `GameViewModel` | MVVM ViewModel, összeköti a logikát és a UI-t | |
| 86 | + |
| 87 | +--- |
| 88 | + |
| 89 | +## A munkafolyamat tanulságos részletei |
| 90 | + |
| 91 | +### 1. A TDD workflow MI-vel |
| 92 | + |
| 93 | +A fejlesztés az alábbi ciklust követte minden komponensnél: |
| 94 | + |
| 95 | +``` |
| 96 | +1. Tesztnevek megírása (szándék leírása) |
| 97 | +2. Copilot által tesztek generálása |
| 98 | +3. Copilot javaslatának kritikus értékelése |
| 99 | +4. Tesztek alapján kód generálása |
| 100 | +5. Tesztek futtatása, felmerülő hibák javítása |
| 101 | +6. Összes teszt zöld -> kész a komponens |
| 102 | +``` |
| 103 | + |
| 104 | +Ez a workflow az egyszerűbb komponenseknél (pl. `WordValidator`, `GameStateManager`) |
| 105 | +jól működött. Copilot gyorsan generált helyes implementációt, mivel a tesztek neve |
| 106 | +egyértelműen leírta a szándékot. |
| 107 | + |
| 108 | +**Példa sikeres promptra (Phase 2 implementáció):** |
| 109 | + |
| 110 | +``` |
| 111 | +The unit tests for Phase 2 — Core Domain Layer are written and finalized. |
| 112 | +Now implement the production code to make all tests pass, one class at a time |
| 113 | +in the order below. Do not move to the next class until all tests for the |
| 114 | +current one are green. |
| 115 | +
|
| 116 | +## Rules |
| 117 | +- All strings are uppercase — no case conversion needed in any service |
| 118 | +- Use the exact class and interface names specified below |
| 119 | +- Do not add any methods or properties that are not required by the tests |
| 120 | +- All classes are plain C# with zero UI dependencies |
| 121 | +
|
| 122 | +## Implementation Order |
| 123 | +[...] |
| 124 | +``` |
| 125 | + |
| 126 | +### 2. A duplikált betűk problémája |
| 127 | + |
| 128 | +A `GuessEvaluator` implementálása során a duplikált betűk helyes kezelése |
| 129 | +nem zajlott zökkenőmentesen. A Wordle szabályai szerint: |
| 130 | + |
| 131 | +- Ha a tippben egy betű többször szerepel, mint a feladványban, a felesleges |
| 132 | + előfordulások `Absent` jelölést kapnak |
| 133 | +- A helyes pozíciókat (`Correct`) kell először meghatározni, és csak ezután |
| 134 | + a rosszul pozicionált (`Misplaced`) betűket |
| 135 | + |
| 136 | +A Copilot még részletes magyarázat után sem generált helyes |
| 137 | +duplikált betű logikát. Az ehhez kapcsolódó teszteket végül kézzel kellett megírni. |
| 138 | + |
| 139 | +**Példa edge case teszt (kézzel írva):** |
| 140 | + |
| 141 | +```csharp |
| 142 | + [Fact] |
| 143 | + public void GivenDuplicateLetterInGuess_WhenOnlyOneExistsInSecret_ShouldMarkSecondAsAbsent() |
| 144 | + { |
| 145 | + var guess = "AWAKE"; // Two A's in guess |
| 146 | + var secret = "STARE"; // Only one A in secret at position 2 |
| 147 | +
|
| 148 | + var result = _evaluator.Evaluate(guess, secret); |
| 149 | + |
| 150 | + result.Should().HaveCount(5); |
| 151 | + // A is in STARE at position 2, BUT the other A in guess at position 2 is correct -> Absent |
| 152 | + result[0].State.Should().Be(LetterState.Absent); |
| 153 | + // W is not in STARE -> Absent |
| 154 | + result[1].State.Should().Be(LetterState.Absent); |
| 155 | + // A is in STARE at position 2 (correct position in guess) -> Correct |
| 156 | + result[2].State.Should().Be(LetterState.Correct); |
| 157 | + // K is not in STARE -> Absent |
| 158 | + result[3].State.Should().Be(LetterState.Absent); |
| 159 | + // E is in STARE at position 4 (correct position in guess) -> Correct |
| 160 | + result[4].State.Should().Be(LetterState.Correct); |
| 161 | + } |
| 162 | +``` |
| 163 | + |
| 164 | +**Tanulság:** Az összetett üzleti logikát tartalmazó teszteket nem érdemes |
| 165 | +a Copilotra bízni. Az ilyen tesztek megírása emberi feladat, a Copilot feladata az |
| 166 | +implementáció generálása a tesztek alapján. |
| 167 | + |
| 168 | +### 3. Architektúra probléma — WinRT hiba a tesztek futtatásakor |
| 169 | + |
| 170 | +Az első megközelítésben a tesztprojekt közvetlenül hivatkozott a MAUI projektre. |
| 171 | +Ez `System.TypeInitializationException` hibát okozott, mert a MAUI projekt |
| 172 | +betölti a WinRT runtime-ot, ami nem elérhető a tesztfuttató környezetben. |
| 173 | + |
| 174 | +**Copilot javasolta megoldás:** A tesztprojekt target frameworkjét `net8.0`-ra |
| 175 | +állítani — ez hibás volt, mert a framework eltérése miatt a projekthivatkozás |
| 176 | +sem működött. |
| 177 | + |
| 178 | +**Helyes megoldás:** Külön osztálykönyvtár projektek |
| 179 | +létrehozása (`WordleMAUI.Core` és `WordleMAUI.ViewModels`), amelyek nem |
| 180 | +húzzák be a WinRT függőségeket. A tesztprojekt csak ezekre hivatkozik, |
| 181 | +a MAUI projektre nem. |
| 182 | + |
| 183 | +**Tanulság:** A Copilot hajlamos a legegyszerűbb technikai megoldást javasolni, |
| 184 | +ami nem mindig az architektúrailag helyes döntés. Az ilyen döntéseknél emberi |
| 185 | +felülvizsgálat szükséges. |
| 186 | + |
| 187 | +### 4. Projekt konfiguráció hibák — .csproj problémák |
| 188 | + |
| 189 | +Copilot több hibás `.csproj` módosítást javasolt: |
| 190 | + |
| 191 | +| Copilot javaslata | Probléma | Helyes megoldás | |
| 192 | +|---|---|---| |
| 193 | +| `<TargetFramework>net8.0</TargetFramework>` a tesztprojektben | Framework eltérés, hivatkozás nem működik | `net8.0-windows10.0.19041.0` | |
| 194 | +| `<RuntimeIdentifier>win10-x64</RuntimeIdentifier>` | Felesleges, projekt hivatkozási problémákat okoz | Törlés | |
| 195 | +| `CommunityToolkit.Maui` NuGet csomag | Verzió inkompatibilitás | Csomag eltávolítása | |
| 196 | + |
| 197 | +**Tanulság:** A `.csproj` fájlokat mindig kritikusan kell átnézni, mielőtt |
| 198 | +elfogadjuk a Copilot javaslatát. Ha valami furcsának tűnik, valószínűleg az is. |
| 199 | + |
| 200 | +### 5. A kontextus prompt fontossága |
| 201 | + |
| 202 | +Mivel a Copilot Chat nem őrzi meg a kontextust munkamenetek között (pl. VS |
| 203 | +összeomlás, fájlok áthelyezése után), részletes kontextus promptot kellett készíteni, |
| 204 | +amelyet minden új munkamenet elején be kellett adni. Ez tartalmazta: |
| 205 | + |
| 206 | +- A projekt teljes struktúráját |
| 207 | +- Az összes projekt hivatkozást |
| 208 | +- A már implementált komponensek interfészeit |
| 209 | +- A fejlesztési konvenciókat (csak nagybetűs szavak, nincs `CommunityToolkit.Maui` stb.) |
| 210 | +- Az aktuális fázist és feladatot |
| 211 | + |
| 212 | +**Példa kontextus prompt részlet:** |
| 213 | + |
| 214 | +``` |
| 215 | +You are helping me build a Wordle clone called WordleMAUI using .NET MAUI |
| 216 | +targeting net8.0-windows10.0.19041.0 in Visual Studio 2022 with .NET 8.0. |
| 217 | +[...] |
| 218 | +Note: CommunityToolkit.Maui is NOT installed due to version incompatibility. |
| 219 | +Do not suggest or use any APIs from it. |
| 220 | +[...] |
| 221 | +We follow strict TDD: tests are always written before implementation. |
| 222 | +Never generate implementation before tests unless I ask. |
| 223 | +``` |
| 224 | + |
| 225 | +**Tanulság:** Minél pontosabb és részletesebb a kontextus prompt, annál kevesebb |
| 226 | +hibás javaslatot ad a Copilot. A negatív megszorítások (pl. "ne használd ezt a |
| 227 | +csomagot") különösen fontosak. |
| 228 | + |
| 229 | +### 6. A UI fejlesztés kihívásai |
| 230 | + |
| 231 | +A MAUI XAML UI generálásánál a Copilot hasznos volt az alapstruktúra elkészítésében, |
| 232 | +de a vizuális hibák javításában korlátozott segítséget nyújtott, mivel nem látja |
| 233 | +a renderelt eredményt. |
| 234 | + |
| 235 | +**Konkrét példa — billentyűzet layout:** |
| 236 | +A virtuális billentyűzet gombjainak helyes elhelyezése több lépésen keresztül tartott: |
| 237 | +- Copilot egymás után javasolt fix pixel szélességeket, padding és margin értékeket |
| 238 | +- Ezek egyike sem oldotta meg a problémát |
| 239 | +- A végső megoldás a MAUI `Button` vezérlő `MinimumWidthRequest="0"` tulajdonsága |
| 240 | + volt |
| 241 | + |
| 242 | +**Tanulság:** A vizuális UI debuggolás emberi feladat. A Copilot tud XAML kódot |
| 243 | +generálni, de a renderelt eredményt nem látja, ezért vizuális javításra |
| 244 | +nem feltétlenül alkalmas. |
| 245 | + |
| 246 | + |
| 247 | +### 7. Mutációs tesztelés Stryker.NET-tel |
| 248 | + |
| 249 | +A fejlesztés végén mutációs tesztelést végeztünk a Stryker.NET eszközzel (v4.14.0) |
| 250 | +a `WordleMAUI.Core` projekt tesztelési minőségének mérésére. |
| 251 | + |
| 252 | +**A mutációs tesztelés folyamata:** |
| 253 | +A Stryker apró változtatásokat ("mutációkat") végez a forráskódon, majd lefuttatja |
| 254 | +a teszteket. Ha egy mutáció nem buktatja el a teszteket, az azt jelenti, hogy |
| 255 | +a tesztek nem fedik le megfelelően azt a logikát — ez egy túlélő mutáns. |
| 256 | + |
| 257 | +**A tesztelés eredményei:** |
| 258 | + |
| 259 | +- **Első futtatás: 74.00%** |
| 260 | +- **`SelectByDate` eltávolítása után: 80.43%** |
| 261 | + A `SelectByDate` egy olyan teszt volt, amely a TDD fejlesztés során megírásra került, |
| 262 | + viszont később ez a funkció mégsem került implementálásra a fejlesztő döntése miatt. |
| 263 | + Így ennek a tesztnek az eltávolítása rögtön növelte a mutáció score-t. |
| 264 | +- **`GameStateManager` tesztek után: 89.13%** |
| 265 | + A hibák abból eredtek, hogy nem volt lekezelve az, ha az egyes függvéynek |
| 266 | + bemeneti paraméterül null-t kapnak. |
| 267 | +- **`WordSelector` tesztek után: 93.48%** |
| 268 | + A `wordList` konstruktorában null argumentum kezelése. |
| 269 | +- **`WordValidator` + `GuessEvaluator` tesztek után: 100.00%** |
| 270 | + `WordValidator` - null kezelése. `GuessEvaluator` - Ha egy betűt már megjelöltünk |
| 271 | + Correct-ként, akkor az ő helye `processed[i] = true` jelölést kap. A mutáns ezt false-ra |
| 272 | + állította, ekkor a betű a következő iterációban újra kiértékelésre került Misplaced |
| 273 | + vagy Absent-ként, ami hibás, ezt kellett lekezelni. |
| 274 | + |
| 275 | +**A Stryker konfigurálásának nehézségei:** |
| 276 | +A Stryker beüzemelése számos kihívással járt, amelyek mind a `.csproj` konfiguráció |
| 277 | +sajátosságaiból eredtek: |
| 278 | +- A `stryker-config.json` helyes formátumát Copilot rosszul generálta |
| 279 | +- A `console` reporter neve Stryker 4.14.0-ban `cleartext` — ezt is tévesen adta meg |
| 280 | +- A `mutate` path filter nem működött relatív útvonalakkal, csak `**/*.cs` mintával |
| 281 | +- A `LinqMutator` érvénytelen kódot generált `List<string>.Count` mutációjakor, |
| 282 | + amit az `ignore-mutations: ["linq"]` beállítással kellett kizárni |
| 283 | + |
| 284 | +**A Stryker + Copilot workflow:** |
| 285 | +A leghasznosabb megközelítésnek az bizonyult, amikor a Stryker riportból azonosított túlélő |
| 286 | +mutánsokat Copilottal öltük meg. A workflow: |
| 287 | + |
| 288 | +``` |
| 289 | +1. Stryker futtatása → HTML riport generálása |
| 290 | +2. Túlélő mutáns azonosítása a riportban (fájl + sor + mutáció típusa) |
| 291 | +3. Copilot Chat: pontos prompt a mutáns megöléséhez szükséges teszttel |
| 292 | +4. Teszt hozzáadása → Stryker újrafuttatása → score javulás ellenőrzése |
| 293 | +``` |
| 294 | + |
| 295 | +**Tanulság:** A mutációs tesztelés hasznos eszköz a tesztek minőségének mérésére, |
| 296 | +de a konfigurálása összetett és eszközismeretet igényel. A Copilot segíthet |
| 297 | +a tesztek generálásában, de a mutáns azonosítása és a prompt pontos |
| 298 | +megfogalmazása emberi feladat marad. |
| 299 | + |
| 300 | +**Megjegyzés:** |
| 301 | +A 100%-os mutáció score azt jelenti, hogy minden meglevő kódsor tesztelve |
| 302 | +van — de nem jelenti azt, hogy a szoftver helyes. A Stryker csak a meglévő |
| 303 | +kódot tudja mutálni, hiányzó funkciókat vagy hibás üzleti logikát nem képes |
| 304 | +detektálni. Jó példa erre a duplikált betűk kezelése: ha a `GuessEvaluator` |
| 305 | +eleve rossz algoritmust tartalmazott volna, a Stryker azt nem találta volna |
| 306 | +meg — csak a kézzel megírt, üzleti logikán alapuló tesztek tudták garantálni |
| 307 | +a helyes viselkedést. |
| 308 | + |
| 309 | +--- |
| 310 | + |
| 311 | +## Összefoglalás |
| 312 | + |
| 313 | +A TDD és az MI eszközök kombinációja hatékony fejlesztési módszernek bizonyult, |
| 314 | +de fontos tanulság, hogy az MI nem helyettesíti a fejlesztői tudást — inkább |
| 315 | +egy erős asszisztens, amely felgyorsítja a munkát, de folyamatos emberi |
| 316 | +felügyeletet igényel. |
| 317 | + |
| 318 | +A legjobb eredményt akkor értük el, amikor: |
| 319 | +1. A fejlesztő írta meg a tesztek leírását |
| 320 | + (NEM az MI dönti el, hogy milyen tesztesetekre lesz szükségünk) |
| 321 | +2. A teszteket vagy MI generálta és alaposan ellenőrizte őket a fejlesztő, |
| 322 | + vagy maga a fejlesztő írta |
| 323 | +3. A Copilot legenerálta az implementációt a tesztek alapján |
| 324 | +4. A fejlesztő itt is kritikusan értékelte és szükség esetén javította a javaslatot |
| 325 | +5. Claude segített az összetettebb promptok megírásában és a nehezebb hibák azonosításában |
| 326 | + |
| 327 | +**Szerző:** Várhegyi Melinda |
| 328 | + |
0 commit comments