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);
+ }
+}