Skip to content

About

Projects for the Software Testing course in University of Tehran, Fall 2024

Resources

Stars

0 stars

Watchers

1 watching

Forks

Latest commit

 

History

70 Commits

Folders and files

Repository files navigation

Software Testing · Course Projects

University of Tehran — Software Testing (SWT), Fall 1403 / 2024 Instructor: Dr. Ehsan Khamespanah

Six assignments that walk one codebase from a first assertEquals all the way to recorded browser scenarios — stopping at mocks, control-flow graphs, logic criteria, mutants and a CI pipeline along the way.


Java Spring Boot JUnit5 Maven GitHub Actions

Mockito JaCoCo PIT Selenium Cucumber


The arc

Each assignment adds one layer of testing theory on top of the last. Same systems, deeper questions.

flowchart LR
    A["CA1<br/>Unit Testing<br/>JUnit 5"]
    B["CA2<br/>Test Doubles<br/>Mockito"]
    C["CA3<br/>Graph Coverage<br/>JaCoCo"]
    D["CA4<br/>Logic &amp; ISP<br/>MockMvc"]
    E["CA5<br/>Mutation + CI<br/>PIT · Actions"]
    F["CA6<br/>GUI &amp; BDD<br/>Katalon · Cucumber"]

    A --> B --> C --> D --> E --> F

    classDef s1 fill:#3B5BDB,stroke:#1B2F7A,stroke-width:0px,color:#ffffff
    classDef s2 fill:#5F3DC4,stroke:#2E1C66,stroke-width:0px,color:#ffffff
    classDef s3 fill:#0B7285,stroke:#063844,stroke-width:0px,color:#ffffff
    classDef s4 fill:#087F5B,stroke:#04402E,stroke-width:0px,color:#ffffff
    classDef s5 fill:#E8590C,stroke:#7A2D06,stroke-width:0px,color:#ffffff
    classDef s6 fill:#C2255C,stroke:#63122F,stroke-width:0px,color:#ffffff

    class A s1
    class B s2
    class C s3
    class D s4
    class E s5
    class F s6
Loading
Assignment What is being tested Core idea Tooling Tests
Unit Testing Mizdooni domain model isolated units, parameterized cases JUnit 5 36
Test Doubles Mizdooni controllers mock vs. stub, state vs. behavior verification Mockito 58
Graph Coverage Fesadyab fraud engine CFG, statement & branch coverage JaCoCo 14
Logic & ISP Mizdooni HTTP API CACC / RACC, input-space partitioning MockMvc 83
Mutation & CI Fesadyab fraud engine mutants as a test of the tests PIT + GitHub Actions 14
GUI & BDD Mizdooni through the browser record-and-replay, Given/When/Then Katalon Recorder · Selenium · Cucumber 83 +8 scenarios

Every assignment folder is a self-contained snapshot of the project at that point, so later folders carry the earlier suites forward — 288 test methods across the six snapshots.


The two systems under test

🍽 Mizdooni

A Spring Boot restaurant-reservation service: users sign up, browse restaurants, check which tables are free in a time window, reserve, review and cancel. Rich domain invariants (working hours, manager permissions, duplicate reviews, past datetimes) make it a generous target for tests.

Used in CA1 · CA2 · CA4 · CA6

🕵️ Fesadyab

A compact bank-fraud detector: it keeps a transaction history, derives per-account averages, hunts for arithmetic patterns above a threshold, and scores suspicious debits. Tiny surface, dense branching — exactly what coverage and mutation analysis want.

Used in CA3 · CA5

flowchart LR
    U["User"] -->|places| R["Reservation"]
    R -->|occupies| T["Table"]
    T -->|belongs to| RS["Restaurant"]
    U -->|writes| RV["Review"]
    RV -->|aggregates into| RT["Rating<br/>food · service · ambiance · overall"]
    RT -->|star count| RS
    M["Manager"] -->|owns| RS

    classDef core fill:#3B5BDB,stroke:#1B2F7A,stroke-width:0px,color:#ffffff
    classDef sat  fill:#E7ECFB,stroke:#3B5BDB,stroke-width:1px,color:#101828
    class U,RS core
    class R,T,RV,RT,M sat
Loading

Unit Testing · JUnit 5

The problem. Mizdooni ships with no tests at all. Build the first safety net: unit tests for the logic-carrying methods of User, Table, Restaurant and Rating — one behaviour per test, no shared state between tests, and no wasted tests on bare getters, setters or constructors.

