Testing — Implemented State
Aligned with the Akaver testing lecture (courses.taltech.akaver.com/web-applications-with-csharp/lectures/testing).
Status: ✅ 44 tests, all green. Local-only via dotnet test. No CI/CD changes — VPS pipeline untouched.
Stack (loengust)
| Tool |
Version |
Roll |
| xUnit |
2.9.3 |
Test framework |
| Moq |
4.20.72 |
IAppUnitOfWork + repo mocking BLL teenuste jaoks |
| FluentAssertions |
6.12.2 |
Loetavad väited (result.Should().NotBeNull()) — pinnitud v6 (täielikult tasuta) |
| Microsoft.EntityFrameworkCore.Sqlite |
10.0.7 |
SQLite in-memory DB DAL testidele (loeng eelistab seda EF InMemory provideri ees) |
| coverlet.collector |
6.0.4 |
Code coverage (vaikimisi mitteaktiivne) |
Project structure
SplitApp/
└── App.Tests/ ← üks test projekt, lisatud SplitApp.sln-i
├── App.Tests.csproj ← references App.DAL.EF, App.BLL, App.Domain
├── RepositoryTestBase.cs ← SQLite in-memory baasklass + SeedTripAsync helper
├── SanityTest.cs ← 1 test
├── Domain/ ← 8 testi
│ ├── LangStrTests.cs ← 7 (sealhulgas [Theory] 3 keelega)
│ └── TripValidationTests.cs ← 3 (IValidatableObject)
├── DAL/ ← 9 testi (SQLite in-memory)
│ ├── TripRepositoryTests.cs ← 2
│ ├── ExpenseRepositoryTests.cs ← 1
│ ├── BudgetCategoryRepositoryTests.cs ← 1
│ ├── TripParticipantRepositoryTests.cs ← 3 (IDOR-relevantne)
│ └── TripInvitationRepositoryTests.cs ← 2
├── BLL/ ← 13 testi (mocked UoW + helper)
│ ├── TripServiceTests.cs ← 5 (3 happy + 2 sad path)
│ ├── BudgetCategoryServiceTests.cs ← 2 (1 happy + 1 sad)
│ ├── ExpenseServiceTests.cs ← 2 (happy + edge case)
│ ├── SettlementServiceTests.cs ← 2 (edge cases)
│ ├── WishlistServiceTests.cs ← 2 (sad + happy)
│ └── CurrencyConverterTests.cs ← 5 (sealhulgas [Theory] 3 valuutapaariga)
└── Mappers/ ← 6 testi
├── TripBllDtoFactoryTests.cs ← 2 (Create + round-trip)
├── ExpenseBllDtoFactoryTests.cs ← 1
├── BudgetCategoryBllDtoFactoryTests.cs ← 2
└── CurrencyBllDtoFactoryTests.cs ← 1 (LangStr säilumine)
Test count summary
| Kiht |
Testid |
Mida testib |
| Sanity |
1 |
xUnit infra töötab |
| Domain |
8 |
LangStr i18n value object + Trip IValidatableObject |
| DAL |
9 |
Repositoorid päris EF + SQLite vastu |
| BLL |
13 |
Teenuste äriloogika + IDOR + edge case-id + helper |
| Mappers |
6 |
Domain ↔ BllDto skalaarsete väljade säilumine |
| KOKKU |
44 |
|
Coverage matrix — kõik Onion-i ringid testitud
WebApp ──┐
├──► App.BLL ────────────► App.Domain ──► Base.Domain
App.DTO ─┘ ✓ TripService ✓ Trip + Validate() ✓ LangStr
✓ ExpenseService ✓ Expense entity ✓ BaseEntity
✓ BudgetCategoryService ✓ BudgetCategory
✓ SettlementService ✓ TripParticipant
✓ WishlistService ✓ TripInvitation
✓ CurrencyConverter ✓ Currency
✓ Mappers (4)
▲
│
App.DAL.EF ──────┘
✓ TripRepository (2)
✓ ExpenseRepository (1)
✓ BudgetCategoryRepository (1)
✓ TripParticipantRepository (3)
✓ TripInvitationRepository (2)
Mitmekesisuse maatriks (loengu tehnikad)
| Tehnika |
Kus kasutatud |
[Fact] |
Kõik klassikalised testid |
[Theory] + [InlineData] (loengust!) |
CurrencyConverterTests.Convert_KnownCurrencies (3 valuutapaari), LangStrTests.Translate_ReturnsCorrectValueForKnownCulture (3 keelt) |
| AAA pattern |
Kõikides testides // Arrange / Act / Assert kommentaarid |
Moq Setup + Returns |
_participants.Setup(r => r.IsOrganizerAsync(...)).ReturnsAsync(true) |
Moq Verify + Times |
_uow.Verify(u => u.SaveChangesAsync(), Times.Never) (kontrollib, et ei kutsutud) |
Moq Callback |
WishlistService test salvestab loodud entity koopia |
| SQLite in-memory |
RepositoryTestBase SqliteConnection-iga — FK-d kehtivad |
| FluentAssertions chain |
.Should().ContainSingle().Which.Id.Should().Be(...) |
BeEquivalentTo + Excluding |
Round-trip testides (mapper) |
| Domain validation |
Trip.Validate() IValidatableObject |
| i18n value object |
LangStr Translate fallback chain (regional → neutral → default) |
Sad path / IDOR / edge case katvus
| Kategooria |
Test |
| IDOR negatiivne |
TripServiceTests.GetByIdAsync_WhenUserIsNotParticipant_ReturnsNull |
| IDOR negatiivne |
TripServiceTests.DeleteAsync_WhenUserIsNotOrganizer_ReturnsFalseAndDoesNotDelete |
| IDOR negatiivne |
BudgetCategoryServiceTests.CreateAsync_WhenUserIsNotOrganizer_ReturnsForbiddenAndDoesNotAdd |
| IDOR negatiivne |
WishlistServiceTests.GetByTripIdAsync_WhenUserIsNotParticipant_ReturnsEmptyList |
| IDOR DAL-tasandil |
TripParticipantRepositoryTests.IsParticipantAsync_WhenUserHasLeft_ReturnsFalse (IsActive=false) |
| Edge case |
ExpenseServiceTests.CreateExpenseWithSplitsAsync_EqualAll_WhenNoParticipants_CreatesExpenseButNoSplits |
| Edge case |
SettlementServiceTests.CalculateBalancesAsync_WhenNoExpenses_ReturnsZeroBalanceForEachParticipant |
| Edge case |
TripValidationTests.Validate_WhenEndDateEqualsStartDate_YieldsNoErrors (sama päev OK) |
| Edge case |
CurrencyConverterTests.Convert_UnknownCurrency_FallsBackToOneToOne |
| Sad path |
SettlementServiceTests.CalculateBalancesAsync_WhenTripDoesNotExist_ReturnsEmptyList |
| Sad path |
TripInvitationRepositoryTests.GetByTokenAsync_WhenTokenDoesNotExist_ReturnsNull |
Run commands
cd SplitApp
# Kõik testid
dotnet test
# Kihtide kaupa
dotnet test --filter "FullyQualifiedName~Domain" # 8
dotnet test --filter "FullyQualifiedName~DAL" # 9
dotnet test --filter "FullyQualifiedName~BLL" # 13
dotnet test --filter "FullyQualifiedName~Mappers" # 6
# Üks konkreetne klass
dotnet test --filter "FullyQualifiedName~TripServiceTests"
# Watch mode (uuesti käivitub failimuutusel)
dotnet watch test --project SplitApp/App.Tests
What is NOT tested (deliberate)
- HTTP/REST integration (
WebApplicationFactory) — JWT/HTTP testimine toimub frondist
IdentityService — UserManager<AppUser> mock-imine on tülikas, vähene kasum
- Admin-teenused — CRUD passthrough, vähene kasum
- EF Core ennast — Microsoft-i kood, mitte sinu loogika
- Scaffolded Identity Razor pages — auto-generated
- 100% coverage — loeng hoiatab selle eest, fookus äriloogikal
Defense-valmidus — õpetaja võimalikud küsimused
| Küsimus |
Vastus |
| "Miks SQLite in-memory mitte EF InMemory provider?" |
Loeng eelistab seda — enforcer FK-d ja käitub nagu päris RDBMS |
| "Miks Moq ainult interface-de jaoks?" |
DI muster — concrete klasside mock-imine on anti-pattern |
| "Miks happy path + sad path mitte ainult happy?" |
Testimispüramiidi alus + IDOR on phase 2 nõue, mida tuleb tõestada |
"Miks [Theory] mitte ainult [Fact]?" |
Sama loogika, erinevad sisendid → DRY (lecture: parameterized tests) |
| "Mis on AAA pattern?" |
Arrange (setup), Act (kutsu meetodit), Assert (kontrolli) — iga test järgib seda |
| "Miks ei kasuta WebApplicationFactory?" |
HTTP-tasandi testid frondist; backend kontrollib äriloogikat unit-tasemel |
| "Mis on round-trip test?" |
DTO → entity → DTO peab andma sama tulemuse — kontrollib mapperite sümmeetriat |
"Miks BLL testid kasutavad Verify(..., Times.Never)?" |
IDOR-i tõestamiseks — kui kasutajal pole õigust, ei tohi DB-sse Add-i kutsuda |
CI/CD note (deferred)
.gitlab-ci.yml ei muudeta. Kui hiljem otsustad VPS-i ressursi anda:
test:
stage: test
image: mcr.microsoft.com/dotnet/sdk:10.0
script:
- cd SplitApp && dotnet test --no-restore
Aga see on eraldi otsus, mitte selle töö osa.