profileShare

rasmusjy / splitapp-backend-clean-onion

Read-only snapshot

No repository description.

main default branch 429 files Expires Sep 13, 2026, 9:06 AM
testing-plan.md 8,702 bytes
1 # Testing — Implemented State
2
3 Aligned with the Akaver testing lecture (`courses.taltech.akaver.com/web-applications-with-csharp/lectures/testing`).
4
5 **Status:** ✅ 44 tests, all green. Local-only via `dotnet test`. **No CI/CD changes** — VPS pipeline untouched.
6
7 ---
8
9 ## Stack (loengust)
10
11 | Tool | Version | Roll |
12 |---|---|---|
13 | **xUnit** | 2.9.3 | Test framework |
14 | **Moq** | 4.20.72 | `IAppUnitOfWork` + repo mocking BLL teenuste jaoks |
15 | **FluentAssertions** | 6.12.2 | Loetavad väited (`result.Should().NotBeNull()`) — pinnitud v6 (täielikult tasuta) |
16 | **Microsoft.EntityFrameworkCore.Sqlite** | 10.0.7 | SQLite in-memory DB DAL testidele (loeng eelistab seda EF InMemory provideri ees) |
17 | **coverlet.collector** | 6.0.4 | Code coverage (vaikimisi mitteaktiivne) |
18
19 ---
20
21 ## Project structure
22
23 ```
24 SplitApp/
25 └── App.Tests/ ← üks test projekt, lisatud SplitApp.sln-i
26 ├── App.Tests.csproj ← references App.DAL.EF, App.BLL, App.Domain
27 ├── RepositoryTestBase.cs ← SQLite in-memory baasklass + SeedTripAsync helper
28 ├── SanityTest.cs ← 1 test
29 ├── Domain/ ← 8 testi
30 │ ├── LangStrTests.cs ← 7 (sealhulgas [Theory] 3 keelega)
31 │ └── TripValidationTests.cs ← 3 (IValidatableObject)
32 ├── DAL/ ← 9 testi (SQLite in-memory)
33 │ ├── TripRepositoryTests.cs ← 2
34 │ ├── ExpenseRepositoryTests.cs ← 1
35 │ ├── BudgetCategoryRepositoryTests.cs ← 1
36 │ ├── TripParticipantRepositoryTests.cs ← 3 (IDOR-relevantne)
37 │ └── TripInvitationRepositoryTests.cs ← 2
38 ├── BLL/ ← 13 testi (mocked UoW + helper)
39 │ ├── TripServiceTests.cs ← 5 (3 happy + 2 sad path)
40 │ ├── BudgetCategoryServiceTests.cs ← 2 (1 happy + 1 sad)
41 │ ├── ExpenseServiceTests.cs ← 2 (happy + edge case)
42 │ ├── SettlementServiceTests.cs ← 2 (edge cases)
43 │ ├── WishlistServiceTests.cs ← 2 (sad + happy)
44 │ └── CurrencyConverterTests.cs ← 5 (sealhulgas [Theory] 3 valuutapaariga)
45 └── Mappers/ ← 6 testi
46 ├── TripBllDtoFactoryTests.cs ← 2 (Create + round-trip)
47 ├── ExpenseBllDtoFactoryTests.cs ← 1
48 ├── BudgetCategoryBllDtoFactoryTests.cs ← 2
49 └── CurrencyBllDtoFactoryTests.cs ← 1 (LangStr säilumine)
50 ```
51
52 ---
53
54 ## Test count summary
55
56 | Kiht | Testid | Mida testib |
57 |---|---|---|
58 | Sanity | 1 | xUnit infra töötab |
59 | **Domain** | 8 | LangStr i18n value object + Trip `IValidatableObject` |
60 | **DAL** | 9 | Repositoorid päris EF + SQLite vastu |
61 | **BLL** | 13 | Teenuste äriloogika + IDOR + edge case-id + helper |
62 | **Mappers** | 6 | Domain ↔ BllDto skalaarsete väljade säilumine |
63 | **KOKKU** | **44** | |
64
65 ---
66
67 ## Coverage matrix — kõik Onion-i ringid testitud
68
69 ```
70 WebApp ──┐
71 ├──► App.BLL ────────────► App.Domain ──► Base.Domain
72 App.DTO ─┘ ✓ TripService ✓ Trip + Validate() ✓ LangStr
73 ✓ ExpenseService ✓ Expense entity ✓ BaseEntity
74 ✓ BudgetCategoryService ✓ BudgetCategory
75 ✓ SettlementService ✓ TripParticipant
76 ✓ WishlistService ✓ TripInvitation
77 ✓ CurrencyConverter ✓ Currency
78 ✓ Mappers (4)
79
80
81 App.DAL.EF ──────┘
82 ✓ TripRepository (2)
83 ✓ ExpenseRepository (1)
84 ✓ BudgetCategoryRepository (1)
85 ✓ TripParticipantRepository (3)
86 ✓ TripInvitationRepository (2)
87 ```
88
89 ---
90
91 ## Mitmekesisuse maatriks (loengu tehnikad)
92
93 | Tehnika | Kus kasutatud |
94 |---|---|
95 | **`[Fact]`** | Kõik klassikalised testid |
96 | **`[Theory]` + `[InlineData]`** (loengust!) | `CurrencyConverterTests.Convert_KnownCurrencies` (3 valuutapaari), `LangStrTests.Translate_ReturnsCorrectValueForKnownCulture` (3 keelt) |
97 | **AAA pattern** | Kõikides testides `// Arrange / Act / Assert` kommentaarid |
98 | **Moq `Setup` + `Returns`** | `_participants.Setup(r => r.IsOrganizerAsync(...)).ReturnsAsync(true)` |
99 | **Moq `Verify` + `Times`** | `_uow.Verify(u => u.SaveChangesAsync(), Times.Never)` (kontrollib, et ei kutsutud) |
100 | **Moq `Callback`** | `WishlistService` test salvestab loodud entity koopia |
101 | **SQLite in-memory** | `RepositoryTestBase` SqliteConnection-iga — FK-d kehtivad |
102 | **FluentAssertions chain** | `.Should().ContainSingle().Which.Id.Should().Be(...)` |
103 | **`BeEquivalentTo` + `Excluding`** | Round-trip testides (mapper) |
104 | **Domain validation** | `Trip.Validate()` `IValidatableObject` |
105 | **i18n value object** | LangStr `Translate` fallback chain (regional → neutral → default) |
106
107 ---
108
109 ## Sad path / IDOR / edge case katvus
110
111 | Kategooria | Test |
112 |---|---|
113 | **IDOR negatiivne** | `TripServiceTests.GetByIdAsync_WhenUserIsNotParticipant_ReturnsNull` |
114 | **IDOR negatiivne** | `TripServiceTests.DeleteAsync_WhenUserIsNotOrganizer_ReturnsFalseAndDoesNotDelete` |
115 | **IDOR negatiivne** | `BudgetCategoryServiceTests.CreateAsync_WhenUserIsNotOrganizer_ReturnsForbiddenAndDoesNotAdd` |
116 | **IDOR negatiivne** | `WishlistServiceTests.GetByTripIdAsync_WhenUserIsNotParticipant_ReturnsEmptyList` |
117 | **IDOR DAL-tasandil** | `TripParticipantRepositoryTests.IsParticipantAsync_WhenUserHasLeft_ReturnsFalse` (IsActive=false) |
118 | **Edge case** | `ExpenseServiceTests.CreateExpenseWithSplitsAsync_EqualAll_WhenNoParticipants_CreatesExpenseButNoSplits` |
119 | **Edge case** | `SettlementServiceTests.CalculateBalancesAsync_WhenNoExpenses_ReturnsZeroBalanceForEachParticipant` |
120 | **Edge case** | `TripValidationTests.Validate_WhenEndDateEqualsStartDate_YieldsNoErrors` (sama päev OK) |
121 | **Edge case** | `CurrencyConverterTests.Convert_UnknownCurrency_FallsBackToOneToOne` |
122 | **Sad path** | `SettlementServiceTests.CalculateBalancesAsync_WhenTripDoesNotExist_ReturnsEmptyList` |
123 | **Sad path** | `TripInvitationRepositoryTests.GetByTokenAsync_WhenTokenDoesNotExist_ReturnsNull` |
124
125 ---
126
127 ## Run commands
128
129 ```bash
130 cd SplitApp
131
132 # Kõik testid
133 dotnet test
134
135 # Kihtide kaupa
136 dotnet test --filter "FullyQualifiedName~Domain" # 8
137 dotnet test --filter "FullyQualifiedName~DAL" # 9
138 dotnet test --filter "FullyQualifiedName~BLL" # 13
139 dotnet test --filter "FullyQualifiedName~Mappers" # 6
140
141 # Üks konkreetne klass
142 dotnet test --filter "FullyQualifiedName~TripServiceTests"
143
144 # Watch mode (uuesti käivitub failimuutusel)
145 dotnet watch test --project SplitApp/App.Tests
146 ```
147
148 ---
149
150 ## What is NOT tested (deliberate)
151
152 - **HTTP/REST integration** (`WebApplicationFactory`) — JWT/HTTP testimine toimub frondist
153 - **`IdentityService`** — `UserManager<AppUser>` mock-imine on tülikas, vähene kasum
154 - **Admin-teenused** — CRUD passthrough, vähene kasum
155 - **EF Core ennast** — Microsoft-i kood, mitte sinu loogika
156 - **Scaffolded Identity Razor pages** — auto-generated
157 - **100% coverage** — loeng hoiatab selle eest, fookus äriloogikal
158
159 ---
160
161 ## Defense-valmidus — õpetaja võimalikud küsimused
162
163 | Küsimus | Vastus |
164 |---|---|
165 | **"Miks SQLite in-memory mitte EF InMemory provider?"** | Loeng eelistab seda — enforcer FK-d ja käitub nagu päris RDBMS |
166 | **"Miks Moq ainult interface-de jaoks?"** | DI muster — concrete klasside mock-imine on anti-pattern |
167 | **"Miks happy path + sad path mitte ainult happy?"** | Testimispüramiidi alus + IDOR on phase 2 nõue, mida tuleb tõestada |
168 | **"Miks `[Theory]` mitte ainult `[Fact]`?"** | Sama loogika, erinevad sisendid → DRY (lecture: parameterized tests) |
169 | **"Mis on AAA pattern?"** | Arrange (setup), Act (kutsu meetodit), Assert (kontrolli) — iga test järgib seda |
170 | **"Miks ei kasuta WebApplicationFactory?"** | HTTP-tasandi testid frondist; backend kontrollib äriloogikat unit-tasemel |
171 | **"Mis on round-trip test?"** | DTO → entity → DTO peab andma sama tulemuse — kontrollib mapperite sümmeetriat |
172 | **"Miks BLL testid kasutavad `Verify(..., Times.Never)`?"** | IDOR-i tõestamiseks — kui kasutajal pole õigust, ei tohi DB-sse Add-i kutsuda |
173
174 ---
175
176 ## CI/CD note (deferred)
177
178 `.gitlab-ci.yml` ei muudeta. Kui hiljem otsustad VPS-i ressursi anda:
179
180 ```yaml
181 test:
182 stage: test
183 image: mcr.microsoft.com/dotnet/sdk:10.0
184 script:
185 - cd SplitApp && dotnet test --no-restore
186 ```
187
188 Aga see on eraldi otsus, mitte selle töö osa.
189