The interesting cases are the ones where the domain quietly bends the rules — a cancelled reservation that must stop counting as reserved, a second review by the same user that should replace rather than append, an empty restaurant whose average rating must not divide by zero. Rating.getStarCount() is the natural parameterized target: a rounding rule with a hard ceiling at five stars, swept across eight inputs instead of eight near-identical test methods.

@ParameterizedTest
@CsvSource({
    "4.6, 5",   // rounds up, clamped at the maximum
    "2.5, 3",   // half rounds up
    "0.1, 0",   // rounds down to the floor
    "10.0, 5"   // out of range, still capped
})
public void testGetStarCount(double overall, int expectedStarCount) { ... }

Where to look → CA1/Mizdooni/src/test/java/mizdooni/model/ · theory answers in CA1/Theory-Questions.md


Test Doubles · Mockito

The problem. Controllers cannot be unit-tested honestly while they reach into real services. Write genuine unit tests for AuthenticationController, ReservationController and ReviewController by replacing every collaborator with a test double — and fix the small bugs the tests expose along the way.

Mockito draws a hard border around the controller: everything inside is real code under test, everything outside is a double that is either trained (when(...).thenReturn(...)) or interrogated (verify(...)).

flowchart LR
    TEST["ReservationControllerTest"]
    CTRL["ReservationController<br/><i>system under test</i>"]
    S1["ReservationService"]
    S2["RestaurantService"]

    TEST -->|"@InjectMocks"| CTRL
    CTRL --> S1
    CTRL --> S2
    TEST -. "@Mock · when / thenReturn / thenThrow" .-> S1
    TEST -. "verify · times · never" .-> S2

    classDef sut fill:#5F3DC4,stroke:#2E1C66,stroke-width:0px,color:#ffffff
    classDef dbl fill:#EFE9FC,stroke:#5F3DC4,stroke-width:1px,color:#101828
    classDef tst fill:#1b1f24,stroke:#5F3DC4,stroke-width:1px,color:#ffffff
    class CTRL sut
    class S1,S2 dbl
    class TEST tst
Loading

Each controller is pushed through both halves of its contract: the happy path (correct Response, correct HttpStatus, service called exactly once with the expected arguments) and the full catalogue of failures — UserNotFound, ManagerReservationNotAllowed, ReservationCannotBeCancelled, DateTimeInThePast and friends — each asserted as the ResponseException the API is supposed to surface. The accompanying theory covers test-spy varieties and when a shared fixture is worth its risks.

Where to look → CA2/Mizdooni/src/test/java/mizdooni/controllers/ · theory answers in CA2/Theory-Questions.md


Graph Coverage · JaCoCo

The problem. Turn the Fesadyab fraud engine into a control-flow graph and drive it to maximum structural coverage. Statement and branch coverage must both clear 90% — and any node or edge that stays unreachable has to be explained, not hidden.

getTransactionPatternAboveThreshold is the knot of the whole engine — a loop with three guard clauses and a stateful diff accumulator. Drawn as a graph, every test case becomes a visible walk through it:

flowchart TD
    S(["enter"]) --> E{"history empty?"}
    E -- yes --> R0(["return 0"])
    E -- no --> INIT["diff ← 0<br/>previous ← first txn"]
    INIT --> LOOP{"another txn?"}
    LOOP -- no --> RD(["return diff"])
    LOOP -- yes --> SAME{"txn.id == previous.id?"}
    SAME -- yes --> LOOP
    SAME -- no --> THR{"amount ≤ threshold?"}
    THR -- yes --> LOOP
    THR -- no --> D0{"diff == 0?"}
    D0 -- yes --> SET["diff ← amount − previous.amount<br/>previous ← txn"]
    SET --> LOOP
    D0 -- no --> NEQ{"diff ≠ amount − previous.amount?"}
    NEQ -- yes --> RZ(["return 0 · pattern broken"])
    NEQ -- no --> LOOP

    classDef dec fill:#0B7285,stroke:#063844,stroke-width:0px,color:#ffffff
    classDef act fill:#E2F2F5,stroke:#0B7285,stroke-width:1px,color:#101828
    classDef ret fill:#1b1f24,stroke:#0B7285,stroke-width:1px,color:#ffffff
    class E,LOOP,SAME,THR,D0,NEQ dec
    class INIT,SET act
    class S,R0,RD,RZ ret
Loading

