You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2022/05/04 16:44:22 UTC

[isis] 01/01: ISIS-3037: adds Persona interface, improves existing docs

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

danhaywood pushed a commit to branch ISIS-3037
in repository https://gitbox.apache.org/repos/asf/isis.git

commit 48828ec2d94f33a6d1685d28e0993ac21c7f8f9c
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Wed May 4 17:43:59 2022 +0100

    ISIS-3037: adds Persona interface, improves existing docs
---
 .../pages/fixture-scripts/api-and-usage.adoc       | 168 +++++++++------------
 .../applib/personas/BuilderScriptAbstract.java     |   2 +-
 ...{PersonaWithBuilderScript.java => Persona.java} |  24 ++-
 .../applib/personas/PersonaWithBuilderScript.java  |   4 +-
 .../fixtures/applib/personas/dom/Customer.java     |  14 ++
 .../applib/personas/dom/CustomerRepository.java    |  21 +++
 .../personas/fixtures/CustomerBuilderScript.java   |  24 +++
 .../applib/personas/fixtures/Customer_persona.java |  33 ++++
 .../personas/fixtures/ScenarioFixtureScript.java   |  24 +++
 9 files changed, 203 insertions(+), 111 deletions(-)

diff --git a/testing/fixtures/adoc/modules/fixtures/pages/fixture-scripts/api-and-usage.adoc b/testing/fixtures/adoc/modules/fixtures/pages/fixture-scripts/api-and-usage.adoc
index c5920d1829..a8e5f40474 100644
--- a/testing/fixtures/adoc/modules/fixtures/pages/fixture-scripts/api-and-usage.adoc
+++ b/testing/fixtures/adoc/modules/fixtures/pages/fixture-scripts/api-and-usage.adoc
@@ -201,153 +201,131 @@ At the same time, "Persona" instances of entity classes help the developer becom
 For example, "Steve Single" the Customer might be 21, single and no kids, whereas vs "Meghan Married-Mum" the Customer might be married 35 with 2 kids.
 Using "Steve" vs "Meghan" immediately informs the developer about the particular scenario being explored.
 
-The xref:refguide:testing:index/fixtures/applib/personas/PersonaWithBuilderScript.adoc[PersonaWithBuilderScript] and
-xref:refguide:testing:index/fixtures/applib/personas/PersonaWithFinder.adoc[PersonaWithFinder]
- interfaces are intended to be implemented typically by "persona" enums, where each enum instance captures the essential data of some persona.
+The xref:refguide:testing:index/fixtures/applib/personas/Persona.adoc[Persona] interfaces is intended to be implemented typically by "persona" enums, where each enum instance captures the essential data of some persona.
+xref:refguide:testing:index/fixtures/applib/personas/Persona.adoc[Persona] in turn unifies two lower-level interfaces, xref:refguide:testing:index/fixtures/applib/personas/PersonaWithBuilderScript.adoc[PersonaWithBuilderScript] and
+xref:refguide:testing:index/fixtures/applib/personas/PersonaWithFinder.adoc[PersonaWithFinder].
+
 So, going back to the previous example, we might have:
 
 [source,xml]
+.Customer_persona.java
 ----
+@Getter
+@RequiredArgsConstructor
 public enum Customer_persona
