Testing an Appointment Service: Strategy, Case Design, and Coverage Evidence for a Campus Tutoring Scheduler
[Author Name]
Department of Computer Science, Southern New Hampshire University
CS 320: Software Testing, Automation and Quality Assurance
Module 4 Assignment
[Instructor Name]
August 11, 2026
Original model document written as a teaching example. The application, the defects and the coverage figures are illustrative; no real product, employer or development team is described.
The Feature Under Test and Its Requirements
The system under test is the appointment service inside a scheduling application built for a campus tutoring center that books about 400 sessions a term across 22 tutors. The application keeps three service classes in memory: a contact service, an appointment service and a reminder service. This write-up covers the appointment service and the Appointment object it stores. The product owner supplied three requirements. An appointment identifier is required, unique across the store, no longer than 10 characters, and fixed once created. An appointment date is required and cannot fall before the moment the booking is made. A description is required and cannot exceed 50 characters. The service supports add, delete by identifier, and update of the description field. Storage is a HashMap keyed by identifier, so the three classes total 214 lines of Java with no database behind them.
Each requirement was ranked before a single case was written, because effort belongs where failure costs the most. Duplicate identifiers carry the highest risk because a silent overwrite deletes a booking that a tutor and a student both believe exists. A date accepted from the past carries the second highest risk, since it lets the scheduler hold a slot that has already gone by and hides the conflict from the tutor's daily view. Description length is the lowest of the three, because a rejected string annoys a user while a lost booking costs an hour of tutoring. The ranking produced three high risk conditions and two medium ones, and it decided both the order the cases were written in and how many negative cases each requirement received.
The strategy is black-box first. Cases were designed from the three requirements using equivalence partitioning and boundary value analysis, so that the design does not inherit the same blind spots as the code it checks (Myers et al., 2011). Coverage was measured only after that set had run, and the gaps it exposed were filled with a small number of structural cases aimed at branches the requirement-driven cases had missed (Ammann & Offutt, 2017). Every test builds its own service instance in a setup method, so no test depends on the order of the others, and the time source is injected as a fixed clock rather than read from the machine, which removes the flakiness a date boundary otherwise brings. JUnit 5 is the harness, and negative paths use assertThrows rather than try-catch blocks.
Test Strategy and Test Cases
Three requirements produced three sets of partitions. For the identifier, the valid partition is a non-null string of 1 to 10 characters that is not already stored, and the invalid partitions are null, the empty string, a string of 11 characters, and a duplicate of a stored value. For the date, the valid partition is any instant at or after the fixed clock reading of 2026-03-02T09:00:00Z, and the invalid partitions are null and any earlier instant. For the description, the valid partition is 1 to 50 characters and the invalid partitions are null, the empty string, and 51 characters or more. Boundary values were taken one step inside and one step outside each edge, which is the specification-based pairing the international testing standard describes (International Organization for Standardization, 2022).
The set below shows eight of the 46 cases, written as they appear in the submission with an identifier, an input and an expected result. TC-01, add with identifier A100, a date one day after the clock and the description Calculus review in room 214: the store reports a size of 1 and returns an equal object on lookup. TC-02, add with an identifier of 11 characters: IllegalArgumentException is thrown and the store size stays at 0. TC-03, add with a null identifier: IllegalArgumentException. TC-04, add a second appointment with identifier A100: IllegalArgumentException, and a lookup confirms the first description is still stored. TC-05, add with a date one second before the clock: IllegalArgumentException. TC-06, add with a description of exactly 50 characters: accepted. TC-07, add with a description of 51 characters: IllegalArgumentException. TC-08, delete A100 from a populated store: size 0, and a second delete of the same identifier throws.
Every case is mapped to the requirement it came from, and the map travels with the submission rather than living in a side note. The identifier requirement holds 14 cases, of which 9 are negative; the date requirement holds 11 cases, of which 6 are negative; the description requirement holds 13 cases, of which 8 are negative; the remaining 8 cases cover delete and update behavior that crosses more than one requirement. Reading the map in the other direction is the point of building it. A requirement with no negative case is a gap, and a case that maps to no requirement is either a missing requirement or a test written out of habit. Two cases were deleted at this step because they asserted implementation details that no requirement had claimed.
Execution Results, Coverage, and Evidence of Correctness
The first full run of the 46 cases produced 43 passes and 3 failures in 1.9 seconds. TC-06 failed because a description of exactly 50 characters was rejected, which traced to a length check written as greater than or equal to 50 rather than greater than 50; this was logged as DEF-01 and fixed in one line. Two date cases failed together because the comparison rejected an instant equal to the clock reading, which the requirement allows; this was logged as DEF-02 and fixed by correcting the side of an isBefore comparison. Each defect was re-tested against its own failing case first and then against the whole set, which is the failing-then-passing rhythm the practice literature describes (Beck, 2003). The second full run produced 46 passes and no new failures.
JaCoCo was then run over the three service classes. Line coverage stood at 96.2 percent, with 8 of 214 lines untouched, and branch coverage at 92.9 percent, with 6 of 84 branches untouched. Reading the report rather than quoting the headline number showed what those gaps were. Five untouched lines belong to toString methods that no requirement mentions. The six missed branches sat in a defensive else path in the update operation and in a null guard no case had reached, because every existing case supplied a real object. Three cases were added for those paths, which brought branch coverage to 97.6 percent and line coverage to 97.7 percent. The toString lines were left uncovered on purpose, with the reason recorded in the write-up rather than hidden behind the percentage.
Coverage tells the reader which lines the tests reached, not whether the assertions after those lines were worth writing. A set can execute every line in a class while asserting almost nothing about what those lines produced (Inozemtseva & Holmes, 2014). The evidence of correctness offered here is three things held together. First, the requirement-to-case map, which shows each of the three requirements carrying positive and negative cases at its boundaries. Second, the failure history, since DEF-01 and DEF-02 were caught by cases that failed before the fix and passed after it, which is the only proof those cases can detect the fault they were written for. Third, repeatability, since a fixed clock and a fresh service instance per test gave identical results on three consecutive runs. The exit criteria written before the run were all met.
References
Ammann, P., & Offutt, J. (2017). Introduction to software testing (2nd ed.). Cambridge University Press.
Beck, K. (2003). Test-driven development: By example. Addison-Wesley.
Inozemtseva, L., & Holmes, R. (2014). Coverage is not strongly correlated with test suite effectiveness. In Proceedings of the 36th International Conference on Software Engineering (pp. 435-445). Association for Computing Machinery.
International Organization for Standardization. (2022). Software and systems engineering: Software testing - Part 1: General concepts (ISO/IEC/IEEE Standard No. 29119-1:2022).
Myers, G. J., Sandler, C., & Badgett, T. (2011). The art of software testing (3rd ed.). John Wiley & Sons.
How this CS 320 Module 4 example is structured
In many sections this module asks for a testing write-up that pairs a described feature with the tests written against it; your classroom's instructions and rubric decide the exact form. SNHU numbers graded work by module, which is why students search CS 320 Module 4, and whether a section calls the deliverable a milestone, an assignment or a journal is set inside the classroom. The order follows how a reviewer reads a quality submission. The feature and its requirements come first, so every later claim has something to be checked against. The strategy and the cases come second, with inputs and expected results side by side. Execution results, coverage and defects come last, because a coverage number means little until the reader knows which requirements the cases were aimed at. The scheduler is a composite written for teaching at the undergraduate computing level at Southern New Hampshire University.
CS 320 Module 4 questions, answered
What does a CS 320 Module 4 testing write-up actually contain?
A described feature with its requirements, a stated strategy, the cases themselves with inputs and expected results, the run results including any defects found, and a coverage report that is read rather than quoted. The example on this page shows all five in that order. Your classroom rubric decides how much weight each part carries.
How many test cases are enough for a module 4 submission?
There is no fixed count, and padding a total is visible to a reader. Work from the requirements instead: each one earns at least one case inside its boundary, one outside it, and one for a null or empty input. Three requirements handled that way produced 46 cases for the small service class in this example.
Is 100 percent coverage the goal?
No. Coverage shows which lines ran, not whether the assertions were meaningful, so a set can reach every line and still miss faults. Aim instead for every requirement carrying positive and negative cases, report branch coverage beside line coverage, and explain any code left uncovered, which is what the example does with its toString methods.
Write yours, or have the desk draft it
This paper is an original model document written by our desk, not a submitted student paper and not an official Southern New Hampshire University document. Read it for the moves, then write your own to the instructions in your classroom. If you want one built to your exact prompt and rubric, the first custom sample is free and arrives in 24 to 48 hours.