Reaching every edge means engineering the inputs: an empty history, a duplicate transaction id, an amount sitting below the threshold, an arithmetic progression that holds, and one that breaks on the last element. The resulting JaCoCo report leaves nothing uncovered.

statements branches branches

Where to look → CA3/Fesadyab/src/test/java/domain/ · engine under test in TransactionEngine.java


Logic-Based Testing, ISP & API Testing

The problem. Test RestaurantController and TableController as a web API — real HTTP verbs, real routing, real JSON — never by calling controller methods directly. Then, on paper, dissect a predicate with active-clause coverage and partition an input space down to a pair-wise test set.

The API tests go through the full Spring stack with the service layer mocked out, so an assertion is made about the contract rather than about a Java return value: status code, JSON body shape, and the exact error message a client would see.

mockMvc.perform(get("/restaurants/{restaurantId}", restaurant.getId()))
       .andExpect(status().isNotFound())
       .andExpect(jsonPath("$.success").value(false))
       .andExpect(jsonPath("$.message").value("restaurant not found"));

The logic half works on p = (¬a ∧ b) ∨ (b ∧ c) ∨ (¬b ∧ ¬c), where the question is not which rows but which pairs of rows let a single clause decide the outcome on its own:

# a b c ¬a∧b b∧c ¬b∧¬c p
1 T T T F T F T
2 T T F F F F F
3 T F T F F F F
4 T F F F F T T
5 F T T T T F T
6 F T F T F F T
7 F F T F F F F
8 F F F F F T T

determining pairs — a: (2,6) · b: (1,3) (2,4) (5,7) · c: (1,2) (3,4) (7,8)

Each pair flips exactly one clause while the rest hold still, and p flips with it — which is also why clause coverage alone cannot be trusted to imply predicate coverage here.

Where to look → CA4/Mizdooni/src/test/java/mizdooni/controllers/ (RestaurantControllerApiTest, TableControllerApiTest) · theory in SWT-CA4-TheoryQuestions.pdf


Mutation Testing & CI Pipeline

The problem. Coverage says the tests ran through the code; it never says they would have noticed if the code were wrong. Mutate the Fesadyab engine with PIT, measure how many mutants the suite kills, justify every survivor — and wire the whole thing into a pipeline that re-runs on every push.

PIT rewrites the bytecode — negating conditionals, swapping boundaries, discarding return values — and re-runs the suite against each variant. A mutant that survives is a defect the tests are blind to.

pie showData
    title PIT results · 29 mutants generated
    "Killed by the suite" : 28
    "Survived" : 1
Loading

The single survivor is an honest one: swapping > for >= on a comparison that no reachable input can distinguish — an equivalent mutant, undetectable by construction rather than by oversight. That leaves the suite at 96.5% mutation coverage, the number that actually justifies refactoring the engine with confidence.

The pipeline keeps it from rotting:

flowchart LR
    P["push / PR<br/>→ main"] --> CO["checkout"] --> J["set up JDK 21<br/>temurin"] --> CA["cache ~/.m2"] --> T["mvn -B test"] --> JR["jacoco:report"]

    classDef step fill:#FFF0E6,stroke:#E8590C,stroke-width:1px,color:#101828
    classDef trig fill:#E8590C,stroke:#7A2D06,stroke-width:0px,color:#ffffff
    class P trig
    class CO,J,CA,T,JR step
Loading

Where to look → .github/workflows/ca5-workflow.yml · CA5/Fesadyab/pom.xml (PIT + JaCoCo plugins) · full write-up in CA5-Report.pdf


GUI Testing & Behaviour-Driven Development

The problem. Leave the JVM entirely. Drive Mizdooni through its Swagger UI in a real browser, recording a full customer journey with Katalon Recorder and exporting it as runnable Selenium WebDriver code — then come back and express domain behaviour as Given/When/Then scenarios with Cucumber.

The recorded scenario is an end-to-end reservation life cycle, clicked through the live API docs:

sequenceDiagram
    autonumber
    actor C as Customer
    participant UI as Swagger UI
    participant API as Mizdooni API

    C->>UI: sign up as a client
    UI->>API: POST /signup
    C->>UI: browse the restaurant list
    UI->>API: GET /restaurants?page=1
    C->>UI: ask for free slots · restaurant 1 · 2 people · 2025-10-26
    UI->>API: GET /reserves/{restaurantId}/available
    API-->>UI: open time slots
    C->>UI: reserve the first open slot
    UI->>API: POST /reserves/{restaurantId}
    C->>UI: review own reservations
    UI->>API: GET /reserves/customer/{customerId}
    C->>UI: cancel the reservation
    UI->>API: POST /reserves/cancel/{reservationNumber}
