This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch 3.0.0
in repository https://gitbox.apache.org/repos/asf/causeway.git

commit d52017bfcefadfc463fd6bd6aebcd9db7f1ca08e
Author: Dan Haywood <[email protected]>
AuthorDate: Tue Jun 4 06:55:14 2024 +0100

    CAUSEWAY-2873: adds 09-02 exercise to petclinic
---
 .../petclinic/pages/090-integration-testing.adoc   | 189 +++++++++++++++++++++
 1 file changed, 189 insertions(+)

diff --git 
a/antora/components/tutorials/modules/petclinic/pages/090-integration-testing.adoc
 
b/antora/components/tutorials/modules/petclinic/pages/090-integration-testing.adoc
index bc923a72f6..df0599dafe 100644
--- 
a/antora/components/tutorials/modules/petclinic/pages/090-integration-testing.adoc
+++ 
b/antora/components/tutorials/modules/petclinic/pages/090-integration-testing.adoc
@@ -188,3 +188,192 @@ public void cannot_book_in_the_past() {
             .hasMessage("Must book in the future");
 }
 ----
+
+[#exercise-9-2-adds-visit-fixture]
+== Ex 9.2: Adds fixture for ``Visit``s
+
+Currently we have a fixture for ``PetOwner``s and their ``Pet``s, but none for 
``Visit``s.
+If we want to write additional integration tests for ``Visit``s also, then 
it's a good idea to have some fixtures.
+They can also be used when prototyping.
+
+In this exercise we'll therefore add a new fixture for the `visit` module.
+
+
+=== Solution
+
+[source,bash,subs="attributes+"]
+----
+git checkout tags/{tag-version}/09-02-adds-visit-fixture
+mvn clean install
+----
+
+=== Tasks
+
+* copy in the following persona enum (we'll add the `Builder` next):
++
+[source,java]
+.Visit_persona.java
+----
+/**
+ * Returns the most recent Visit, or the one scheduled.
+ */
+@RequiredArgsConstructor
+public enum Visit_persona
+implements Persona<Visit, Visit_persona.Builder> {
+
+    JAMAL_VISITS(PetOwner_persona.JAMAL),
+    CAMILA_VISITS(PetOwner_persona.CAMILA),
+    ARJUN_VISITS(PetOwner_persona.ARJUN),
+    NIA_VISITS(PetOwner_persona.NIA),
+    OLIVIA_VISITS(PetOwner_persona.OLIVIA),
+    LEILA_VISITS(PetOwner_persona.LEILA),
+    MATT_VISITS(PetOwner_persona.MATT),
+    BENJAMIN_VISITS(PetOwner_persona.BENJAMIN),
+    JESSICA_VISITS(PetOwner_persona.JESSICA),
+    DANIEL_VISITS(PetOwner_persona.DANIEL);
+
+    private final PetOwner_persona petOwner_p;
+
+    @Override
+    public Builder builder() {
+        return new Builder().setPersona(this);
+    }
+
+    @Override
+    public Visit findUsing(final ServiceRegistry serviceRegistry) {
+        final var owner = petOwner_p.findUsing(serviceRegistry);
+        final var visits = serviceRegistry.lookupService(VisitRepository.class)
+                .map(x -> x.findByPetOwner(owner))
+                .orElseThrow();
+        return lastOf(visits);
+    }
+
+    private static Visit lastOf(List<Visit> visits) {
+        return visits.get(visits.size()-1);
+    }
+
+    // ...
+}
+----
+
+* now add in the `Builder`:
++
+[source,java]
+.Visit_persona.java
+----
+@RequiredArgsConstructor
+public enum Visit_persona
+implements Persona<Visit, Visit_persona.Builder> {
+    // ...
+
+    @Accessors(chain = true)
+    public static class Builder extends BuilderScriptWithResult<Visit> {
+
+        @Getter @Setter private Visit_persona persona;
+
+        @Override
+        protected Visit buildResult(final ExecutionContext ec) {
+
+            final var petOwner = ec.executeChildT(this, persona.petOwner_p);   
 // <.>
+
+            petOwner.getPets().forEach(pet -> {
+
+                // in the past
+                final var numVisits = fakeDataService.ints().between(2, 4);    
 // <.>
+                for (var i = 0; i < numVisits; i++) {
+                    final var daysAgo = fakeDataService.ints().between(5, 500);
+                    final var minsInTheDay = randomAppointmentTime();
+                    final var appointmentTime = 
randomAppointmentTimeFromToday(-daysAgo, minsInTheDay);
+                    wrapperFactory.wrapMixin(PetOwner_bookVisit.class, 
petOwner, SyncControl.control().withSkipRules()).act(pet, appointmentTime);
+                }
+
+                // in the future
+                if (fakeDataService.booleans().coinFlip()) {                   
 // <.>
+                    final var daysAhead = fakeDataService.ints().between(1, 
10);
+                    final var minsInTheDay = randomAppointmentTime();
+                    final var appointmentTime = 
randomAppointmentTimeFromToday(daysAhead, minsInTheDay);
+                    wrapperFactory.wrapMixin(PetOwner_bookVisit.class, 
petOwner, SyncControl.control().withSkipRules()).act(pet, appointmentTime);
+                }
+            });
+
+            final var numDaysAgo = fakeDataService.ints().between(2, 100);
+            final var lastVisit = 
clockService.getClock().nowAsLocalDate().minusDays(numDaysAgo);
+            petOwner.setLastVisit(lastVisit);
+
+            final var visits = wrapperFactory.wrapMixin(PetOwner_visits.class, 
petOwner, SyncControl.control().withSkipRules()).coll();
+            return lastOf(visits);
+        }
+
+        private LocalDateTime randomAppointmentTimeFromToday(int days, int 
appointmentTime) {
+            return 
clockService.getClock().nowAsLocalDate().atStartOfDay().plusDays(days).plusMinutes(appointmentTime);
+        }
+
+        private int randomAppointmentTime() {
+            return (9 * 60) + (fakeDataService.ints().between(0, 32) * 15);
+        }
+
+        // -- DEPENDENCIES
+
+        @Inject ClockService clockService;
+        @Inject FakeDataService fakeDataService;
+    }
+}
+----
+<.> Using the supplied `ExecutionContext`, we can execute any prerequisites 
fixtures (in this case to obtain the corresponding `PetOwner` and ``Pet``s)
+<.> we create between 2 and 4 ``Visit``s in the past for each ``Pet``.
+<.> we create a ``Visit`` in the future for approximately every other ``Pet``.
+
+* add in the `PersistAll`:
++
+[source,java]
+.Visit_persona.java
+----
+@RequiredArgsConstructor
+public enum Visit_persona
+implements Persona<Visit, Visit_persona.Builder> {
+    // ...
+
+    public static class PersistAll
+            extends PersonaEnumPersistAll<Visit, Visit_persona, Builder> {
+        public PersistAll() {
+            super(Visit_persona.class);
+        }
+    }
+}
+----
+
+* update the top-level `DomainAppDemo` fixture to create visits rather than 
petowners/pets:
++
+[source,java]
+.DomainAppDemo.java
+----
+public class DomainAppDemo extends FixtureScript {
+
+    @Override
+    protected void execute(final ExecutionContext ec) {
+        ec.executeChildren(this, 
moduleWithFixturesService.getTeardownFixture());
+        ec.executeChild(this, new Visit_persona.PersistAll());      // <.>
+    }
+
+    @Inject ModuleWithFixturesService moduleWithFixturesService;
+}
+----
+<.> Because `Visit_persona` automatically creates its prereqs, there's no need 
to run the `PetOwner_persona` fixture.
++
+[NOTE]
+====
+Currently the `PetOwner_persona` isn't rerunnable, so we have to take some 
care.
+However, it's easy to refactor if we wanted to:
+
+[source,java]
+.PetOwner_persona.java
+----
+val petOwner = serviceRegistry.lookupService(PetOwners.class)
+                    .map(x -> x.findByNameExact(persona.name))
+                    .orElseGet(
+                        () -> petOwners.create(persona.name, null, null, null)
+                    );
+----
+
+Note that the above change _isn't_ one of the exercises.
+====

Reply via email to