Testing Strategy: Unit, Contract, Integration, E2E
Learn Enterprise CPQ OMS Camunda 7 - Part 043
Testing strategy untuk large production-grade Java microservices CPQ/OMS dengan unit, contract, integration, workflow, event, dan E2E tests yang menjaga lifecycle correctness, data correctness, dan operational confidence.
Part 043 — Testing Strategy: Unit, Contract, Integration, E2E
Testing di sistem CPQ/OMS enterprise bukan aktivitas untuk membuktikan bahwa method berjalan. Testing adalah mekanisme untuk menjaga commercial correctness, fulfillment correctness, dan operational recoverability saat sistem berubah.
Di aplikasi sederhana, test sering berarti:
input A menghasilkan output B.
Di CPQ/OMS, test harus bisa menjawab:
Apakah sistem tetap menghasilkan quote, price, approval, order, workflow, event, audit, dan recovery behavior yang benar walaupun catalog berubah, user melakukan retry, event datang dua kali, workflow tertunda, external system timeout, dan ada amendment terhadap order yang sedang berjalan?
Kalau test hanya memeriksa happy path, sistem akan tampak stabil di CI tetapi gagal di production.
Part ini membangun strategi testing dari first principles untuk sistem:
- OpenAPI-first
- schema-first
- JAX-RS/Jersey
- PostgreSQL
- EclipseLink JPA
- Camunda 7
- Kafka
- Redis
- multi-service CPQ/OMS
Tujuannya bukan membuat banyak test. Tujuannya adalah membuat lapisan bukti bahwa perubahan tidak merusak invariant enterprise.
1. Prinsip Utama: Test Harus Mengunci Invariant, Bukan Mengunci Implementasi
Salah satu kesalahan terbesar engineer dalam sistem enterprise adalah menulis test yang terlalu dekat dengan struktur kode.
Contoh test yang rapuh:
assertEquals("APPROVED", quote.getStatus().name());
assertEquals(3, quote.getLines().size());
Test seperti itu belum tentu salah, tetapi belum cukup. Ia hanya mengunci nilai permukaan.
Test yang lebih bernilai mengunci invariant:
Given quote revision R is approved
When any commercial material field changes
Then approval evidence for R must no longer authorize acceptance
And a new approval cycle is required before acceptance
Test ini lebih dekat ke kebenaran bisnis.
Ia tidak peduli apakah internal class bernama QuoteAggregate, QuoteEntity, atau QuoteDocument.
Ia peduli bahwa sistem tidak menerima quote stale.
Testing invariant untuk CPQ/OMS
Invariant yang wajib dites:
- Quote yang accepted harus berasal dari priced quote yang masih fresh.
- Approval tidak boleh dipakai setelah material quote berubah.
- Order tidak boleh dibuat dua kali dari quote revision yang sama karena retry.
- Price result harus reproducible dari snapshot input dan policy version.
- Order cancellation harus menghasilkan compensation plan, bukan mutate fulfillment history.
- Event yang dikonsumsi dua kali tidak boleh menggandakan side effect.
- Workflow incident harus bisa dikorelasikan ke business object.
- Audit trail tidak boleh hilang walaupun command gagal setelah sebagian validasi.
- Search/read model boleh lag, tetapi command decision tidak boleh bergantung pada read model stale.
- Tenant boundary harus berlaku di API, DB query, event consumer, workflow, dan cache.
Inilah yang harus menjadi tulang punggung test strategy.
2. Testing Pyramid Tidak Cukup: Gunakan Testing Mesh
Pyramid klasik berguna: banyak unit test, lebih sedikit integration test, lebih sedikit E2E test.
Tetapi CPQ/OMS bukan hanya call stack. Ia adalah kombinasi dari:
- domain lifecycle
- HTTP contract
- database constraints
- workflow state
- event propagation
- cache invalidation
- external system uncertainty
- human task
- authorization
- audit
Karena itu, model yang lebih tepat adalah testing mesh.
Testing mesh berarti setiap risk surface punya jenis test yang cocok.
Bukan semua hal dites E2E. Bukan semua hal dites unit.
Yang penting adalah: setiap failure mode penting punya minimal satu test yang menangkapnya di layer paling murah dan paling stabil.
3. Test Taxonomy untuk CPQ/OMS
Berikut taxonomy yang akan kita pakai.
| Test Type | Yang Dikunci | Contoh CPQ/OMS |
|---|---|---|
| Domain unit test | invariant, transition, calculation rule | cannot accept stale approved quote |
| Policy/DMN test | commercial decision | discount 18% requires finance approval |
| API contract test | request/response compatibility | POST /quotes/{id}/submit-for-approval error shape |
| Schema compatibility test | event/data compatibility | QuotePriced.v2 backward-compatible with v1 consumers |
| Persistence integration test | JPA mapping, SQL constraint, transaction | optimistic lock prevents double accept |
| Workflow test | BPMN path, wait state, timer, incident | quote approval escalates after SLA |
| Event test | outbox, ordering, idempotency | duplicate OrderSubmitted does not create duplicate task |
| Cache test | stale data, invalidation, TTL | catalog cache invalidated on new publication |
| Security test | object-level authz, tenant | seller cannot access another tenant's quote |
| E2E journey test | cross-service critical path | configure → price → approve → accept → order |
| Operational test | observability, audit, recovery | failed fulfillment creates fallout case and metric |
Top engineer tidak bertanya “berapa coverage?” terlebih dahulu. Ia bertanya:
Risiko apa yang belum punya bukti otomatis?
Coverage angka bisa tinggi tetapi blind spot tetap besar.
4. Test Boundary per Service
Dalam microservices CPQ/OMS, setiap service harus punya test boundary yang jelas.
Catalog Service
Wajib dites:
- product offering publication
- effective dating
- bundle validity
- option compatibility
- catalog snapshot creation
- cache invalidation event
- backward compatibility of published catalog schema
Tidak perlu dites berlebihan:
- getter/setter entity
- generated DTO mapping satu per satu tanpa business significance
Configuration Service
Wajib dites:
- invalid option combination
- required option missing
- derived option
- compatibility graph
- partial configuration
- explainability output
- deterministic result for same catalog snapshot
Pricing Service
Wajib dites:
- price component breakdown
- discount stacking
- rounding
- manual override
- price trace
- price reproducibility
- approval trigger from price result
- currency handling
Quote Service
Wajib dites:
- lifecycle transition
- revisioning
- snapshot immutability
- quote locking/concurrency
- stale approval invalidation
- idempotent accept
- document generation trigger
- order creation command emission
Order Service
Wajib dites:
- order capture from quote
- duplicate order prevention
- decomposition
- fulfillment step lifecycle
- cancellation
- compensation
- fallout case creation
- reconciliation from external callback
Workflow Service / Camunda 7
Wajib dites:
- process start idempotency
- business key mapping
- BPMN path
- task creation
- task completion semantics
- timer escalation
- incident mapping
- external task failure handling
- compensation path
5. Unit Test: Cepat, Deterministik, dan Berorientasi Invariant
Unit test di domain CPQ/OMS harus bebas dari:
- database
- Kafka
- Redis
- Camunda runtime
- real clock
- random UUID yang tidak dikontrol
- timezone implicit
Unit test idealnya memvalidasi rule murni.
Contoh target test:
class QuoteLifecycleTest {
@Test
void approvedQuoteCannotBeAcceptedAfterMaterialChange() {
QuoteRevision quote = QuoteScenario.standardEnterpriseBundle()
.priced()
.approvedBy("finance-director")
.build();
quote.changeDiscount("line-1", Percent.of("22.00"));
assertThrows(StaleApprovalException.class, quote::accept);
}
}
Yang penting bukan syntax persisnya. Yang penting struktur mentalnya:
Given business state
When command happens
Then invariant is preserved
Domain test harus punya vocabulary bisnis
Test buruk:
testStatusChange1()
Test baik:
approvedQuoteCannotBeAcceptedAfterMaterialPriceChange()
orderCannotBeCancelledAfterAllFulfillmentStepsAreCompleted()
duplicateAcceptCommandReturnsExistingOrderReference()
manualDiscountAboveThresholdRequiresApproval()
Nama test adalah dokumentasi executable.
Unit test untuk pricing
Pricing test harus selalu memeriksa breakdown, bukan hanya total.
Given base price 1000
And recurring discount 10%
And one-time activation fee 50
When price is calculated
Then recurring subtotal is 900
And one-time subtotal is 50
And price trace contains base price, discount, and activation fee components
Kenapa breakdown penting?
Karena enterprise customer jarang memperdebatkan total saja. Mereka memperdebatkan alasan total.
Unit test untuk configuration
Configuration test harus memeriksa explanation.
Given product A requires option B
When user selects A without B
Then configuration is invalid
And explanation contains missing required option B
And no price request is allowed
Configuration engine yang tidak bisa menjelaskan error akan membuat support dan sales operation tersandera.
6. Contract Test: API Tidak Boleh Diam-Diam Berubah
Dalam sistem OpenAPI-first, contract test menjaga bahwa implementasi sesuai spec dan perubahan tidak merusak consumer.
Ada dua pendekatan utama:
- Provider contract verification against OpenAPI
- Consumer-driven contract testing
Keduanya berbeda fungsi.
OpenAPI provider verification
Ini memastikan server tidak berbohong terhadap spec.
Contoh:
OpenAPI says:
POST /quotes/{quoteId}/submit-for-approval
returns 409 with ProblemDetails when quote is not priced
Provider test must verify:
- status 409
- content-type application/problem+json
- type stable
- title stable enough
- code machine-readable
- correlationId exists
- invalid fields shape if relevant
Consumer-driven contract
Consumer-driven contract memastikan provider tidak merusak consumer yang benar-benar memakai endpoint tertentu.
Misalnya BFF hanya membutuhkan:
{
"quoteId": "q-123",
"status": "APPROVAL_REQUIRED",
"requiredApprovals": [
{
"approvalType": "FINANCE",
"reasonCode": "DISCOUNT_THRESHOLD_EXCEEDED"
}
]
}
Provider boleh menambah field baru. Provider tidak boleh menghapus field yang dipakai consumer tanpa negotiation.
Contract yang wajib dijaga
Untuk CPQ/OMS, contract yang wajib dites:
- command endpoint
- query endpoint
- error model
- idempotency behavior
- pagination/search response
- task action API
- document artifact API
- event schema
- workflow command API
7. Schema Compatibility Test
Schema-first berarti schema adalah kontrak. Kontrak harus dites.
Schema compatibility test menjawab:
Apakah perubahan schema masih aman untuk producer dan consumer yang berbeda versi?
Contoh event evolution:
QuotePriced.v1
{
"quoteId": "q-123",
"revision": 4,
"total": "1200.00",
"currency": "USD"
}
QuotePriced.v2
{
"quoteId": "q-123",
"revision": 4,
"total": "1200.00",
"currency": "USD",
"priceResultId": "pr-999"
}
Menambah optional field biasanya aman.
Mengubah total dari string decimal menjadi number bisa merusak consumer.
Menghapus currency jelas breaking.
Schema test minimal
Untuk setiap schema event penting:
- sample valid payload harus pass
- sample payload lama harus tetap bisa dibaca jika compatibility dijanjikan
- required field tidak boleh hilang
- enum expansion harus diperlakukan hati-hati
- unknown field policy harus eksplisit
- decimal/money representation harus stabil
- timestamp format harus stabil
Enum compatibility trap
Menambah enum value sering dianggap non-breaking. Padahal consumer lama bisa punya switch tanpa default.
Contoh:
switch (orderStatus) {
case SUBMITTED -> ...;
case IN_PROGRESS -> ...;
case COMPLETED -> ...;
}
Jika producer menambah PARTIALLY_CANCELLED, consumer bisa gagal.
Rule praktis:
Enum pada event publik harus diperlakukan sebagai contract yang mahal untuk diubah.
8. Persistence Integration Test: Jangan Mock Database untuk Hal yang Database Jaga
Jangan mock PostgreSQL untuk menguji:
- unique constraint
- foreign key
- partial index
- optimistic locking
- JSONB query
- transaction isolation
- outbox claim query
- JPA mapping
- lazy loading behavior
- cascade/orphan behavior
Itu semua harus dites dengan database nyata.
Testcontainers cocok untuk ini karena menyediakan dependency nyata yang disposable untuk integration test.
Contoh persistence test penting
@Test
void acceptingSameQuoteRevisionTwiceCreatesOnlyOneOrder() {
QuoteRevisionEntity quote = fixtures.persistApprovedQuoteRevision();
UUID idempotencyKey1 = UUID.fromString("00000000-0000-0000-0000-000000000001");
UUID idempotencyKey2 = UUID.fromString("00000000-0000-0000-0000-000000000002");
OrderRef first = quoteApplication.acceptQuote(quote.id(), quote.version(), idempotencyKey1);
OrderRef second = quoteApplication.acceptQuote(quote.id(), quote.version(), idempotencyKey2);
assertEquals(first.orderId(), second.orderId());
assertEquals(1, orderRepository.countByQuoteRevision(quote.id()));
}
Ada dua lapisan yang dites:
- application logic mengembalikan existing order
- database constraint mencegah duplicate order jika race terjadi
Optimistic locking test
Given two sessions load same quote revision
When both try to update material quote field
Then only one succeeds
And the loser receives conflict
And loser must reload before retry
Jangan hanya mengetes exception. Tes juga mapping ke HTTP 409.
Outbox integration test
Given quote is accepted
When transaction commits
Then quote status is ACCEPTED
And exactly one outbox row exists for QuoteAccepted
And outbox payload contains quoteId, revision, orderIntentId, correlationId
Ini lebih penting daripada mengecek Kafka publish langsung dari service method. Service method tidak boleh publish Kafka sebagai side effect sebelum DB commit.
9. Workflow/BPMN Test: Test Process dalam Potongan Kecil
Workflow test tidak harus selalu menjalankan seluruh order journey. Untuk Camunda 7, workflow lebih sehat dites dalam potongan:
- approval required path
- approval not required path
- rejection path
- rework path
- escalation timer path
- external task success path
- external task failure path
- BPMN error path
- incident path
- compensation path
Camunda 7 mendukung test proses di Java. Gunakan itu untuk menguji model BPMN sebagai executable artifact.
Workflow test harus menghindari business logic besar di BPMN
Test BPMN seharusnya tidak perlu memvalidasi detail pricing. Pricing sudah dites di Pricing Service.
BPMN test harus mengunci orchestration:
Given quote requires finance approval
When approval process starts
Then a Finance Approval user task exists
And business key equals quoteId:revision
And process waits at Finance Approval
Testing timer
Timer harus dites secara eksplisit.
Given finance approval task is open
When SLA duration passes
Then escalation event is triggered
And escalation notification command is emitted
And task priority is raised
Jangan hanya berharap timer berjalan di production.
Testing external task failure
Given order orchestration waits on Reserve Inventory external task
When worker reports failure with retries left
Then process remains retryable
And no fallout case is created yet
When retries are exhausted
Then incident is created
And fallout case projection receives failure signal
Business error dan technical failure harus dites berbeda.
10. Event Test: At-Least-Once Reality
Kafka consumer harus diasumsikan menerima event:
- sekali
- dua kali
- terlambat
- out-of-order untuk aggregate berbeda
- setelah consumer restart
- setelah schema berubah
Jangan tulis consumer yang hanya benar untuk perfect delivery.
Consumer idempotency test
Given OrderSubmitted event E
When consumer receives E twice
Then only one workflow start command is created
And inbox record stores E as processed
Ordering test
Kafka memberi ordering dalam partition, bukan global ordering. Maka partition key harus dites.
Given all events for quote q-123
When events are published
Then partition key is q-123
And consumer sees quote events in aggregate order
DLQ test
Given consumer receives payload that passes envelope schema but fails business validation
When retry budget is exhausted
Then event is moved to dead-letter topic
And operational alert includes eventId, aggregateId, schemaVersion, consumerName
DLQ tanpa observability hanya memindahkan masalah.
11. Cache Test: Redis Harus Dibuktikan Tidak Menjadi Authority
Redis test bukan hanya set lalu get.
Redis test harus membuktikan bahwa stale cache tidak membuat command salah.
Catalog cache test
Given catalog publication v10 is active
And Redis contains catalog v9
When quote configuration starts
Then service detects active publication mismatch
And reloads catalog from source of truth
And does not configure quote using v9
Idempotency fast-path test
Given idempotency result exists in PostgreSQL
And Redis cache is empty
When same idempotency key is retried
Then service returns result from PostgreSQL
And may repopulate Redis
Redis boleh mempercepat. Redis tidak boleh menjadi satu-satunya bukti.
12. E2E Test: Sedikit, Mahal, dan Bernilai Tinggi
E2E test harus sedikit. Tetapi yang ada harus mewakili journey paling kritis.
Minimal E2E suite:
- Standard quote to order happy path
- Quote requires approval then accepted
- Quote rejected then revised then approved
- Order fulfillment partial failure creates fallout case
- Cancel order triggers compensation
- Duplicate accept command returns same order
- Tenant isolation across quote search and task list
- Catalog publication change invalidates stale quote
E2E test bukan tempat memeriksa semua detail
E2E test harus memeriksa milestone.
Contoh:
- quote created
- quote configured
- quote priced with trace id
- approval task created
- approval completed
- quote accepted
- order created
- order orchestration started
- fulfillment step completed
- audit trail query shows full chain
Jangan memasukkan semua assertion kecil ke E2E. Itu membuat test lambat dan rapuh.
13. Security and Tenant Test
Security test wajib berada di beberapa layer.
API authorization test
seller from tenant A cannot read quote from tenant B
finance approver cannot approve quote outside assigned approval scope
viewer cannot submit quote for approval
service token for notification cannot mutate quote
Object-level authorization test
Broken object-level authorization adalah risiko besar pada API enterprise. Maka test harus mencoba mengganti identifier.
GET /quotes/q-owned-by-other-team
Authorization: Bearer valid-token-from-user-same-tenant-but-wrong-region
Expected:
403 or 404 according to disclosure policy
never 200
never partial data leak
Tenant propagation test
Given request has tenant T1
When quote accepted
Then DB rows, outbox event, Kafka header, workflow business context, audit row, and cache key all carry tenant T1
Tenant test yang hanya memeriksa API tidak cukup.
14. Observability and Audit Test
Observability bisa dites. Audit bisa dites.
Correlation test
Given request has X-Correlation-Id C
When quote is accepted
Then application log contains C
And outbox event contains C
And workflow process variable contains C
And audit row contains C
Audit test
Given finance approver approves quote
When audit timeline is queried
Then timeline contains actor, authority snapshot, decision, reason, quote revision, price result id, and timestamp
Audit test penting karena audit yang rusak sering baru ketahuan saat incident, dispute, atau audit eksternal. Saat itu sudah terlambat.
15. CI Pipeline Strategy
CI harus memisahkan test berdasarkan biaya dan signal.
Stage 1: fast checks
- formatting
- compile
- generated code drift
- OpenAPI lint
- schema lint
- unit tests
Target: cepat.
Stage 2: contract and schema
- provider contract verification
- consumer contract verification
- JSON Schema compatibility
- event schema compatibility
- error model compatibility
Target: mencegah breaking change.
Stage 3: integration
- PostgreSQL Testcontainers
- Kafka Testcontainers
- Redis Testcontainers
- JPA mapping
- outbox publisher
- consumer idempotency
Target: membuktikan integration primitives benar.
Stage 4: workflow
- BPMN path tests
- DMN decision tests
- timer tests
- external task tests
- incident tests
Target: mencegah process regression.
Stage 5: ephemeral E2E
- deploy minimal environment
- run critical journey tests
- verify observability hooks
- verify audit chain
Target: release confidence.
16. Test Data Discipline
Test enterprise sering gagal bukan karena logic, tetapi karena data test kacau.
Ciri test data buruk:
- menggunakan fixture besar yang tidak dipahami
- test saling bergantung pada database state
- ID random membuat failure sulit direproduksi
- time bergantung pada clock nyata
- scenario tidak punya nama bisnis
- expected result disalin dari output sistem tanpa reasoning
Ciri test data baik:
- scenario kecil tetapi bermakna
- builder menggunakan vocabulary domain
- time deterministic
- currency deterministic
- tenant/persona eksplisit
- catalog version eksplisit
- approval authority eksplisit
- external system response eksplisit
Part berikutnya akan membahas ini secara khusus.
17. Minimal Test Suite per Capability
Product Catalog
| Scenario | Layer |
|---|---|
| publish catalog with valid offering | integration |
| reject invalid bundle constraint | domain |
| effective date selects correct catalog | domain + persistence |
| cache invalidated after publication | integration |
| old quote keeps catalog snapshot | domain + integration |
Configuration
| Scenario | Layer |
|---|---|
| required option missing | domain |
| incompatible options selected | domain |
| explain invalid configuration | domain |
| deterministic result under same snapshot | property/metamorphic |
| partial configuration not priceable | domain/API |
Pricing
| Scenario | Layer |
|---|---|
| base recurring price calculated | domain |
| discount threshold triggers approval | domain + policy |
| rounding stable | domain |
| price result persisted immutable | integration |
| price trace returned by API | contract/API |
Quote
| Scenario | Layer |
|---|---|
| draft quote edited | domain/API |
| priced quote submitted | domain/API |
| approved quote accepted | E2E |
| stale approval rejected | domain/integration |
| duplicate accept idempotent | integration/E2E |
Order
| Scenario | Layer |
|---|---|
| order created from accepted quote | integration/E2E |
| order decomposed into fulfillment steps | domain/workflow |
| external task success advances workflow | workflow |
| external task failure creates incident | workflow |
| reconciliation resolves unknown outcome | integration/E2E |
18. Failure Modes yang Harus Punya Test
Checklist failure mode:
- duplicate HTTP command
- duplicate Kafka event
- stale quote revision
- stale catalog snapshot
- stale price result
- stale approval
- optimistic lock conflict
- DB constraint violation mapped to 409
- outbox publish failure
- consumer crash after side effect before commit
- workflow process already started
- external task worker timeout
- BPMN error path
- technical incident path
- Redis unavailable
- Redis stale cache
- search projection lag
- tenant mismatch
- unauthorized approval
- audit write failure policy
- document generation retry
- notification duplicate prevention
Jika item ini tidak dites, production yang akan mengetesnya.
19. Anti-Pattern Testing di CPQ/OMS
Anti-pattern 1: E2E sebagai pengganti semua test
E2E lambat dan rapuh. Jangan pakai E2E untuk semua variasi pricing. Pricing variation harus domain test.
Anti-pattern 2: Mock semua repository
Jika repository behavior penting karena transaction dan constraint, mock akan menipu.
Anti-pattern 3: Test berdasarkan implementation detail
Test yang terlalu tahu method private atau urutan internal akan pecah saat refactor sehat.
Anti-pattern 4: Snapshot test untuk response besar tanpa semantic assertion
Snapshot besar mudah disetujui tanpa dibaca. Gunakan assertion semantik.
Anti-pattern 5: Tidak mengetes negative path
Enterprise system lebih sering gagal di path exception daripada happy path.
Anti-pattern 6: Test tanpa tenant/persona
CPQ/OMS enterprise hampir selalu multi-role dan multi-segment. Test tanpa persona sering melewatkan authorization bug.
Anti-pattern 7: Test data “mystery meat”
Fixture besar bernama test-data.json adalah utang engineering.
20. Definition of Done untuk Feature CPQ/OMS
Feature belum selesai jika hanya code sudah jalan.
Definition of Done minimal:
- domain invariant test ada
- API contract updated dan verified
- schema compatibility checked jika ada schema berubah
- persistence integration test ada jika DB berubah
- workflow test ada jika BPMN/DMN berubah
- event test ada jika event berubah
- idempotency behavior dites jika command/event side-effectful
- security/tenant test ada jika object access berubah
- audit assertion ada untuk high-value action
- observability assertion ada untuk async/long-running process
- migration test ada jika schema DB berubah
- E2E journey updated jika critical path berubah
Top engineer tidak hanya bertanya:
Apakah feature selesai?
Ia bertanya:
Bukti otomatis apa yang membuat kita berani mengubah feature ini enam bulan lagi?
21. Kesimpulan
Testing strategy untuk enterprise CPQ/OMS harus dimulai dari risk model.
Unit test menjaga invariant. Contract test menjaga boundary. Schema test menjaga evolution. Integration test menjaga database, messaging, cache, dan persistence reality. Workflow test menjaga orchestration. E2E test menjaga journey kritis. Security test menjaga tenant dan authority. Observability/audit test menjaga kemampuan recovery dan defensibility.
Sistem CPQ/OMS production-grade tidak bisa dibangun dengan keyakinan manual. Ia membutuhkan test suite yang membuktikan:
- lifecycle benar
- data benar
- event benar
- workflow benar
- recovery benar
- authorization benar
- audit benar
Part berikutnya akan membangun fondasi test data dan scenario catalog agar semua test di atas tidak berubah menjadi fixture chaos.
You just completed lesson 43 in deepen practice. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.
Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.