Add reference documentation chapter on 'Testing'.
Resolves gh-102.
This commit is contained in:
@@ -13,7 +13,7 @@ _Table of Contents_
|
||||
3. <<geode-auto-configuration-disable>>
|
||||
4. <<geode-gemfire-switch>>
|
||||
5. <<geode-cluster-configuration-bootstrapping>>
|
||||
6. <<geode-testing>>
|
||||
6. <<geode-testing-support>>
|
||||
7. <<geode-examples>>
|
||||
8. <<references>>
|
||||
|
||||
@@ -555,7 +555,7 @@ Congratulations! You just started a small {geode-name} cluster, with 3 members,
|
||||
It is pretty simple to build and run a Spring Boot, {geode-name}, `ClientCache` application that connects to
|
||||
this cluster. Simply include and use Spring Boot for {geode-name}, ;-).
|
||||
|
||||
[[geode-testing]]
|
||||
[[geode-testing-support]]
|
||||
=== Testing
|
||||
|
||||
https://github.com/spring-projects/spring-test-data-geode[Spring Test for {geode-name}] is a new, soon to be released
|
||||
|
||||
251
spring-geode-docs/src/docs/asciidoc/_includes/testing.adoc
Normal file
251
spring-geode-docs/src/docs/asciidoc/_includes/testing.adoc
Normal file
@@ -0,0 +1,251 @@
|
||||
[[geode-testing]]
|
||||
== Testing
|
||||
:gemfire-name: {pivotal-gemfire-name}
|
||||
:geode-name: {apache-geode-name}
|
||||
:stdg-website: https://github.com/spring-projects/spring-test-data-geode
|
||||
|
||||
Spring Boot for {geode-name} (SBDG), with help from {stdg-website}[Spring Test for {geode-name} (STDG)], offers
|
||||
first-class support for both _Unit_ & _Integration Testing_ of {geode-name} in your Spring Boot applications.
|
||||
|
||||
TIP: See the Spring Test for Apache Geode (STDG) {stdg-website}/#stdg-in-a-nutshell[documentation] for more details.
|
||||
|
||||
[[geode-testing-unit]]
|
||||
=== Unit Testing
|
||||
|
||||
_Unit Testing_ with {geode-name} using _mock objects_ in a Spring Boot Test is as simple as declaring the STDG
|
||||
`@EnableGemFireMockObjects` annotation in your test configuration:
|
||||
|
||||
.Unit Test with {geode-name} using Spring Boot
|
||||
[source,java]
|
||||
----
|
||||
@SpringBootTest
|
||||
@RunWith(SpringRunner.class)
|
||||
public class SpringBootApacheGeodeUnitTest extends IntegrationTestsSupport {
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Test
|
||||
public void saveAndFindUserIsSuccessful() {
|
||||
|
||||
User jonDoe = User.as("jonDoe");
|
||||
|
||||
assertThat(this.userRepository.save(jonDoe)).isNotNull();
|
||||
|
||||
User jonDoeFoundById = this.userRepository.findById(jonDoe.getName()).orElse(null);
|
||||
|
||||
assertThat(jonDoeFoundById).isEqualTo(jonDoe);
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableGemFireMockObjects
|
||||
@EnableEntityDefinedRegions(basePackageClasses = User.class)
|
||||
static class TestConfiguration { }
|
||||
|
||||
}
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
@RequiredArgsConstructor(staticName = "as")
|
||||
@Region("Users")
|
||||
class User {
|
||||
|
||||
@Id
|
||||
@lombok.NonNull
|
||||
private String name;
|
||||
|
||||
}
|
||||
|
||||
interface UserRepository extends CrudRepository<User, String> { }
|
||||
----
|
||||
|
||||
While this test class is not a "pure" _Unit Test_, particularly since it bootstraps an actual Spring `ApplicationContext`
|
||||
using Spring Boot, it does, however, "_mock_" all {geode-name} objects, such as the "_Users_" `Region` declared by the
|
||||
`User` application entity class, which was annotated with SDG's `@Region` mapping annotation.
|
||||
|
||||
This test class conveniently uses Spring Boot's _auto-configuration_ to auto-configure an {geode-name} `ClientCache`
|
||||
instance. In addition, SDG's `@EnableEntityDefinedRegions` annotation was used to conveniently create the {geode-name}
|
||||
"_Users_" `Region` to store instances of `User`.
|
||||
|
||||
Finally, Spring Data's _Repository_ abstraction was used to conveniently perform basic CRUD (e.g. `save`) and simple
|
||||
(OQL) query (e.g. `findById`) data access operations on the "_Users_" `Region`.
|
||||
|
||||
Even though the {geode-name} objects (e.g. "_Users_" `Region`) are "_mock objects_", you can still perform many of
|
||||
the data access operations required by your Spring Boot application's business logic in a {geode-name} API agnostic way,
|
||||
that is, using Spring's powerful programming model and constructs!
|
||||
|
||||
TIP: By extending STDG's `org.springframework.data.gemfire.tests.integration.IntegrationTestSupport` class,
|
||||
you ensure that all {geode-name} mock objects and resources are properly released after the test class runs,
|
||||
thereby preventing any interference with downstream tests.
|
||||
|
||||
While STDG tries to {stdg-website}/#mock-regions-with-data[mock the functionality and behavior] for many `Region`
|
||||
operations, it is simply not pragmatic to mock them all. For example, it would not be practical to mock `Region`
|
||||
query operations involving complex OQL statements having sophisticated predicates.
|
||||
|
||||
If such functional testing is required, then the test might be better suited as an _Integration Test_. Alternatively,
|
||||
you can follow the advice in this {stdg-website}/#mocking-unsupported-region-operations[section].
|
||||
|
||||
In general, STDG provides the following capabilities when mocking {geode-name} objects out-of-the-box:
|
||||
|
||||
* {stdg-website}#mock-object-scope--lifecycle-management[Mock Object Scope & Lifecycle Management]
|
||||
* {stdg-website}#mock-regions-with-data[Support for Mock Regions with Data]
|
||||
* {stdg-website}#mock-region-callbacks[Support for Mocking Region Callbacks]
|
||||
* {stdg-website}#mocking-unsupported-region-operations[Support for Mocking Unsupported Region Operations]
|
||||
|
||||
TIP: See documentation on {stdg-website}/#unit-testing-with-stdg[Unit Testing with STDG] for more details.
|
||||
|
||||
[[geode-testing-integration]]
|
||||
=== Integration Testing
|
||||
|
||||
_Integration Testing_ with {geode-name} in a Spring Boot Test is as simple as **not** declaring STDG's
|
||||
`@EnableGemFireMockObjects` annotation in your test configuration. Of course, you may then want to additionally use
|
||||
SBDG's `@EnableClusterAware` annotation to conditionally detect the presence of a {geode-name} cluster:
|
||||
|
||||
.Using `@EnableClusterAware` in test configuration
|
||||
[source,java]
|
||||
----
|
||||
@SpringBootApplication
|
||||
@EnableClusterAware
|
||||
@EnableEntityDefinedRegions(basePackageClasses = User.class)
|
||||
static class TestConfiguration { }
|
||||
----
|
||||
|
||||
The SBDG `@EnableClusterAware` annotation will conveniently toggle your auto-configured `ClientCache` instance
|
||||
between local-only mode and client/server. Additionally, it will even push configuration metadata
|
||||
(e.g. `Region` definitions) up to the server(s) in the cluster required by the application to persist data.
|
||||
|
||||
In most cases, in addition to testing with "_live_" {geode-name} objects (e.g. _Regions_), we also want to test
|
||||
in a client/server capacity. This unlocks the full capabilities of the {geode-name} data management system
|
||||
in a Spring context, and gets you as close as possible to production from the comfort of your IDE.
|
||||
|
||||
Building on our example from the section on <<geode-testing-unit>>, you can modify the test to use "_live_"
|
||||
{geode-name} objects in a client/server topology as follows:
|
||||
|
||||
.Integration Test with {geode-name} using Spring Boot
|
||||
[source,java]
|
||||
----
|
||||
@ActiveProfiles("client")
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(properties = "spring.data.gemfire.management.use-http=false")
|
||||
public class SpringBootApacheGeodeIntegrationTest extends ForkingClientServerIntegrationTestsSupport {
|
||||
|
||||
@BeforeClass
|
||||
public static void startGeodeServer() throws IOException {
|
||||
startGemFireServer(TestGeodeServerConfiguration.class);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Test
|
||||
public void saveAndFindUserIsSuccessful() {
|
||||
|
||||
User jonDoe = User.as("jonDoe");
|
||||
|
||||
assertThat(this.userRepository.save(jonDoe)).isNotNull();
|
||||
|
||||
User jonDoeFoundById = this.userRepository.findById(jonDoe.getName()).orElse(null);
|
||||
|
||||
assertThat(jonDoeFoundById).isEqualTo(jonDoe);
|
||||
assertThat(jonDoeFoundById).isNotSameAs(jonDoe);
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableClusterAware
|
||||
@EnableEntityDefinedRegions(basePackageClasses = User.class)
|
||||
@Profile("client")
|
||||
static class TestGeodeClientConfiguration { }
|
||||
|
||||
@CacheServerApplication
|
||||
@Profile("server")
|
||||
static class TestGeodeServerConfiguration {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
new SpringApplicationBuilder(TestGeodeServerConfiguration.class)
|
||||
.web(WebApplicationType.NONE)
|
||||
.profiles("server")
|
||||
.build()
|
||||
.run(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
@RequiredArgsConstructor(staticName = "as")
|
||||
@Region("Users")
|
||||
class User {
|
||||
|
||||
@Id
|
||||
@lombok.NonNull
|
||||
private String name;
|
||||
|
||||
}
|
||||
|
||||
interface UserRepository extends CrudRepository<User, String> { }
|
||||
----
|
||||
|
||||
The application client/server-based _Integration Test_ class extend STDG's
|
||||
`org.springframework.data.gemfire.tests.integration.ForkingClientServerIntegrationTestsSupport` class.
|
||||
This ensures that all {geode-name} objects and resources are properly cleaned up after the test class runs. In addition,
|
||||
it coordinates the client & server components of the test (e.g. connecting the client to the server using a random port).
|
||||
|
||||
The server is started in a `@BeforeClass` setup method:
|
||||
|
||||
.Start the {geode-name} server
|
||||
[source,java]
|
||||
----
|
||||
class SpringBootApacheGeodeIntegrationTest extends ForkingClientServerIntegrationTestsSupport {
|
||||
|
||||
@BeforeClass
|
||||
public static void startGeodeServer() throws IOException {
|
||||
startGemFireServer(TestGeodeServerConfiguration.class);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
STDG allows you to configure the server with Spring config, specified in the `TestGeodeServerConfiguration` class.
|
||||
The Java class needs to provide a `main` method. It uses the `SpringApplicationBuilder` to bootstrap the {geode-name}
|
||||
`CacheServer` application.
|
||||
|
||||
.{geode-name} server configuration
|
||||
[source,java]
|
||||
----
|
||||
@CacheServerApplication
|
||||
@Profile("server")
|
||||
static class TestGeodeServerConfiguration {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
new SpringApplicationBuilder(TestGeodeServerConfiguration.class)
|
||||
.web(WebApplicationType.NONE)
|
||||
.profiles("server")
|
||||
.build()
|
||||
.run(args);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
In this case, we provide very minimal configuration since the configuration is determined and pushed up to the server
|
||||
by the client. For example, we do not need to explicitly create the "_Users_" `Region` on the server-side since it is
|
||||
implicitly handled for you by the SBDG/STDG frameworks from the client.
|
||||
|
||||
We take advantage of Spring _Profiles_ in the test setup to distinguish between the client & server configuration. Keep
|
||||
in mind that the test is the "client" in this arrangement.
|
||||
|
||||
The STDG framework is doing as the supporting class states, "forking" the Spring Boot-based, {geode-name} `CacheServer`
|
||||
application in a separate JVM process. Subsequently, the STDG framework will stop the server upon completion of
|
||||
the tests in the test class.
|
||||
|
||||
Of course, you are free to start your server(s) or cluster however you choose. STDG simply and conveniently provides
|
||||
this capability for you since it is a common concern.
|
||||
|
||||
This test class is very simple and much more complex test scenarios can be easily handled by STDG.
|
||||
|
||||
TIP: Review SBDG's test suite to witness the full power and functionality of the STDG framework for yourself.
|
||||
|
||||
NOTE: See documentation on {stdg-website}/#integration-testing-with-stdg[Integration Testing with STDG]
|
||||
for more details.
|
||||
@@ -429,7 +429,6 @@ repositories {
|
||||
----
|
||||
endif::[]
|
||||
|
||||
|
||||
include::{include-dir}/clientcache-applications.adoc[]
|
||||
include::{include-dir}/configuration-auto.adoc[]
|
||||
include::{include-dir}/configuration-declarative.adoc[]
|
||||
@@ -444,6 +443,7 @@ include::{include-dir}/data.adoc[]
|
||||
include::{include-dir}/data-serialization.adoc[]
|
||||
include::{include-dir}/logging.adoc[]
|
||||
include::{include-dir}/security.adoc[]
|
||||
include::{include-dir}/testing.adoc[]
|
||||
include::{include-dir}/geode-api-ext.adoc[]
|
||||
include::{include-dir}/actuator.adoc[]
|
||||
include::{include-dir}/session.adoc[]
|
||||
|
||||
Reference in New Issue
Block a user