Skip to content

Commit 9deabaf

Browse files
ArneLimburgnilshartmann96
authored andcommitted
fix: Use pact matchers
1 parent 096e44c commit 9deabaf

3 files changed

Lines changed: 89 additions & 3 deletions

File tree

.github/workflows/build.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ jobs:
8888
working-directory: customer-client
8989

9090
- name: Build customer-service
91-
run: mvn clean package -f customer-service/pom.xml -Dservice.name=customer-service -DskipTests
91+
run: mvn clean package -f customer-service/pom.xml -Dservice.name=customer-service
9292

9393
- name: Build billing-service
9494
run: mvn clean package -f billing-service/pom.xml -Dservice.name=billing-service

README.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,85 @@ Nach der Umsetzung:
4848
- Der erzeugte Pact dokumentiert die Einschränkungen flexibel: Der `Location`-Header muss dem Regex-Muster entsprechen, die Kundennummer muss ein String sein – nicht aber einen bestimmten Wert haben.
4949
- Der Provider-Test `CustomerServiceTest.java` verifiziert den Contract erfolgreich gegen die echte Implementierung.
5050

51+
---
52+
53+
## Übung: Pact States
54+
55+
### Ausgangssituation
56+
57+
Im Branch `playwright-pact-states` ist der Consumer-Test `create-customer.spec.ts` bereits auf Pact umgebaut. Der Test simuliert folgendes Szenario:
58+
59+
1. Ein neuer Kunde (Sherlock Holmes) wird per POST angelegt.
60+
2. Danach wird die Kundenliste per GET abgerufen – und Sherlock Holmes soll darin erscheinen.
61+
62+
Dabei gibt es zwei Probleme:
63+
64+
- Der Test erstellt den `PactV4`-Provider noch manuell mit `new PactV4(...)` und einem eigenen Consumer-Namen, statt die gemeinsame Hilfsfunktion `createProvider()` aus `pact-proxy.ts` zu nutzen.
65+
- Die GET-Interaction deklariert keine Vorbedingung: Der Provider-Test (`CustomerServiceTest.java`) weiß nicht, dass Sherlock Holmes für diese Verifikation bereits in der Datenbank vorhanden sein muss.
66+
- Der `TestCustomerRepository` setzt seinen Zustand zwischen den einzelnen Pact-Interaktionen nicht zurück, sodass Daten aus einer Interaktion in die nächste „auslaufen" können.
67+
68+
### Aufgabe
69+
70+
#### Consumer-Seite (`create-customer.spec.ts`)
71+
72+
1. Ersetze `import { setupApiProxy } from './pact-proxy'` durch `import { createProvider, setupApiProxy } from './pact-proxy'`.
73+
2. Ersetze die manuelle `new PactV4({...})`-Instanziierung durch den Aufruf `createProvider()`.
74+
3. Füge der GET-Interaction eine Vorbedingung hinzu:
75+
76+
```typescript
77+
await provider
78+
.addInteraction()
79+
.given('Sherlock is available')
80+
.uponReceiving('a request to get all customers (inkl. Sherlock)')
81+
.withRequest('GET', '/customers/')
82+
// ...
83+
```
84+
85+
#### Provider-Seite (`CustomerServiceTest.java`)
86+
87+
Implementiere eine State-Handler-Methode, die für den Zustand `"Sherlock is available"` Sherlock Holmes in das Repository einfügt:
88+
89+
```java
90+
@Inject
91+
private CustomerRepository customerRepository;
92+
93+
@State("Sherlock is available")
94+
public void insertSherlock() {
95+
customerRepository.persist(new Customer(new CustomerName("Sherlock Holmes")));
96+
}
97+
```
98+
99+
#### Provider-Seite: Zustand zurücksetzen (`TestCustomerRepository.java`)
100+
101+
Damit der Repository-Zustand nach jeder HTTP-Anfrage zurückgesetzt wird (Testfälle sollen sich nicht gegenseitig beeinflussen), erweitere `TestCustomerRepository` um eine Reset-Methode. Entferne gleichzeitig die `@RequestScoped`-Annotation, da der Repository-Scope durch die übergeordnete Klasse bestimmt wird:
102+
103+
```java
104+
@Specializes
105+
public class TestCustomerRepository extends CustomerRepository {
106+
107+
public void reset(@Observes @Destroyed(RequestScoped.class) Object event) {
108+
super.initialize();
109+
}
110+
}
111+
```
112+
113+
Damit der Reset auch den internen Zähler für Kundennummern zurücksetzt, muss in `CustomerRepository.initialize()` der Zähler explizit auf 0 gesetzt werden:
114+
115+
```java
116+
@PostConstruct
117+
public void initialize() {
118+
CUSTOMER_NUMBERS.set(0);
119+
customers = new ConcurrentHashMap<>();
120+
// ...
121+
}
122+
```
123+
124+
### Ziel
125+
126+
Nach der Umsetzung:
127+
128+
- Nutzt `create-customer.spec.ts` die gemeinsame `createProvider()`-Hilfsfunktion und erzeugt einen Pact unter dem Consumer-Namen `customer-client`.
129+
- Die GET-Interaction trägt den State `"Sherlock is available"`, der im erzeugten Pact-JSON dokumentiert ist.
130+
- Der Provider-Test `CustomerServiceTest.java` kann alle Interaktionen erfolgreich verifizieren, weil er den State-Handler ausführt, bevor er die GET-Anfrage an den echten Service schickt.
131+
- Der Datenbankzustand wird zwischen den Interaktionen automatisch zurückgesetzt, sodass die Tests unabhängig voneinander laufen.
132+

customer-client/tests/create-customer.spec.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* limitations under the License.
1515
*/
1616
import { test, expect } from '@playwright/test';
17+
import { MatchersV3 } from '@pact-foundation/pact';
1718
import { createProvider, setupApiProxy } from './pact-proxy';
1819

1920
test.describe('Neuer Kunde', () => {
@@ -42,7 +43,10 @@ test.describe('Neuer Kunde', () => {
4243
})
4344
.willRespondWith(201, (builder) => {
4445
builder.headers({
45-
Location: 'http://localhost:50376/customers/1',
46+
Location: MatchersV3.regex(
47+
'http://localhost/customers/\\d+',
48+
'http://localhost/customers/1',
49+
),
4650
});
4751
});
4852

@@ -56,7 +60,7 @@ test.describe('Neuer Kunde', () => {
5660
{ number: '007', name: 'James Bond' },
5761
{ number: '0815', name: 'Max Mustermann' },
5862
{ number: '0816', name: 'Erika Mustermann' },
59-
{ number: '1', name: 'Sherlock Holmes' },
63+
{ number: MatchersV3.like('1'), name: 'Sherlock Holmes' },
6064
]);
6165
})
6266
.executeTest(async (mockServer) => {

0 commit comments

Comments
 (0)