-        implements PersonaWithBuilderScript<..>, PersonaWithFinder<..> {
+        implements Persona<Customer, CustomerBuilderScript> {
 
-    SteveSingle("Steve", "Single", 21, MaritalStatus.SINGLE, 0)
-    MeghanMarriedMum("Meghan", "Married-Mum", 35, MaritalStatus.MARRIED, 2);
-    ...
-}
-----
+    SteveSingle(1, "Steve", "Single", 21),
+    MeghanMarriedMum(2, "Meghan", "Married-Mum", 35);
 
-The xref:refguide:testing:index/fixtures/applib/personas/PersonaWithBuilderScript.adoc[PersonaWithBuilderScript] interface means that this enum is able to act as a factory for a xref:refguide:testing:index/fixtures/applib/personas/BuilderScriptAbstract.adoc[BuilderScriptAbstract].
-This is a specialization of `FixtureScript` that is used to actually create the entity (customer, or whatever), using the data taken out of the enum instance:
-
-[source,xml]
-----
-public interface PersonaWithBuilderScript<T, F extends BuilderScriptAbstract<T,F>>  {
-    F builder();
-}
-----
+    private final int id;
+    private final String firstName;
+    private final String lastName;
+    private final int age;
 
-The xref:refguide:testing:index/fixtures/applib/personas/PersonaWithFinder.adoc[PersonaWithFinder] interface meanwhile indicates that the enum can "lookup" its corresponding entity from the appropriate repository domain service:
+    @Override
+    public CustomerBuilderScript builder() {                            // <.>
+        return new CustomerBuilderScript(this);                         // <.>
+    }
 
-[source,xml]
-----
-public interface PersonaWithFinder<T> {
-    T findUsing(final ServiceRegistry2 serviceRegistry);
+    @Override
+    public Customer findUsing(ServiceRegistry serviceRegistry) {        // <.>
+        return serviceRegistry.lookupServiceElseFail(CustomerRepository.class).findById(id).orElseThrow();
+    }
 
 }
 ----
+<.> from xref:refguide:testing:index/fixtures/applib/personas/PersonaWithBuilderScript.adoc[PersonaWithBuilderScript]
+<.> it's idiomatic to just pass self to the build script.
+<.> from xref:refguide:testing:index/fixtures/applib/personas/PersonaWithFinder.adoc[PersonaWithFinder]
 
-The xref:docs:starters:simpleapp.adoc[SimpleApp] starter app provides a sample implementation of these interfaces:
+Here the `CustomerBuilderScript` is a subclass of xref:refguide:testing:index/fixtures/applib/personas/BuilderScriptAbstract.adoc[BuilderScriptAbstract],  a specialized fixture script that acts as a factory of the domain object (`Customer`, in this case), usig the data taken out of the enum instance.
+In many cases a builder script will create a single top-level object, so the related xref:refguide:testing:index/fixtures/applib/personas/BuilderScriptWithResult.adoc[BuilderScriptWithResult] removes some boilerplate:
 
 [source,java]
+.CustomerBuilderScript.java
 ----
-@lombok.AllArgsConstructor
-public enum SimpleObject_persona
-        implements PersonaWithBuilderScript<SimpleObject, SimpleObjectBuilder>,
-                   PersonaWithFinder<SimpleObject> {
-    FOO("Foo"),
-    BAR("Bar"),
-    BAZ("Baz"),
-    FRODO("Frodo"),
-    FROYO("Froyo"),
-    FIZZ("Fizz"),
-    BIP("Bip"),
-    BOP("Bop"),
-    BANG("Bang"),
-    BOO("Boo");
-
-    private final String name;
+@RequiredArgsConstructor
+public class CustomerBuilderScript extends BuilderScriptWithResult<Customer> {
 
-    @Override
-    public SimpleObjectBuilder builder() {
-        return new SimpleObjectBuilder().setName(name);
-    }
+    private final Customer_persona persona;
 
     @Override
-    public SimpleObject findUsing(final ServiceRegistry2 serviceRegistry) {
-        SimpleObjectRepository simpleObjectRepository =
-            serviceRegistry.lookupService(SimpleObjectRepository.class);
-        return simpleObjectRepository.findByNameExact(name);
+    protected Customer buildResult(ExecutionContext ec) {
+        return customerRepository.create(persona.getFirstName(), persona.getLastName(), persona.getAge());
     }
+
+    @Inject CustomerRepository customerRepository;
 }
 ----
 
-where `SimpleObjectBuilder` in turn is:
+Put together, the persona enums provide the "what" - hard-coded values for certain key data that the developer becomes very familiar with - while the builder provides the "how-to".
 
-[source,java]
-----
-import javax.inject.Inject;
-import lombok.Accessors;
-import lombok.Getter;
-import lombok.Setter;
 
-@Accessors(chain = true)
-public class SimpleObjectBuilder
-            extends BuilderScriptAbstract<SimpleObject, SimpleObjectBuilder> {
+=== Using within a Scenario Fixture Script
 
-    @Getter @Setter
-    private String name;                                    // <1>
+With these definitions in place, the payback is that within the context of a parent fixture script, a new domain object can be easily built and retrieved later:
+
+[source,java]
+.ScenarioFixtureScript.java
+----
+public class ScenarioFixtureScript extends FixtureScript {
 
     @Override
-    protected void execute(final ExecutionContext ec) {
-        checkParam("name", ec, String.class);               // <2>
-        object = wrap(simpleObjects).create(name);
-    }
+    protected void execute(ExecutionContext executionContext) {
 
-    @Getter
-    private SimpleObject object;                            // <3>
+        // build it ..
+        Customer steve = Customer_persona.SteveSingle.build(this, executionContext);
 
-    @Inject
-    SimpleObjects simpleObjects;
+        // ... look it up
+        Customer steve2 = Customer_persona.SteveSingle.findUsing(serviceRegistry);
+    }
 }
 ----
-<1> The persona class should set this value (copied from its own state)
-<2> the inherited "checkParam" is used to ensure that a value is set
-<3> the created entity is provided as an output
+
+It's also possible to use personas (or indeed any fixture scripts) from integration tests.
+This is discussed in the next section.
+
 
 
-== Using within Tests
+=== Using within Tests
 
 Fixture scripts can be called from integration tests just the same way that fixture scripts can call one another.
 
-For example, here's part of an integration test from the xref:docs:starters:simpleapp.adoc[SimpleApp] starter app:
+Using the example persona from the previous section, we can use the xref:refguide:testing:index/fixtures/applib/fixturescripts/FixtureScripts.adoc[FixtureScripts] domain service to build the fixture.
 
 [source,java]
-.SimpleObject_IntegTest.java
+.Customer_IntegTest.java
 ----
-@Transactional
-public class SimpleObject_IntegTest extends SimpleModuleIntegTestAbstract {
+public class Customer_IntegTest {
 
-    SimpleObject simpleObject;
+    @Inject FixtureScripts fixtureScripts;
+    @Inject ServiceRegistry serviceRegistry;
 
     @BeforeEach
-    void setUp() {
-        // given
-        simpleObject = fixtureScripts.runPersona(SimpleObject_persona.FOO); // <.>
-    }
-
-    public static class updateName extends SimpleObject_IntegTest {
+    public void setup() {
 
-        @Test
-        void can_be_updated_directly() {
+        // build ...
+        Customer steve = fixtureScripts.runPersona(Customer_persona.SteveSingle);       // <.>
 
-            // when
-            wrap(simpleObject).updateName("new name");
-            transactionService.flushTransaction();
+    }
 
-            // then
-            assertThat(wrap(simpleObject).getName()).isEqualTo("new name");
-        }
+    @Test
+    public void update_customer() {
 
-        // ...
+        // ... look it up
+        Customer steve = Customer_persona.SteveSingle.findUsing(serviceRegistry);   // <.>
     }
 }
 ----
-<.> runs a persona fixture script and stores the resultant domain object for testing.
+<.> runs a persona fixture script
+<.> looks up the domain object.
+An alternative design would be to simply store the domain object as a field.
 
-Put together, the persona enums provide the "what" - hard-coded values for certain key data that the developer becomes very familiar with - while the builder provides the "how-to".
 
-These builder scripts (xref:refguide:testing:index/fixtures/applib/personas/BuilderScriptAbstract.adoc[BuilderScriptAbstract] implementations) can be used independently of the enum personas.
+=== More sophisticated use cases
+
+Although its idiomatic for builder scripts (xref:refguide:testing:index/fixtures/applib/personas/BuilderScriptAbstract.adoc[BuilderScriptAbstract] implementations) and persona enums to come in pairs, there's no requirement to do so; builder scripts  can be used independently of the enum personas.
 And for more complex entity -where there might be many potential values that need to be provided
 - the builder script can automatically default some or even all of these values.
 
diff --git a/testing/fixtures/applib/src/main/java/org/apache/isis/testing/fixtures/applib/personas/BuilderScriptAbstract.java b/testing/fixtures/applib/src/main/java/org/apache/isis/testing/fixtures/applib/personas/BuilderScriptAbstract.java
index 927cca24da..f61e530ea6 100644
--- a/testing/fixtures/applib/src/main/java/org/apache/isis/testing/fixtures/applib/personas/BuilderScriptAbstract.java
+++ b/testing/fixtures/applib/src/main/java/org/apache/isis/testing/fixtures/applib/personas/BuilderScriptAbstract.java
@@ -75,7 +75,7 @@ extends FixtureScript implements FixtureScriptWithExecutionStrategy {
     // -- RUN PERSONAS
 
     public T objectFor(
-            final PersonaWithBuilderScript<BuilderScriptAbstract<T>> persona,
+            final PersonaWithBuilderScript<T, BuilderScriptAbstract<T>> persona,
             final ExecutionContext executionContext) {
 
         if(persona == null) {
diff --git a/testing/fixtures/applib/src/main/java/org/apache/isis/testing/fixtures/applib/personas/PersonaWithBuilderScript.java b/testing/fixtures/applib/src/main/java/org/apache/isis/testing/fixtures/applib/personas/Persona.java
similarity index 53%
copy from testing/fixtures/applib/src/main/java/org/apache/isis/testing/fixtures/applib/personas/PersonaWithBuilderScript.java
copy to testing/fixtures/applib/src/main/java/org/apache/isis/testing/fixtures/applib/personas/Persona.java
index a9eafd0144..01597379a9 100644
--- a/testing/fixtures/applib/src/main/java/org/apache/isis/testing/fixtures/applib/personas/PersonaWithBuilderScript.java
+++ b/testing/fixtures/applib/src/main/java/org/apache/isis/testing/fixtures/applib/personas/Persona.java
@@ -18,24 +18,22 @@
  */
 package org.apache.isis.testing.fixtures.applib.personas;
 
+import org.apache.isis.applib.services.registry.ServiceRegistry;
+import org.apache.isis.testing.fixtures.applib.fixturescripts.FixtureScript;
+
 /**
- * Intended for persona enums to implement, to provide an instance of a {@link BuilderScriptAbstract} in order to
- * instantiate an instance of the persona (normally in the form of a domain entity or set of related domain entities).
- *
- * <p>
- *     ({@link BuilderScriptAbstract} is a specialization of
- *     {@link org.apache.isis.testing.fixtures.applib.fixturescripts.FixtureScript}).
- * </p>
+ * Unifies {@link PersonaWithFinder} and {@link PersonaWithBuilderScript}, so that an implementation (usually
+ * an enum) can both be {@link #build(FixtureScript, FixtureScript.ExecutionContext)} built) (in the context of an
+ * existing {@link FixtureScript}) and {@link PersonaWithFinder#findUsing(ServiceRegistry) found}.
  *
- * @see PersonaWithFinder
  * @since 2.x {@index}
  */
-public interface PersonaWithBuilderScript<T extends BuilderScriptAbstract<?>>  {
+public interface Persona<T, B extends BuilderScriptAbstract<T>>
+    extends PersonaWithFinder<T>, PersonaWithBuilderScript<T, B> {
 
-    /**
-     * Returns a {@link BuilderScriptAbstract} to use to instantiate this persona.
-     */
-    T builder();
+    default T build(final FixtureScript parentFixtureScript, FixtureScript.ExecutionContext executionContext) {
+        return builder().build(parentFixtureScript, executionContext).getObject();
+    }
 
 }
 
diff --git a/testing/fixtures/applib/src/main/java/org/apache/isis/testing/fixtures/applib/personas/PersonaWithBuilderScript.java b/testing/fixtures/applib/src/main/java/org/apache/isis/testing/fixtures/applib/personas/PersonaWithBuilderScript.java
index a9eafd0144..8ddfcd9ab0 100644
--- a/testing/fixtures/applib/src/main/java/org/apache/isis/testing/fixtures/applib/personas/PersonaWithBuilderScript.java
+++ b/testing/fixtures/applib/src/main/java/org/apache/isis/testing/fixtures/applib/personas/PersonaWithBuilderScript.java
@@ -30,12 +30,12 @@ package org.apache.isis.testing.fixtures.applib.personas;
  * @see PersonaWithFinder
  * @since 2.x {@index}
  */
-public interface PersonaWithBuilderScript<T extends BuilderScriptAbstract<?>>  {
+public interface PersonaWithBuilderScript<T, B extends BuilderScriptAbstract<T>>  {
 
     /**
      * Returns a {@link BuilderScriptAbstract} to use to instantiate this persona.
      */
-    T builder();
+    B builder();
 
 }
 
diff --git a/testing/fixtures/applib/src/test/java/org/apache/isis/testing/fixtures/applib/personas/dom/Customer.java b/testing/fixtures/applib/src/test/java/org/apache/isis/testing/fixtures/applib/personas/dom/Customer.java
new file mode 100644
index 0000000000..833005f568
--- /dev/null
+++ b/testing/fixtures/applib/src/test/java/org/apache/isis/testing/fixtures/applib/personas/dom/Customer.java
@@ -0,0 +1,14 @@
+package org.apache.isis.testing.fixtures.applib.personas.dom;
+
+import lombok.Builder;
+import lombok.Data;
+
+@Data
+@Builder
+public class Customer {
+
+    private String firstName;
+    private String lastName;
+    private int age;
+
+}
diff --git a/testing/fixtures/applib/src/test/java/org/apache/isis/testing/fixtures/applib/personas/dom/CustomerRepository.java b/testing/fixtures/applib/src/test/java/org/apache/isis/testing/fixtures/applib/personas/dom/CustomerRepository.java
new file mode 100644
index 0000000000..b13fbeda68
--- /dev/null
+++ b/testing/fixtures/applib/src/test/java/org/apache/isis/testing/fixtures/applib/personas/dom/CustomerRepository.java
@@ -0,0 +1,21 @@
+package org.apache.isis.testing.fixtures.applib.personas.dom;
+
+import java.util.Optional;
+
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class CustomerRepository {
+
+    public Customer create(String firstName, String lastName, int age) {
+        return Customer.builder()
+                .firstName(firstName)
+                .lastName(lastName)
+                .age(age)
+                .build();
+    }
+
+    public Optional<Customer> findById(int id) {
+        return Optional.empty();
+    }
+}
diff --git a/testing/fixtures/applib/src/test/java/org/apache/isis/testing/fixtures/applib/personas/fixtures/CustomerBuilderScript.java b/testing/fixtures/applib/src/test/java/org/apache/isis/testing/fixtures/applib/personas/fixtures/CustomerBuilderScript.java
new file mode 100644
index 0000000000..fdaf3689cd
--- /dev/null
+++ b/testing/fixtures/applib/src/test/java/org/apache/isis/testing/fixtures/applib/personas/fixtures/CustomerBuilderScript.java
@@ -0,0 +1,24 @@
+package org.apache.isis.testing.fixtures.applib.personas.fixtures;
+
+import javax.inject.Inject;
+
+import org.apache.isis.testing.fixtures.applib.personas.BuilderScriptWithResult;
+import org.apache.isis.testing.fixtures.applib.personas.dom.Customer;
+import org.apache.isis.testing.fixtures.applib.personas.dom.CustomerRepository;
+
+import lombok.RequiredArgsConstructor;
+
+
+@RequiredArgsConstructor
+public class CustomerBuilderScript extends BuilderScriptWithResult<Customer> {
+
+    private final Customer_persona persona;
+
+    @Override
+    protected Customer buildResult(ExecutionContext ec) {
+        return customerRepository.create(persona.getFirstName(), persona.getLastName(), persona.getAge());
+    }
+
+    @Inject CustomerRepository customerRepository;
+
+}
diff --git a/testing/fixtures/applib/src/test/java/org/apache/isis/testing/fixtures/applib/personas/fixtures/Customer_persona.java b/testing/fixtures/applib/src/test/java/org/apache/isis/testing/fixtures/applib/personas/fixtures/Customer_persona.java
new file mode 100644
index 0000000000..7ed18efc57
--- /dev/null
+++ b/testing/fixtures/applib/src/test/java/org/apache/isis/testing/fixtures/applib/personas/fixtures/Customer_persona.java
@@ -0,0 +1,33 @@
+package org.apache.isis.testing.fixtures.applib.personas.fixtures;
+
+import org.apache.isis.applib.services.registry.ServiceRegistry;
+import org.apache.isis.testing.fixtures.applib.personas.Persona;
+import org.apache.isis.testing.fixtures.applib.personas.dom.Customer;
+import org.apache.isis.testing.fixtures.applib.personas.dom.CustomerRepository;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@Getter
+@RequiredArgsConstructor
+public enum Customer_persona
+        implements Persona<Customer, CustomerBuilderScript> {
+
+    SteveSingle(1, "Steve", "Single", 21),
+    MeghanMarriedMum(2, "Meghan", "Married-Mum", 35);
+
+    private final int id;
+    private final String firstName;
+    private final String lastName;
+    private final int age;
+
+    @Override
+    public CustomerBuilderScript builder() {
+        return new CustomerBuilderScript(this);
+    }
+
+    @Override
+    public Customer findUsing(ServiceRegistry serviceRegistry) {
+        return serviceRegistry.lookupServiceElseFail(CustomerRepository.class).findById(id).orElseThrow();
+    }
+}
diff --git a/testing/fixtures/applib/src/test/java/org/apache/isis/testing/fixtures/applib/personas/fixtures/ScenarioFixtureScript.java b/testing/fixtures/applib/src/test/java/org/apache/isis/testing/fixtures/applib/personas/fixtures/ScenarioFixtureScript.java
new file mode 100644
index 0000000000..48d51f2001
--- /dev/null
+++ b/testing/fixtures/applib/src/test/java/org/apache/isis/testing/fixtures/applib/personas/fixtures/ScenarioFixtureScript.java
@@ -0,0 +1,24 @@
+package org.apache.isis.testing.fixtures.applib.personas.fixtures;
+
+import javax.inject.Inject;
+
+import org.apache.isis.testing.fixtures.applib.fixturescripts.FixtureScript;
+import org.apache.isis.testing.fixtures.applib.personas.BuilderScriptWithResult;
+import org.apache.isis.testing.fixtures.applib.personas.dom.Customer;
+import org.apache.isis.testing.fixtures.applib.personas.dom.CustomerRepository;
+
+import lombok.RequiredArgsConstructor;
+
+
+public class ScenarioFixtureScript extends FixtureScript {
+
+    @Override
+    protected void execute(ExecutionContext executionContext) {
+
+        // build it ..
+        Customer steve = Customer_persona.SteveSingle.build(this, executionContext);
+
+        // ... look it up
+        Customer steve2 = Customer_persona.SteveSingle.findUsing(serviceRegistry);
+    }
+}