This is a ready-to-use Operational Qualification (OQ) protocol for GAMP Category 5 custom software: it is written for systems where the custom logic’s internal branches, not just its external behavior, need to be exercised and confirmed against an independently derived expected result. Replace every <<FILL: ...>> placeholder with your own specifics, approve the protocol before execution, and record actual results and evidence at the time of execution. A worked filled specimen follows. This content is educational reference, not legal or regulatory advice; verify each cited regulation against the current source before you rely on it.
This protocol assumes the Design Specification, code review, and unit testing described in validating custom software: GAMP Category 5 from specification to release are already complete; see Specification: Design Specification (DS) for Custom GxP Software and Record: Unit Test Record for GxP Custom Software for those upstream deliverables. This protocol tests the integrated, deployed system; it does not replace either of them. For a general-purpose computerized system OQ across all GAMP categories, see computerized system OQ protocol; for verification testing scoped to a standalone script, notebook, or dashboard, see verification testing of a GxP script, notebook, or dashboard. This protocol is for a Category 5 system where structural branch coverage against the DS, not just functional testing against the FS, is required.
Approval page
Testing does not begin until this page is signed.
| Field | Entry |
|---|---|
| Protocol title | OQ, <<FILL: system / component name>> |
| Protocol number | <<FILL: OQ-ID>> |
| Version | <<FILL>> |
| FS reference and version | <<FILL>> |
| DS reference and version | <<FILL>> |
| Version / commit under test | Tag <<FILL>> at commit <<FILL>> |
| Governing procedure | <<FILL: SOP-ID>> |
Pre-execution approvals
| Role | Name | Signature | Date |
|---|---|---|---|
| Author of protocol | <<FILL>> | ||
| System owner | <<FILL>> | ||
| Validation lead / CSV | <<FILL>> | ||
| Quality Assurance | <<FILL>> |
Post-execution approvals
| Role | Name | Signature | Date |
|---|---|---|---|
| Executed by | <<FILL>> | ||
| Reviewed by (independent of executor) | <<FILL>> | ||
| Validation lead / CSV | <<FILL>> | ||
| Quality Assurance | <<FILL>> |
1. Objective
To verify that <<FILL: system / component name>>, at the version identified above, satisfies its functional specification from the outside (black-box), executes every significant internal branch of its DS-specified algorithm correctly (white-box), behaves correctly at every input boundary, and, where applicable, handles concurrent access correctly, and to record evidence sufficient to support release for its intended GxP use.
2. Scope
In scope: the functions, modules, and workflows of <<FILL: system / component name>> listed in section 7, at the version named on the approval page.
Out of scope, with the control that covers each:
| Out of scope | Covered by |
|---|---|
| Unit-level testing of individual functions during development | <<FILL: unit test record reference>> |
| Independent code review confirming the code implements the DS | <<FILL: code review record reference>> |
| Installation of the software and environment | <<FILL: IQ protocol reference>> |
| End-to-end use under real operating conditions with trained users | <<FILL: PQ/UAT protocol reference>> |
3. System description
| Field | Entry |
|---|---|
| What the system does | <<FILL: two sentences>> |
| GxP decision(s) supported | <<FILL>> |
| GAMP category and rationale | <<FILL: 5, or the Category 5 component of a hybrid system>> |
| Environment identifier | <<FILL: manifest file, package snapshot, or image digest>> |
| Test environment relationship to production | <<FILL: identical / stated differences and justification>> |
| Concurrent-access applicable? | Yes/No, <<FILL: if yes, name the concurrency model, e.g. multiple simultaneous users, or a real-time polling process>> |
4. Prerequisites
| # | Prerequisite | Confirmed | Evidence reference |
|---|---|---|---|
| P1 | FS and DS are approved and current; this protocol traces to their requirement and algorithm IDs | <<FILL>> | <<FILL>> |
| P2 | The version under test is tagged in version control and matches the tag on the approval page | <<FILL>> | <<FILL>> |
| P3 | Independent code review for this version is complete with no open Critical or Major findings | <<FILL>> | <<FILL>> |
| P4 | Unit testing for this version is complete and passed, per the unit test record | <<FILL>> | <<FILL>> |
| P5 | Every conditional branch and error handler named in the DS is listed in section 8 before execution begins | <<FILL>> | <<FILL>> |
| P6 | Expected results for every white-box and black-box known-answer case were derived independently of the code and recorded before execution | <<FILL>> | <<FILL>> |
| P7 | Test environment is pinned and matches production in every respect that could affect the result, or the difference is assessed | <<FILL>> | <<FILL>> |
| P8 | Testers are trained on this protocol and the governing procedure | <<FILL>> | <<FILL>> |
5. Roles
| Role | Responsibility during execution |
|---|---|
| Tester | Executes each test case exactly as written, records the actual result verbatim, attaches evidence |
| Independent verifier of expected results | Derived and recorded expected results before execution, by hand calculation or an independent tool, distinct from the code under test |
| Reviewer | Reviews executed cases for completeness and correct pass/fail determination; independent of the tester |
| System owner | Available to clarify intended behavior; does not determine pass or fail |
| Validation lead / CSV | Adjudicates deviations, approves the summary |
| Quality Assurance | Approves the protocol before execution and the summary after |
6. Acceptance criteria
| # | Criterion |
|---|---|
| AC1 | Every black-box test case in section 7 is executed with an actual result and evidence recorded |
| AC2 | Every branch and error handler listed in the branch-coverage matrix in section 8 is exercised by at least one test case, with a pass result |
| AC3 | Every boundary value case in section 9 returns the specified value, including rejection at values just outside the valid range |
| AC4 | Where concurrent access applies, every case in section 10 passes with no data corruption, no lost update, and correct session handling |
| AC5 | Every known-answer expected result agrees with an independently derived value, not a value produced by the code under test |
| AC6 | Every requirement and every DS branch traces to at least one executed test case with no untested item |
| AC7 | Every deviation is documented, assessed, and closed before the protocol is concluded |
| AC8 | The executed protocol is reviewed and approved by a person independent of the tester |
7. Black-box functional test cases (against the FS)
| Test ID | FS requirement | Case type | Input | Expected result | Actual | Pass/Fail | Tester | Date |
|---|---|---|---|---|---|---|---|---|
<<FILL: TC-BB-001>> | <<FILL>> | Normal | <<FILL>> | <<FILL>> | ||||
<<FILL: TC-BB-002>> | <<FILL>> | Boundary | <<FILL>> | <<FILL>> | ||||
<<FILL: TC-BB-003>> | <<FILL>> | Error | <<FILL>> | <<FILL: specified error, no partial result>> |
8. White-box structural test cases (against the DS algorithm’s branches)
Every conditional branch, every error handler, and every derived flag named in the DS algorithm specification gets a row here, and each row’s expected value is derived independently of the code, per the DS’s own worked example or a fresh hand calculation, not by running the function and reading its output.
| DS branch / condition ID | Description (from DS) | Test ID | Input designed to hit this branch | Independently derived expected result | Actual | Pass/Fail |
|---|---|---|---|---|---|---|
<<FILL: e.g. ALG-001 step 1>> | <<FILL: e.g. load mass = 0 raises ERR-ALG-001>> | <<FILL: TC-WB-001>> | <<FILL>> | <<FILL>> | ||
<<FILL: e.g. ALG-001 step 6, LOW_YIELD>> | <<FILL>> | <<FILL: TC-WB-002>> | <<FILL>> | <<FILL>> | ||
<<FILL: e.g. ALG-001 step 6, HIGH_YIELD>> | <<FILL>> | <<FILL: TC-WB-003>> | <<FILL>> | <<FILL>> | ||
<<FILL: e.g. ALG-001 step 6, IMPLAUSIBLE>> | <<FILL>> | <<FILL: TC-WB-004>> | <<FILL>> | <<FILL>> | ||
<<FILL: e.g. ALG-001 step 6, NORMAL>> | <<FILL>> | <<FILL: TC-WB-005>> | <<FILL>> | <<FILL>> |
8.1 Branch coverage completeness statement
| Field | Entry |
|---|---|
| Total branches / conditions identified in the DS | <<FILL: count>> |
| Branches exercised by a test case above | <<FILL: count>> |
| Branches not covered, with justification | <<FILL: none, or list and justify each>> |
A DS branch with no corresponding row above is either untested or the DS was incomplete; neither is acceptable at protocol conclusion.
9. Boundary value analysis
For every numeric parameter the system accepts, test at the minimum valid value, the maximum valid value, one value below the minimum, and one value above the maximum. For string or count fields, test at minimum length, maximum length, and empty.
| Parameter | Minimum valid | Maximum valid | Just below minimum (expect reject) | Just above maximum (expect reject) | Actual | Pass/Fail |
|---|---|---|---|---|---|---|
<<FILL>> | <<FILL>> | <<FILL>> | <<FILL>> | <<FILL>> | ||
<<FILL>> | <<FILL>> | <<FILL>> | <<FILL>> | <<FILL>> |
10. Concurrent access testing (execute where section 3 marks this applicable)
| Test ID | Scenario | Expected result | Actual | Pass/Fail |
|---|---|---|---|---|
<<FILL: TC-CC-001>> | Two users submit the same record simultaneously | Only one succeeds, or the system serializes correctly; no duplicate or corrupted record | ||
<<FILL: TC-CC-002>> | One user reads a record while another modifies it | Reading user sees a consistent state, not a partial write | ||
<<FILL: TC-CC-003>> | Session exceeds the configured timeout | Session terminated; re-authentication required before further action |
Mark this section “Not applicable, single-user batch execution confirmed” if section 3 states concurrency does not apply, and state the basis for that conclusion.
11. Deviation handling
- A test case whose actual result does not match the expected result is recorded as Fail. The expected result, the input, or the code is not adjusted to obtain a pass.
- Every failure raises a deviation under
<<FILL: SOP-ID>>, recording the test ID, expected, actual, evidence, and the immediate assessment of whether other executed cases are affected. - The validation lead classifies each deviation as a code defect, a DS defect, or a protocol/expected-result defect, and dispositions accordingly.
- A deviation is closed only when the corrective action is complete, affected cases are re-executed and pass, and closure is approved by the validation lead and QA.
| Deviation ID | Test ID | Description | Classification | Corrective action | Re-executed | Closed |
|---|---|---|---|---|---|---|
<<FILL>> | <<FILL>> | <<FILL>> | <<FILL>> | <<FILL>> | <<FILL>> | <<FILL>> |
12. Traceability
| Requirement / branch ID | Test case ID(s) | Executed | Result |
|---|---|---|---|
<<FILL>> | <<FILL>> | <<FILL>> | <<FILL>> |
Untested requirements or branches: <<FILL: none, or ID and justification>>
13. Summary and conclusion
| Field | Entry |
|---|---|
| Black-box cases executed / passed | <<FILL>> |
| White-box branch cases executed / passed | <<FILL>> |
| Branch coverage achieved | <<FILL: e.g. 100%, or state gaps>> |
| Boundary cases executed / passed | <<FILL>> |
| Concurrent-access cases executed / passed (or N/A) | <<FILL>> |
| Deviations raised / open at conclusion | <<FILL: open should be zero>> |
| Version at conclusion | Tag <<FILL>> at commit <<FILL>> |
Conclusion statement: <<FILL: state whether the system, at the identified version, is verified against its FS and DS and suitable to proceed to PQ/UAT, and name any limitation>>
14. Attachments
| # | Attachment |
|---|---|
| A | Approved FS and DS |
| B | Independent code review record |
| C | Unit test record |
| D | Independently derived expected results, dated before execution |
| E | Test evidence (screenshots, output files, logs) |
| F | Deviation records |
15. References
21 CFR 211.68 (automatic, mechanical, and electronic equipment). 21 CFR Part 11 (electronic records and signatures). ISPE GAMP 5 (Second Edition), for the Category 5 verification expectation (reference by title; describe, do not paste). FDA guidance, Computer Software Assurance for Production and Quality Management System Software (current version).
Confirm the current version and clause numbers of each reference before issue.
16. Revision history
| Version | Date | Author | Summary of change |
|---|---|---|---|
<<FILL: 1.0>> | <<FILL: date>> | <<FILL: author>> | Initial issue. |
Filled specimen
The following shows an extract of an OQ executed for the step-yield calculation script referenced in the Design Specification example (DS-CUST-014, algorithm ALG-001), version tagged v1.3.0, commit 9e21ab4, executed 22 August 2026. Numbers are illustrative.
Black-box (extract)
| Test ID | FS requirement | Case | Input | Expected | Actual | Pass/Fail |
|---|---|---|---|---|---|---|
| TC-BB-001 | FS-040, yield calculation | Normal | load_titer=12.5, load_vol=400, pool_titer=8.2, pool_vol=550 | 90.2% displayed on the batch record | 90.2% | Pass |
| TC-BB-002 | FS-041, audit trail on calculation | Normal | Same as above | Audit entry: timestamp, operator, batch ID, result | Entry present with all four fields | Pass |
White-box branch coverage (extract)
| DS branch | Description | Test ID | Input | Expected | Actual | Pass/Fail |
|---|---|---|---|---|---|---|
| ALG-001 step 1 | load_mass = 0 raises ERR-ALG-001 | TC-WB-001 | load_titer=0, load_vol=400, pool_titer=8.2, pool_vol=550 | ERR-ALG-001 raised, no result written | ERR-ALG-001 raised | Pass |
| ALG-001 step 6, LOW_YIELD | yield < 70.0 sets result_flag = LOW_YIELD | TC-WB-002 | inputs giving yield = 65.0% (hand-calculated) | result_flag = LOW_YIELD | LOW_YIELD | Pass |
| ALG-001 step 6, boundary at 70.0 | yield = 70.0 exactly is NORMAL, not LOW_YIELD | TC-WB-003 | inputs giving yield = 70.0% exactly (hand-calculated) | result_flag = NORMAL | NORMAL | Pass |
| ALG-001 step 6, HIGH_YIELD | yield > 110.0 sets result_flag = HIGH_YIELD | TC-WB-004 | inputs giving yield = 120.0% | result_flag = HIGH_YIELD | HIGH_YIELD | Pass |
| ALG-001 step 6, IMPLAUSIBLE | yield > 150.0 sets result_flag = IMPLAUSIBLE, held pending review | TC-WB-005 | inputs giving yield = 155.0% | result_flag = IMPLAUSIBLE, held from batch record | IMPLAUSIBLE, held | Pass |
Branch coverage completeness: 5 branches identified in DS-CUST-014 ALG-001 (zero-mass error, LOW_YIELD, the 70.0 boundary, HIGH_YIELD, IMPLAUSIBLE), 5 exercised, 0 gaps.
Boundary value analysis (extract)
| Parameter | Min valid | Max valid | Below min | Above max | Actual | Pass/Fail |
|---|---|---|---|---|---|---|
| load_titer_mg_ml | 0.01 | 999.99 | 0 (reject) | 1000.00 (reject) | Both rejected with field-specific message | Pass |
Concurrent access
| Test ID | Scenario | Expected | Actual | Pass/Fail |
|---|---|---|---|---|
| TC-CC-001 | Two operators submit a yield calculation for the same batch ID within one second | Second submission rejected with “calculation already in progress for this batch,” first completes normally | Behaved as expected | Pass |
In this example, the branch-coverage table is what separates this protocol from a functional test alone: TC-WB-003 specifically targets the boundary the DS calls out explicitly (yield at exactly 70.0 is NORMAL, not LOW_YIELD), a case a black-box-only test plan would very likely never generate, because nothing about normal use produces a value at exactly a threshold.
Common inspection findings this protocol prevents
- OQ testing covers the functional specification but never exercises the internal branches of a custom algorithm, so a wrong comparison operator (< instead of <=) at a threshold is never caught.
- The exact boundary values named in the DS were never tested, only “typical” values near them.
- Expected results for calculation test cases were read from the system’s own output rather than derived independently.
- A DS names an error-handling branch that has no corresponding test case anywhere in the validation package.
- Concurrent-access behavior was never tested for a multi-user system, and a race condition first appears in production.
- The branch-coverage claim in the validation summary is asserted with no table showing which branches were actually exercised.
How to adapt this protocol
- Set your protocol number, FS and DS references, and the version under test on the approval page.
- Build section 7 from your FS requirements and section 8 by walking through every conditional and error-handling branch named in your DS, one row per branch.
- Derive every white-box expected result independently, before execution, using the DS’s own worked example as a starting point where one exists.
- Complete section 9 for every numeric or length-bounded input your system accepts.
- Mark section 10 not applicable only after confirming, and stating the basis, that the system genuinely has no concurrent-access exposure.
- Complete the branch coverage completeness statement in section 8.1 before concluding the protocol; an unexplained gap blocks release.
- Confirm every regulation in section 15 against the current published version before issue.