Loading

Katalon captures every interaction with a ranked list of candidate locators, and the Java export turns the suite into a plain JUnit class driving a FirefoxDriver — the clearest demonstration of why XPath-anchored GUI tests are powerful and brittle: the resulting selectors are full DOM paths through Swagger's generated markup.

The BDD half drops back to the domain and re-states its rules as behaviour. Three feature files — addReservation, addReview and getAverageRating — hold 8 scenarios written in Gherkin, each one naming a rule a stakeholder would recognise rather than a method a developer would call:

Scenario: Replacing a review by the same user
  Given a first user
  And a restaurant
  And a review with food 4, service 5, ambiance 4, and overall 4
  When the user adds the review to the restaurant
  And a review with food 2, service 3, ambiance 2, and overall 2
  And the user adds the review to the restaurant
  Then the restaurant should only have 1 review
  And the review in the list should have food 2, service 3, ambiance 2, and overall 2

Fifteen step definitions back those sentences with parameterised expressions ({int}, {double}), so a scenario reads as prose while still binding to real assertions. @CucumberContextConfiguration boots the Spring context once for the suite, and a JUnit TestRunner glues features to steps and emits an HTML report.

flowchart LR
    F[".feature files<br/>Gherkin scenarios"] --> R["TestRunner<br/>@RunWith(Cucumber.class)"]
    R --> G["StepDefinition<br/>@Given · @When · @Then"]
    G --> D["Mizdooni domain<br/>User · Restaurant"]
    R --> H["target/cucumber-reports<br/>HTML output"]

    classDef gher fill:#C2255C,stroke:#63122F,stroke-width:0px,color:#ffffff
    classDef glue fill:#FCE7EF,stroke:#C2255C,stroke-width:1px,color:#101828
    classDef out  fill:#1b1f24,stroke:#C2255C,stroke-width:1px,color:#ffffff
    class F gher
    class R,G glue
    class D,H out
Loading

Where to look → CA6/CA6-TestSuit.krecorder (the recording) · CA6/CA6-Export.java (the Selenium export) · features/ (the scenarios) · StepDefinition.java (the glue)


Repository map

SWT-Course-Projects
├── CA1/  Unit Testing ............... Mizdooni · JUnit 5 · model tests + theory
├── CA2/  Test Doubles ............... Mizdooni · Mockito · controller tests + theory
├── CA3/  Graph Coverage ............. Fesadyab · JaCoCo · CFG & branch coverage
├── CA4/  Logic Testing & ISP ........ Mizdooni · MockMvc API tests + CACC/pair-wise theory
├── CA5/  Mutation Testing & CI ...... Fesadyab · PIT · GitHub Actions + report
├── CA6/  GUI Testing & BDD .......... Mizdooni · Katalon/Selenium · Cucumber
└── .github/workflows/
    └── ca5-workflow.yml ............. build · test · coverage on every push to main

Inside each assignment folder: the original SWTF03-CAx-Description.pdf brief, the project snapshot, and the submitted theory answers.


Running the suites

# Java 21 is required — verify first
mvn --version          # must report Java 21

# Mizdooni (CA1 · CA2 · CA4 · CA6)
mvn -B test -f CA1/Mizdooni/pom.xml

# Fesadyab + JaCoCo coverage report
mvn -B test -f CA3/Fesadyab/pom.xml
open CA3/Fesadyab/target/site/jacoco/index.html

# Fesadyab + PIT mutation report
mvn -f CA5/Fesadyab/pom.xml org.pitest:pitest-maven:mutationCoverage
open CA5/Fesadyab/target/pit-reports/index.html

# Cucumber BDD scenarios (CA6) — report lands in target/cucumber-reports
mvn -B test -f CA6/Mizdooni/pom.xml -Dtest=TestRunner

# Run Mizdooni and open Swagger UI for the GUI scenarios (CA6)
mvn -f CA6/Mizdooni/pom.xml spring-boot:run
# → http://localhost:8080/api/swagger-ui/index.html

Authors

Amir Parsa Mobed · 810100271
Arshia Ataei Naeini · 810100252

Theory answers are written in Persian. Assignment briefs are the course originals.

About

Projects for the Software Testing course in University of Tehran, Fall 2024

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages