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

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
  • IdentityServiceUserManager<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.