{"id":14972953,"url":"https://github.com/spring-projects/spring-test-data-geode","last_synced_at":"2025-07-19T01:38:00.616Z","repository":{"id":31716651,"uuid":"127950234","full_name":"spring-projects/spring-test-data-geode","owner":"spring-projects","description":"A Spring-based Test Framework supporting Unit and Integration testing for Spring Boot applications using Spring Data with either Apache Geode or VMware Tanzu GemFire","archived":false,"fork":false,"pushed_at":"2023-11-29T21:48:07.000Z","size":1380,"stargazers_count":22,"open_issues_count":6,"forks_count":26,"subscribers_count":8,"default_branch":"main","last_synced_at":"2025-04-05T09:40:56.522Z","etag":null,"topics":["apache-geode","framework","java","spring-test"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/spring-projects.png","metadata":{"files":{"readme":"README.adoc","changelog":null,"contributing":"CONTRIBUTING.adoc","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2018-04-03T18:19:26.000Z","updated_at":"2024-10-01T23:51:16.000Z","dependencies_parsed_at":"2023-02-10T12:16:24.799Z","dependency_job_id":"921bdc06-00e5-43b3-829c-b387295692f5","html_url":"https://github.com/spring-projects/spring-test-data-geode","commit_stats":null,"previous_names":[],"tags_count":62,"template":false,"template_full_name":null,"purl":"pkg:github/spring-projects/spring-test-data-geode","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spring-projects%2Fspring-test-data-geode","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spring-projects%2Fspring-test-data-geode/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spring-projects%2Fspring-test-data-geode/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spring-projects%2Fspring-test-data-geode/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/spring-projects","download_url":"https://codeload.github.com/spring-projects/spring-test-data-geode/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spring-projects%2Fspring-test-data-geode/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265871426,"owners_count":23842028,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apache-geode","framework","java","spring-test"],"created_at":"2024-09-24T13:47:48.725Z","updated_at":"2025-07-19T01:38:00.590Z","avatar_url":"https://github.com/spring-projects.png","language":"Java","readme":"image:https://api.travis-ci.org/spring-projects/spring-test-data-geode.svg?branch=master[\"Build Status\", link=\"https://travis-ci.org/spring-projects/spring-test-data-geode\"]\n\n[[about]]\n== Spring Test Framework for Apache Geode\n\nThe STDG project is a _Spring Data_ module, building on the core _Spring Framework's_ `TestContext`, used to write\n_Unit_ and _Integration Tests_ when building _Spring_ for https://geode.apache.org/[Apache Geode]\n\u0026 https://tanzu.vmware.com/gemfire[VMware GemFire] applications.\n\n[[notice]]\n== NOTICE\n\n[[notice-2023-january]]\n*2023-January-17*:\n\nAt the end of 2022, VMware https://tanzu.vmware.com/content/blog/spring-for-vmware-gemfire-is-now-available[announced]\nthe general availability of the Spring for VMware GemFire portfolio of projects.\n\nWhile these Spring based projects for VMware GemFire are open source and a succession to the Spring for Apache Geode\nprojects, they are not a replacement. VMware GemFire forked from the Apache Geode project and is not open source.\nAdditionally, newer Apache Geode and VMware GemFire clients are not backwards compatible with older Apache Geode\nand VMware GemFire servers. Currently, there is no replacement for STDG and directions to transition.\n\nAlternatively, the Spring portfolio provides first-class https://docs.spring.io/spring-boot/docs/current/reference/html/io.html#io.caching.provider[integration]\nwith other comparable caching providers. Also, see https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#cache-store-configuration[here]\nand https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#cache-plug[here], along with\nhttps://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testing[documentation on testing].\n\nFinally, keep in mind, the Spring for Apache Geode projects will still be maintained until OSS and commercial support\nends. Maintenance will only include CVE and critical fixes. No new features or major enhancements will be made.\nThe Spring Test for Apache Geode support timelines can be viewed https://spring.io/projects/spring-data-geode#support[here].\n\n[[notice-2022-october]]\n*2022-October-24*:\n\nSee the October 24th https://github.com/spring-projects/spring-data-geode#notice[NOTICE]\non the _Spring Data for Apache Geode_ GitHub project page for complete details.\n\n[[overview]]\n== Overview\n\nThis project was born from https://spring.io/projects/spring-data-gemfire[_Spring Data for VMware GemFire's_]\n(https://github.com/spring-projects/spring-data-gemfire[@GitHub])\nhttps://github.com/spring-projects/spring-data-gemfire/tree/2.1.19.RELEASE/src/test/java/org/springframework/data/gemfire/test[test framework].\nThis _test framework_ is used in SDG's test suite to test the proper function and behavior of Apache Geode\n\u0026 VMware GemFire in a _Spring_ context.\n\nFor several years now, users have asked for a better way to test their Apache Geode \u0026 VMware GemFire based,\n_Spring_ applications reliably and easily, particularly when writing _Unit_ and _Integrations_ tests.\n\nAdditionally, STDG was created to consolidate the testing efforts, lessons learned, and knowledge and experience of\neffectively testing all Spring for Apache Geode/VMware GemFire projects: _Spring Boot for Apache Geode \u0026 VMware GemFire_\n(SBDG) and _Spring Session for Apache Geode \u0026 VMware GemFire_ (SSDG) in addition to\n_Spring Data for Apache Geode \u0026 VMware GemFire_ (SDG).\n\nEventually, STDG will replace the SDG test classes so that tests and testing efforts are consistent across all Spring\nprojects for Apache Geode/VMware GemFire: SDG, SSDG and SBDG.\n\nThis (relatively) **new** project is still under development and will have documentation, examples and an extensive test\nsuite once completed.\n\nIn the meantime, you can review the\nhttps://github.com/spring-projects/spring-boot-data-geode/tree/master/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure[test suite for SBDG]\nand the https://github.com/spring-projects/spring-session-data-geode/tree/master/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire[test suite for SSDG]\nto get a sense of how this project is used and works.\n\n[[code-of-conduct]]\n== Code of Conduct\n\nPlease see our https://github.com/spring-projects/.github/blob/master/CODE_OF_CONDUCT.md[code of conduct]\n\n[[report-security-vulnerability]]\n== Reporting Security Vulnerabilities\n\nPlease see our https://github.com/spring-projects/spring-test-data-geode/security/policy[Security policy].\n\n[[license]]\n== License\n\n_Spring Test for Apache Geode_ and _Spring Test for VMware GemFire_ is Open Source Software\nreleased under the https://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0 license].\n\n\n[[nutshell]]\n== STDG in a Nutshell\n\nUntil proper documentation has been provided, this very short and simple tutorial will hopefully give you a better idea\nof how this project is used.\n\n\n[[unit-tests]]\n=== Unit Testing with STDG\n\nWe all write tests, right?  TDD style?  ;-)\n\nAs we begin to write tests, we typically start with _Unit Tests_ since they are designed to test the subject\nin isolation without actual collaborators and dependencies in order to gather feedback quickly,\ni.e. \"_Is my logic correct?_\"\n\nIt common when writing _Unit Tests_ to *mock* the collaborators/dependencies since the test should assume that the\ndependencies \"_work as designed_\".  During _Unit Testing_, it does not matter whether or not the dependencies actually\nwork as expected (that is the purpose of _Integration Tests_ or the _Unit Tests_ for the dependencies themselves), just\nthat they have a contract and our application components, the \"_Subject Under Test_\" (SUT), honors that contract and\nuses the external dependencies correctly. Essentially we are asserting that the interactions between our application\ncomponents and external collaborators/dependencies are correct and the results lead to the desired outcome.\n\nWell, it is, or should be, no different when you are using Apache Geode or VMware GemFire.\n\nFor instance, you might want to mock that your _Data Access Object_ (DAO) performs the proper interactions on\na GemFire/Geode Region, performing the right CRUD operations, making sure the right (OQL) Queries are executed\nfor the Use Case or business function and workflow being performed by the application.\n\nIn this case, we don't care whether the Region is real or not, or that an OQL Query is actually well formed and would\nexecute properly, performantly, returning the correct results.  We would \"mock\" the Regions' behavior in this case\nto make sure that our DAO interactions with the Region are correct, that it handles the translation of Exceptions\nor other Error conditions, that it transforms values to/from the backend data store (i.e. Region), and so on. That is\nhow you properly test the subject.\n\nTo support _Unit Testing_ with Apache Geode or VMware GemFire in a Spring context, STDG provides the\n`@EnableGemFireMockObjects` annotation.  If you want to use GemFire/Geode Mock Objects, e.g. a \"mock\" Region rather\nthan a \"live\" Region, than you simply only need to annotate your test configuration with `@EnableGemFireMockObjects`.\n\nFor example:\n\n.Unit Test with GemFire/Geode Mock Objects\n[source,java]\n----\n@RunWith(SpringRunner.class)\n@ContextConfiguration\nclass ExampleUnitTestClass {\n\n  // test case methods here\n\n  @EnableGemFireMockObjects\n  @ClientCacheApplication\n  @EnableEntityDefinedRegions(basePackageClasses=ExampleEntity.class,\n      clientRegionShortcut = ClientRegionShortcut.LOCAL)\n  static class TestConfiguration { }\n\n}\n----\n\n\nIn the example above, the `@EnableGemFireMockObjects` annotation creates \"mocks\" for the `ClientCache`, all the `Regions`\nidentified and created by the `@EnableEntityDefinedRegions(..)` annotation, along with all the other GemFire/Geode\nobject types. There are no \"live\" GemFire/Geode objects when \"mocking\" is enabled.\n\nHere is 1\nhttps://github.com/spring-projects/spring-test-data-geode/blob/master/spring-data-geode-test/src/test/java/org/springframework/data/gemfire/MockClientCacheApplicationIntegrationTests.java[example]\nof a concrete _Unit Test_ in action, using STDG's `@EnableGemFireMockObjects` annotation.\n\nIt really is that simple!\n\nTIP: Mocking GemFire/Geode objects outside a Spring context is possible, but beyond the scope of this simple tutorial\nfor the time being.\n\n[[unit-tests-mock-object-cleanup]]\n==== Mock Object Scope \u0026 Lifecycle Management\n\nCurrently, GemFire/Geode mock objects are cleaned up after an individual test class runs. Therefore, the mocked\nGemFire/Geode objects persist for the entire lifecycle of a single test class, or test suite, and can be reused\nacross all the test cases of the test class.\n\nThe Spring `TestContext` framework emits certain \"test\" events during the test lifecycle as documented in\nthe `EventPublishingTestExecutionListener` class https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/context/event/EventPublishingTestExecutionListener.html[_Javadoc_].\nThe test events are actually contained in the https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/context/event/package-summary.html[`org.springframework.test.context.event`] package.\n\nCurrently, STDG defaults the cleanup of all mocked GemFire/Geode objects to the\nhttps://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/context/event/AfterTestClassEvent.html[`AfterTestClassEvent`] type.\n\nBy way of example, this would be equivalent to:\n\n.Default STDG GemFire/Geode mock object cleanup\n[source,java]\n----\n@RunWith(SpringRunner.class)\n@ContextConfiguration\nclass ExampleTest {\n\n\t@ClientCacheApplication\n    @EnableGemFireMockObject(destroyOnEvents = AfterTestClassEvent.class)\n\tstatic class TestConfiguration { }\n\n}\n----\n\nYou might want to cleanup all GemFire/Geode mock objects after each test case method in your test class using\nthe https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/context/event/AfterTestMethodEvent.html[`AfterTestMethodEvent`] class.\n\nIn this case, you can do:\n\n.GemFire/Geode mock object cleanup after each test case\n[source,java]\n----\n@RunWith(SpringRunner.class)\n@ContextConfiguration\nclass ExampleTest {\n\n\t@ClientCacheApplication\n    @EnableGemFireMockObject(destroyOnEvents = AfterTestMethodEvent.class)\n\tstatic class TestConfiguration { }\n\n}\n----\n\nThe `destroyOnEvents` attribute of the `@EnableGemFireMockObjects` annotation accepts more than one test event type,\nthereby allowing to perform GemFire/Geode mock object cleanup at multiple points in the test lifecycle.\n\nFor example, maybe you need to cleanup all mocked GemFire/Geode objects before each test case executes and after each\ntest class completes:\n\n.GemFire/Geode mock object cleanup before each test case executes and after each test class completes\n[source,java]\n----\n@RunWith(SpringRunner.class)\n@ContextConfiguration\nclass ExampleTest {\n\n\t@ClientCacheApplication\n    @EnableGemFireMockObject(destroyOnEvents = { BeforeTestExecutionEvent.class, AfterTestClassEvent.class })\n\tstatic class TestConfiguration { }\n\n}\n----\n\nYou now have the granularity required to control the scope and lifecycle of the GemFire/Geode mocked objects in STDG.\n\n\n[[unit-tests-mock-region-data]]\n==== Mock Regions with Data\n\nWhile implementing a fully capable GemFire/Geode Region would defeat the purpose of Mocking and Unit Testing in general,\nit is desirable to sometimes perform basic Region data access operations, such as `get` and `put`, with small quantities\nof data and emulate, or simulate the same effects.\n\nAs such, with STDG, it is currently possible to perform the following Region data access operations:\n\n* `clear()`\n* `containsKey(key)`\n* `containsValue(value)`\n* `containsValueForKey(value)`\n* `forEach(:BiConsumer\u003cK, V\u003e)`\n* `get(key)`\n* `getAll()`\n* `getEntry(key)`\n* `getOrDefault(key, defaultValue)`\n* `invalidate(key)`\n* `isEmpty()`\n* `keySet()`\n* `localClear()`\n* `localValidate()`\n* `put(key, value)`\n* `putAll(:Map\u003cK, V\u003e)`\n* `remove(key)`\n* `removeAll(:Collection\u003cK\u003e)`\n* `size()`\n* `values()`\n\nNOTE: Some mock Map/Region data access operations are still being considered, such as: `putIfAbsent(key, value)`,\n`remove(key, value)`, `replace(key, value)`, `replace(key, oldValue, newValue)` and `replaceAll(:BiFunction\u003cK, V\u003e)`.\nOther mock Region data access operations will not be implemented at all (e.g. `keySetOnServer()` or `sizeOnServer()`,\netc) since they necessarily involve a more complex topology. Regardless, you can still mock any Map/Region operation\nyou like by following these \u003c\u003cunit-tests-mock-unsupported-region-ops,instructions\u003e\u003e.\n\nWARNING: Some mock Map/Region data access operations are implemented in terms of other Map/Region operations\n(e.g. `putAll(:Map\u003cK, V))` is implemented in terms of `put(key, value)`) and are therefore compound actions\nthat are not atomic.  In other words, we did not make the atomic.\n\nThe \"mock\" Region will behave and function similarly to an actual GemFire/Geode Region involving these\ndata access operations.\n\nBy way of example, this means you can do things like the following in a Unit Test with a \"mock\" Region:\n\n.Basic data access operations on a mocked Region\n[source,java]\n----\n@RunWith(SpringRunner.class)\n@ContextConfiguration\nclass MyGeodeMockRegionUnitTests {\n\n  @Resource(name = \"Example\")\n  private Region\u003c?, ?\u003e  mockRegion;\n\n  @Test\n  public void simpleGetAndPutRegionOpsWork() {\n\n      mockRegion.put(1, \"test\");\n\n      assertThat(mockRegion).containsKey(1);\n      assertThat(mockRegion.get(1)).isEqualTo(\"test\");\n  }\n\n  @ClientCacheApplication\n  @EnableGemFireMockObjects\n  static class TestConfiguration {\n\n    @Bean(\"Example\")\n    ClienRegionFactoryBean mockRegion(GemFireCache gemfireCache) {\n\n        ClientRegionFactoryBean mockRegion = new ClientRegionFactoryBean();\n\n        mockRegion.setCache(gemfireCache);\n\n        return mockRegion;\n    }\n  }\n}\n----\n\nOf course, you can also perform similar Region data access operations using the _Spring Data Repository_ abstraction\ninstead. The benefit of _Spring Data's_ _Repository_ abstraction is that it shields your application from Apache Geode\nand hides the fact that you are interfacing with an Region under-the-hood by using the proper _Data Access Object_ (DAO)\npattern.\n\nFor example, you can \"mock\" a Region and `put`/`get` data using a _Spring Data Repository_ for the Region\nas demonstrated in the following code.\n\nGiven a `Customer` application domain object annotated with the `@Region` mapping annotation:\n\n.Customer\n[source,java]\n----\n@Region(\"Customers\")\nclass Customer {\n\n    @Id\n    private Long id;\n\n    // ...\n\n}\n----\n\nAlong with a SD _Repository_ for `Customers`:\n\n.CustomerRepository\n[source,java]\n----\ninterface CustomerRepository extends CrudRepository\u003cCustomer, Long\u003e {\n\t//...\n}\n----\n\nThen you can write a test class like the following, still using a \"mock\" Region to `put` and `get` actual data:\n\n.Spring Data _Repository_ on a mocked Region\n[source,java]\n----\n@RunWith(SpringRunner.class)\n@ContextConfiguration\nclass MySpringDataRepositoryUnitTests {\n\n    @Autowired\n    private CustomerRepository customerRepository;\n\n    @Test\n    public void simpleRepositoryCrudOpsWork() {\n\n        Customer jonDoe = new Customer(1L, \"Jon Doe\");\n\n        customerRepository.save(jonDoe);\n\n        assertThat(customerRepository.existsById(jonDoe.getId())).isTrue();\n        assertThat(customerRepository.findById(jonDoe.getId()).orElse(null)).isEqualTo(jonDoe);\n    }\n\n    @ClientCacheApplication\n    @EnableEntityDefinedRegions(basePackageClasses = Customer.class)\n    @EnableGemfireRepositories(basePackageClasses = CustomerRepository.class)\n    static class TestConfiguration {  }\n\n}\n----\n\nEven though you are using _Spring Data Repositories_ and the `@EnableEntityDefinedRegions` annotation (perhaps;\nyes these components still work with Mocks and mock data), you can still autowire (inject) the Region and access\nit directly in the same test class:\n\n.Accessing the mock Region directly in the SD _Repository_ test\n[source,java]\n----\n@RunWith(SpringRunner.class)\n@ContextConfiguration\nclass MySpringDataRepositoryWithMockRegionUnitTests {\n\n    @Autowired\n    private CustomerRepository customerRepository;\n\n    @Resource(name = \"Customers\")\n    private Region\u003cLong, Customer\u003e customers;\n\n    @Test\n    public void simpleRepositoryCrudOpsWork() {\n    \t//...\n    }\n\n    @Test\n    public void customerRegionOpsWorkToo() {\n\n        Customer janeDoe = new Customer(2L, \"Jane Doe\");\n\n        customers.put(janeDoe.getId(), janeDoe);\n\n        assertThat(customers).containsKey(janeDoe.getId());\n        assertThat(customers.get(janeDoe.getId())).isEqualTo(janeDoe);\n        assertThat(customerRepository.findById(janeDoe.getId()).orElse(null)).isEqualTo(janeDoe);\n    }\n}\n----\n\nWhile you are allowed to inject a Region directly into your test class, it is better to use SDG's `GemfireTemplate`,\nwhich wraps and decorates a Region's data access operations.  `GemfireTemplate` provides a lower-level API, closer\nto the Region API, than _Spring Data Repositories_ allowing you to perform and exercise more control over advanced\nfunctions, while still shielding you from the Region API.\n\nThe test class above could be rewritten as:\n\n.Accessing the mock Region using the SDG `GemfireTemplate` in the SD _Repository_ test\n[source,java]\n----\n@RunWith(SpringRunner.class)\n@ContextConfiguration\nclass MySpringDataRepositoryWithGemfireTemplateUnitTests {\n\n    @Autowired\n    private CustomerRepository customerRepository;\n\n    @Autowired\n    @Qualifier(\"customersTemplate\")\n    private GemfireTemplate customersTemplate;\n\n    @Test\n    public void simpleRepositoryCrudOpsWork() {\n    \t//...\n    }\n\n    @Test\n    public void customerTemplateOpsWorkToo() {\n\n        Customer janeDoe = new Customer(2L, \"Jane Doe\");\n\n        customersTemplate.put(janeDoe.getId(), janeDoe);\n\n        assertThat(customersTemplate).containsKey(janeDoe.getId());\n        assertThat(customersTemplate.get(janeDoe.getId())).isEqualTo(janeDoe);\n        assertThat(customerRepository.findById(janeDoe.getId()).orElse(null)).isEqualTo(janeDoe);\n    }\n}\n----\n\nFor clarification, obviously many of the Region functions and behaviors are not implemented, like persistence\nand overflow to disk, distribution, replication, eviction, expiration, querying, etc.  If you find you need to test\nyour application with these behaviors and functions, then your test would clearly be better suited as an actual\nIntegration Test.\n\n[[unit-tests-mock-region-callbacks]]\n==== Mock Region Callbacks\n\nA relatively *new* feature in STDG is the ability to register and invoke cache (Region) callbacks, such as\n`CacheListeners`, or a `CacheLoader` or a `CacheWriter`.\n\nCache callbacks like `CacheListeners` or `CacheLoader/Writers` are user-defined, application objects that can be\nregistered with a Region to listen for events, load data on cache misses, or write the Region's data to a backend,\nexternal data source.\n\nIt is sometimes useful when testing to partially mock some dependencies (a.k.a. collaborators; e.g. Regions)\nwhile using live objects for others (e.g. cache callbacks like a `CacheListener`).\n\nThe reason behind this testing strategy is that some objects are mostly infrastructure related (e.g. a Region),\nand not the primary focus of the test, while other objects are still very much tied to the application's function\nand behavior (e.g. a `CacheListener` or a `CacheLoader`), i.e. they are part of the application's workflow.\n\nAs such, STDG not only allows you to register `CacheListeners` and `CacheLoaders/Writers` (you could do so before\nas well), but will now additionally invoke the Listeners, Loader and Writer at the appropriate point in the Region\noperation's process flow.\n\nFor example, a registered `CacheWriter` is invoked before the object (value) is put into the Region using the\n`Region.put(key, value)` operation.  This is exactly what GemFire/Geode does in order to ensure consistency with\nthe backend, external data source.  If the `CacheWriter` throws an exception during 1 of it's event handler callbacks\n(e.g. `beforeCreate(:EntryEvent\u003cK, V\u003e)` then it will prevent the object from being inserted into the Region.\nThe same behavior is true for a STDG mock Region.\n\nBy way of example, let's demonstrate with a `CacheLoader`:\n\n.Application `CacheLoader` on mock Region\n[source,java]\n----\n@RunWith(SpringRunner.class)\n@ContextConfiguration\nclass MyMockRegionWithCacheLoaderUnitTests {\n\n  @Resource(name = \"Example\")\n  private Region example;\n\n  @Test\n  public void cacheLoaderWorks() {\n\n    assertThat(example.get(\"one\")).isEqualTo(1);\n    assertThat(example.get(\"two\")).isEqualTo(2);\n    // ...\n\n  }\n\n  @ClientCacheApplication\n  @EnableGemFireMockObjects\n  static class TestConfiguration {\n\n    @Bean\n    ClienRegionFactoryBean exampleRegion(GemFireCache gemfireCache) {\n\n      ClientRegionFactoryBean exampleRegion = new ClientRegionFactoryBean();\n\n      exampleRegion.setCache(gemfireCache);\n      exampleRegion.setCacheLoader(counterCacheLoader());\n\n      return exampleRegion;\n    }\n  }\n\n  @Bean\n  CacheLoader\u003cObject, Object\u003e counterCacheLoader() {\n\n    AtomicInteger counter = new AtomicInteger(0);\n\n    return new CacheLoader\u003c\u003e() {\n\n      @Override\n      public Object load(LoaderHelper\u003cObject, Object\u003e helper) {\n        return counter.incrementAndGet();\n      }\n    };\n  }\n}\n----\n\nAs seen in the test above, performing a `Region.get(key)` for keys \"one\" and \"two\" on an initially empty Region\nwill result in cache misses, which will then invoke the registered, application \"counter\" `CacheLoader` to supply\nthe value for the requested keys.\n\nYou can register a `CacheWriter` along with 1 or more `CacheListeners` and they will be invoked, too.\n\n[[unit-tests-mock-unsupported-region-ops]]\n==== Mocking Unsupported Region Operations\n\nAs stated in the \u003c\u003cunit-tests-mock-region-data\u003e\u003e section above, only the following Region data access operations are\nsupported by STDG out-of-the-box (OOTB):\n\n* `clear()`\n* `containsKey(key)`\n* `containsValue(value)`\n* `containsValueForKey(value)`\n* `forEach(:BiConsumer\u003cK, V\u003e)`\n* `get(key)`\n* `getAll()`\n* `getEntry(key)`\n* `getOrDefault(key, defaultValue)`\n* `invalidate(key)`\n* `isEmpty()`\n* `keySet()`\n* `localClear()`\n* `localValidate()`\n* `put(key, value)`\n* `putAll(:Map\u003cK, V\u003e)`\n* `remove(key)`\n* `removeAll(:Collection\u003cK\u003e)`\n* `size()`\n* `values()`\n\nHow then do you mock other Region operations (e.g. `putIfAbsent(key, value)`) provided by the Region API that is not\nsupported by STDG OOTB?\n\nFortunately, you can rely on the fact that the Region object returned when mocking with `@EnableGemFireMockObjects`\ninside your _Unit Tests_ is a \"_mock_\" object, specifically mocked by _Mockito_. Therefore, you are able to mock\nany other Region data access operations that might be required by your application given a reference to the \"mock\"\nRegion object.\n\nFor example, suppose you also want to mock the `putIfAbsent(key, value)` _Map_ operation on Region, then you can do:\n\n.Mocking Region.putIfAbsent(key, value)\n[source,java]\n----\n@RunWith(SpringRunner.class)\n@ContextConfiguration\nclass ExampleUnitTest {\n\n\t@Autowired\n    @Qualifer(\"exampleTemplate\")\n\tGemfireTemplate exampleTemplate;\n\n\t@Resource(name = \"Example\")\n\tRegion\u003c?, ?\u003e example;\n\n\t@Before\n\tpublic void setup() {\n\n\t\tdoAnswer(invocation -\u003e {\n\n\t\t\tObject key = invocation.getArgugment(0);\n\t\t\tObject value = invocation.getArgument(1);\n\t\t\tObject existingValue;\n\n\t\t\tsynchronized (this.example) {\n\n\t\t\t\texistingValue = this.example.get(key);\n\n\t\t\t\tif (existingValue == null) {\n\t\t\t\t\tthis.example.put(key, value);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn existingValue;\n\n\t\t}).when(this.example).putIfAbsent(any(), any());\n\t}\n\n\t@Test\n\tpublic void putIfAbsentWorks() {\n\n\t\tassertThat(this.exampleTemplate.putIfAbsent(1, \"test\")).isNull();\n\t\tassertThat(this.exampleTemplate.putIfAbsent(1, \"mock\")).isEqualTo(\"test\");\n\t\tassertThat(this.exampleTemplate.get(1)).isEqualTo(\"test\");\n\t}\n\n\t@ClientCacheApplication\n    @EnableGemFireMockObjects\n\tstatic class TestConfiguration {\n\n        @Bean(\"Example\")\n        ClienRegionFactoryBean mockRegion(GemFireCache gemfireCache) {\n\n            ClientRegionFactoryBean mockRegion = new ClientRegionFactoryBean();\n\n            mockRegion.setCache(gemfireCache);\n\n            return mockRegion;\n        }\n\n        @Bean\n        GemfireTemplate exampleTemplate(GemFireCache gemfireCache) {\n        \treturn new GemfireTemplate(gemifreCache.getRegion(\"/Example\"));\n        }\n\t}\n}\n----\n\nWhile the `putIfAbsent(key, value)` operation above was mocked (implemented) in terms of the existing, mocked `get(key)`\nand `put(key, value)` Region operations, you could very well have implemented/mocked `putIfAbsent(key, value)` however\nyou wanted.  The Region object is a \"_mock_\" object after all.\n\nNot only can you mock unsupported Region methods, you can also redefine the mocked behavior of a STDG supported\nand mocked Region method, like `get(key)` or `put(key, value)` as well.\n\nThis capability applies to any GemFire/Geode mocked object. The choice is up to you what a GemFire/Geode mock object\ndoes or does not do.\n\n[[integration-testing]]\n=== Integration Testing with STDG\n\nYou should write many more _Unit Tests_ than _Integration Tests_ to get reliable and fast feedback.  This is a\nno brainer and software development 101.\n\nHowever, _Unit Tests_ do not completely take the place of _Integration Tests_, either.  Both are necessary, as are\nperhaps other forms of testing (e.g. Functional Testing, Acceptance Testing, Smoke Testing, Performance Testing,\nConcurrency Testing, etc).\n\nFor instance, you should verify that the (OQL) Query you just constructed, maybe even generated, is well-formed\nand yields the desired results, is performant, and all that jazz.  You can only reliably do that by executing\nthe (OQL) Query against an actual GemFire/Geode Region with a properly constructed and deliberate data set.\n\nThis sort _Integration Test_ does not have a complex arrangement, and can be performed simply by removing\nor disabling the `@EnableGemFireMockObjects` annotation in our previous example above.\n\nHowever, other forms of _Integration Testing_ might require a more complex arrangement,\nsuch as client/server integration tests.\n\nFor instance, you may want to test that a client receives all the events from the server to which it has explicitly\nregistered interests.  For this type of test, you need to have a (1 or more) GemFire/Geode server(s) running,\nand perhaps even a few clients.\n\nIdeally, you want to fork a GemFire/Geode server JVM process in the _Integration Test_ class requiring\na server instance.\n\nOnce again, STDG comes to the rescue.\n\nFor example:\n\n.Client/Server Integration Test\n[source,java]\n----\n@RunWith(SpringRunner.class)\n@ContextConfiguration(classes = GeodeClientTestConfiguration.class)\nclass ExampleIntegrationTestClass extends ForkingClientServerIntegrationTestsSupport {\n\n  @BeforeClass\n  public static void startGemFireServer() {\n    startGemFireSever(GeodeServerTestConfiguration.class);\n  }\n\n  // test case method here\n\n  @CacheServerApplication\n  @EnableEntityDefinedRegions\n  static class GeodeServerTestConfiguration {\n\n    public static void main(String[] args) {\n\n        AnnotationConfigApplicationContext applicationContext =\n          new AnnotationConfigApplicationContext(GeodeServerTestConfiguration.class);\n\n        applicationContext.registerShutdownHook();\n    }\n  }\n\n  @ClientCacheApplication\n  @EnableEntityDefinedRegions\n  static class GeodeClientTestConfiguration { }\n\n}\n----\n\nFirst we extend the STDG provided `ForkingClientServerIntegrationTestsSupport` class.  Then, we define a JUnit\n`@BeforeClass` static setup method to fork our GemFire/Geode JVM process using the `GeodeServerTestConfiguration.class`\nspecifying exactly how the server should be configured and finally we create the matching `GeodeClientTestConfiguration`\nclass to configure and bootstrap our JUnit, Spring `TestContext` based test, which acts as the client.\n\nSTDG takes care of coordinating the client \u0026 server, using random connection ports, etc.  You simply just need to\nprovide the configuration of the client and server as required by your application and test case(s).\n\nHere is 1\nhttps://github.com/spring-projects/spring-boot-data-geode/blob/master/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/security/ssl/AutoConfiguredSslIntegrationTests.java[example]\nof a concrete client/server _Integration Test_ extending STDG's `ForkingClientServerIntegrationTestsSupprt` class.\n\nNotice, too, that I am using SDG's\nhttps://docs.spring.io/spring-data/geode/docs/current/reference/html/#bootstrap-annotation-config[Annotation-based configuration model]\n(e.g. `CacheServerApplication`, `@EnableEntityDefinedRegions`) to make the GemFire/Geode configuration even easier.\n\nIf you are using SBDG with this project, then some of the annotations are not even required (e.g. `ClientCacheApplication`).\n\nWhen SBDG \u0026 STDG are combined, the power you have is quite extensive.\n\nNOTE: Through the _Integration Test_ support provided by and in STDG is relatively simple, this is also not quite yet\nthe ideal way for writing client/sever _Integration Tests_.  Eventually, we want to include an annotation, something\nlike `@ClientServerIntegrationTest(serverConfigClass = GeodeServerTestConfiguration.class)`, the equivalent to\n`@EnableGemFireMockObjects` for _Unit Testing_, to make configuration and testing of client/server applications\nthat much easier.  See https://github.com/spring-projects/spring-test-data-geode/issues/9[Issue #9] for more details.\nThis feature would be loosely based on, and similar to,\n_Spring Boot_ https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html[Testing]\nwith _Test Slices_.\n\n[[integration-testing-resource-cleanup]]\n==== Cleaning up after GemFire/Geode during Integration Tests\n\nWhen writing _Integration Tests_ using \"live\" GemFire/Geode objects (e.g. Regions), those object can leave artifacts\nbehind after a test run completes.\n\nThis can potentially cause conflicts between _Integration Test Cases_ that use features like persistence having\nsimilarly named Regions particularly if you are not careful to differentiate the working directory between your tests.\nThis is also problematic, especially when switching between versions of GemFire/Geode, used by your application, during\ntesting. Perhaps you are in the middle of testing a (rolling) upgrade.\n\nAt any rate, STDG has you covered. If you would like to make sure that artifacts are properly cleaned up after a test\nrun, then you can annotate your test class with STDG's `@EnableGemFireResourceCollector` annotation, like so:\n\n.Using `@EnableGemFireResourceCollector\n[source,java]\n----\n@RunWith(SpringRunner.class)\n@ContextConfiguration\nclass ExampleIntegrationTest {\n\n\t@CacheServerApplication\n    @EnableLocator\n    @EnableManager\n    @EnableGemFireResourceCollector\n    static class TestGeodeConfiguration { }\n\n}\n----\n\nLike the `@EnableGemFireMockObjects` annotation, you can control which Spring `TestContext` test event will trigger\na GemFire/Geode resource (garbage) collection process using the `collectOnEvents` attribute.\n\nAlso, you can attempt to clean any GemFire/Geode `DiskStore` files (created by persistence, overflow or PDX) by setting\nthe `@EnableGemFireResourceCollector` annotation, `tryCleanDiskStoreFiles` attribute to `true`.\n\nThe following list of GemFire/Geode files with extensions or names are cleaned up by STDG's\n`@EnableGemFireResourceCollector` functionality:\n\n.GemFire/Geode File Extensions\n|===\n| File Extension | Description\n\n| `.dat`\n| Locator view file; e.g. `locator10334view.dat`\n\n| `.gfs`\n| Statistics archive file\n\n| `.crf`\n| Oplog file containing create, update, invalidate operations\n\n| `.drf`\n| Oplog file containing delete operations\n\n| `.if`\n| DiskStore metadata file\n\n| `.krf`\n| Oplog file for key and crf offset information\n\n| `.lk`\n| DiskStore access control file\n\n| `.log`\n| Log files created by GemFire/Geode process (Locators, Servers, Manager, etc)\n\n| `.pid`\n| File containing the OS process ID of the GemFire/Geode process (Locator, Server, etc)\n\n| `.properties`\n| GemFire/Geode properties configuration file (e.g. `gemfire.properties`)\n\n| `.xml`\n| GemFire/Geode XML configuration file (e.g. `cache.xml`)\n|===\n\n.GemFire/Geode Filenames\n|===\n| Filename | Description\n\n| `backup`        | filename prefix\n| `cache`         | filename prefix\n| `configdiskdir` | _Cluster Configuration Service_ directory name\n| `default`       | filename prefix\n| `drlk_if`       | filename prefix\n| `gfsecurity`    | filename prefix\n| `gemfire`       | directory/file name\n| `geode`         | directory/file name\n| `locator`       | directory/file prefix name\n| `overflow`      | filename prefix\n\n|===\n\nThe names of file extensions and files/directories are treated by STDG as case insensitive when matching.\n\nFor a complete https://cwiki.apache.org/confluence/display/GEODE/Geode+Artifacts[list of artifacts] created by\nGemFire/Geode processes, follow the link.\n\n\n[[testing-logging-behavior]]\n=== Asserting Logging Behavior\n\nIt is sometimes necessary or useful to write tests to assert an application's logging behavior.\n\nFor instance, if your application needs to log an event that occurred, output configuration meta-data on startup,\nalert a user to some system event such as low memory, out of disk space, or a temporary network outage, or whatever\nthe case might be, it is useful to assert that your application logs an appropriate message.\n\nBut, how do you assert that certain log events with an appropriate log message has been made by the application\nwhen the conditions constituting the log event have been arranged?\n\nNow, STDG provides the capability to 1) assert that your application, or an application component, made a log event\nat the appropriate moment and 2) that the log message communicates enough contextual-based information to be useful\nto the user of your application.\n\nTo do this, STDG provides the `org.springframework.data.geode.tests.logging.slf4j.logback.TestAppender` class.\n\nThis Log Appender can be used when your application logging framework is configured with _Logback_ as the provider.\n\nYou declare the `TestAppender` in a `logback.xml` configuration file as follows:\n\n.logback.xml configuration file\n[source,xml]\n----\n\u003cappender name=\"testAppender\" class=\"org.springframework.data.gemfire.tests.logging.slf4j.logback.TestAppender\"\u003e\n    \u003cencoder\u003e\n        \u003cpattern\u003eTEST - %m%n\u003c/pattern\u003e\n    \u003c/encoder\u003e\n\u003c/appender\u003e\n----\n\nThen, the `TestAppender` can be used by registering it with a `Logger`:\n\n.Logger using the TestAppender\n[source,xml]\n----\n\u003clogger name=\"example.app.net.service.NetworkService\" level=\"WARN\"\u003e\n    \u003cappender-ref ref=\"testAppender\"/\u003e\n\u003c/logger\u003e\n----\n\nFor example, assume your application's `NetworkService` class uses the named `Logger` to log network events,\ne.g. a DDoS attack:\n\n.Application component with logging\n[source,java]\n----\n@Service\nclass NetworkService {\n\n    private final Logger logger = LoggerFactory.getLogger(NetworkService.class);\n\n    void processDenialOfServiceAttack(NetworkEvent event) {\n\n        logger.warn(\"A DDoS attack occured at {} from IP Address {}\", event.getTime(), event.getIpAddress());\n\n        // process the network event\n\n        logger.warn(\"Another log message\");\n    }\n\n    void processLoginRequest(LoginRequest request) {\n\n        logger.info(\"User {} is attepting to login\", request.getUser().getName());\n\n        // process login request\n    }\n}\n----\n\nThen, it is a simple matter to test the logging behavior of your application by doing:\n\n.Test logging behavior of the NetworkService class\n[source,java]\n----\nclass NetworkServiceUnitTests {\n\n  private static TestAppender testAppender = TestAppender.getInstance();\n\n  private NetworkService service;\n\n  @Before\n  public void setup() {\n    this.service = new NetworkService();\n  }\n\n  @Test\n  public void processDenialOfServiceAttackLogsNetworkEvent() {\n\n    NetworkEvent event = new NetworkEvent();\n\n    this.service.processDenialOfServiceAttack(event);\n\n    assertThat(testAppender.lastLogMessage())\n      .isEqualTo(\"A DDoS attack occurred at 2019-07-02 19:39:15 from IP Address 10.22.101.16\");\n\n    assertThat(testAppender.lastLogMessage())\n      .isEqualTo(\"Another log message\");\n\n    assertThat(testAppender.lastLogMessage()).isNull();\n  }\n\n  @Test\n  public void processLoginRequestDoesNotLogAnyMessageWithLogLevelSetToWarn() {\n\n      LoginRequest request = new LoginRequest();\n\n      this.service.processLoginRequest(request);\n\n      assertThat(testAppender.lastLogMessage()).isNull();\n  }\n}\n----\n\nYou may also clear any remaining, pending log messages from the in-memory queue (`Stack`)\nby calling `TestAppender.clear()`.\n\nAll log message recorded by the `TestAppender` are stored from the most recent log event to the earliest log event.\nSuccessively calling `TestAppender.lastLogMessage()` gets the most recent, last log message recorded first, then\nthe next log message recorded before the last, most recent log message and so on until no more log messages\nfor the operation under test exists, in which case `null` is returned from `lastLogMessage()` thereafter.\n\n\n[[conclusion]]\n=== Conclusion\n\nAnyway, we hope this has intrigued your interests and gets you started for now.  Ideas, contributions, or other\nfeedback is most welcomed.\n\nThank you!\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspring-projects%2Fspring-test-data-geode","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fspring-projects%2Fspring-test-data-geode","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspring-projects%2Fspring-test-data-geode/lists"}