0

For unit-testing Services which use GemFire, we are planning to include GemFire 'Regions' in the test-context to avoid mocking and have any dependency on the Gemfire server. What are the best-practices / tools available to enable this?

Theoritically, below is the option we are planning:

Service --> GemFire(embedded in test-context)

To bootstrap context, ContextConfiguration: add a test-applicationContext which has reference to the gfe-cache xml.

Junits will load the test-context and bootstrap the Regions, now we can fire the create/fetch/delete using GemFire and validate the results.

Jebuselwyn Martin
  • 543
  • 1
  • 5
  • 18

1 Answers1

1

What you are describing are Integration Tests and something Spring Data GemFire does almost exactly as you describe, albeit a bit more directly (i.e. injecting GemFire Regions or perhaps other GemFire components directly into test classes). For instance have a look at the GemFireTemplateIntegrationTest class along with it's associated Spring (XML) config.

This makes sense for Spring Data GemFire to use GemFire components directly in tests. Although, this is perhaps less than ideal in an actual application test suite, primarily because I believe in good Separation of Concerns and providing appropriate facades around the dependencies my application uses.

In other words, and as you alluded to above, having the following (traditional n) tiers in the application...

UI -> Service -> DAO

The Service tier is pure business logic and rules and any interactions (CRUD, Querying, Function execution, etc) with any data store (GemFire/Geode included) is done so via the DAO.

This makes mocking the DAO very easy so that you can focus on the point of the Service tests, to test the business logic and rules independently of how the underlying data store behaves.

Of course, it is important to have Integration Tests to ensure the proper interactions with your underlying data store, such as GemFire/Geode, if only to ensure proper transactional behavior, or that your OQL (query) statements are well formed.

But, there are many options when it comes to implementing your DAO.

  1. You can inject the Region into your DAO(s) and perform operations (CRUD, Queries, etc) directly on the Region.

  2. If you are using Spring Data GemFire, you might prefer to shield your DAO(s) from direct use of the GemFire/Geode API using the GemfireTemplate (in case of interface breaking changes introduced by GemFire/Geode, or to wrap GemFire/Geode Exceptions in Spring's convenient and consistent (across data stores) Exception class hierarchy, which is useful if you ever swap out data stores). See here for more details.

  3. Lastly (but not least), you can use Spring Data GemFire's extension of Spring Data Common's Repository abstraction with support for GemFire/Geode. This makes implementing DAOs (a.k.a. Repositories) as simples as defining an interface that extends GemfireRepository.

You choice depends on the level of abstraction you prefer and each choice will slightly change how you approach your Integration Testing.

As one final tidbit, this does not preclude you from still writing true Unit Tests either.

Spring Data GemFire employs a custom test framework (with mocks and stubs) to simplify Unit Tests involving GemFire components (such as Regions, AEQs, Gateways, etc). This "custom test framework" is rooted in the GemfireTestApplicationContextInitializer and the associated GemfireTestBeanPostProcessor. If you follow the logic through you will begin to see how it works.

This custom test framework is very useful for testing the validity of the GemFire components created and initialized using SDG's XML namespace. However, it is becoming increasingly popular to put configuration meta-data, even for GemFire/Geode components into Spring config now, something I am looking to enhance/simplify further in Spring Data GemFire 1.9.

In addition, I hope to, at some point, work that I have already begun, to uplift and refactor Spring Data GemFire's custom test framework into a separate, top-level Spring project extension for Spring Data GemFire as this sort of question has been asked a lot.

Anyway, hopefully this gives you some ideas how to best approach tests for you application in a simple, concise and consistent manner.

Cheers!

John Blum
  • 7,381
  • 1
  • 20
  • 30
  • Thanks for the clear & detailed response John! I agree in principle on the scope of UnitTests, but there are always few pieces which have some shared based-data which are easier to have it in the Datasource rather than mocking every object. We are using Option#3 for the DAO's relying on the spring-data abstraction. What are the options to have it integration-testable without connecting to the common-gemfire server? I just want to use as much as the existing cache-definitions with just a few config which will make the server-caches behave as in-memory cache regions and testable. – Jebuselwyn Martin Jun 21 '16 at 06:55
  • Really, it is just a matter of avoiding the costs of starting up an (embedded) GemFire instance, but you can minimize that cost by setting a few GemFire System properties up front, such as the log-level to (minimally) 'warning' and mcast-port to '0'. I have done this in many of SDG's integration tests (e.g. https://github.com/jxblum/spring-data-gemfire/blob/master/src/test/resources/org/springframework/data/gemfire/GemfireTemplateIntegrationTest-context.xml#L13-L17)... – John Blum Jun 21 '16 at 19:02
  • By setting mcast-port in particular (with no locators specified), you create a standalone node that minimizes the overhead of distribution (reserving ports, creating Sockets, searching for other members, etc) which is unnecessary if you are not testing against a multi-node cluster in your test suite. Typically, a single node is sufficient for most test cases. Setting up a multi-node cluster is both expensive and complex, something the GemFire team created a separate framework for (called Hydra). It is not trivial by any means. – John Blum Jun 21 '16 at 19:14
  • However, I agree with you; you definitely want tests using your actual Spring XML/Java config meta-data, if only to verify GemFire is configured properly and behaves how your application expects it to. When it comes to data (and verifying queries/OQL statements) there is no substitute for datasource other than using the actual datasource itself, especially for shared data. All this is to say, I think you are going down the right path. Hopefully, in time, the extensions I am building specifically for testing will help all users simplify integration tests using GemFire. Cheers. – John Blum Jun 21 '16 at 19:15
  • Thanks!. The embedded gemfire works after following the samples from spring-data-gemfire. But involved a bit of hit & try.. Additional documentation for int-testing Gemfire would really help! – Jebuselwyn Martin Jun 25 '16 at 10:30
  • Good to hear. Right, so my plan in time is to pull out the integrating testing framework and supporting classes from SDG and put it into a separate project that can be used independently, which will include support for both unit as well as integration testing, even in client/server and distributed, p2p scenarios. It just needs some cleaning up before I can publish it, but the work is underway. – John Blum Jul 03 '16 at 01:40
  • Hey @JohnBlum, I know it's been a while since this last comment but what is the status of what you describe above? (the testing framework) – Cahlen Humphreys Feb 14 '19 at 08:46
  • See here... https://github.com/spring-projects/spring-test-data-geode. This project is still a WIP (I still need to write documentation, provide examples, etc). But, I have used STDG extensively in SBDG's test suite (https://github.com/spring-projects/spring-boot-data-geode) and SSDG's test suite (https://github.com/spring-projects/spring-session-data-geode). So, those projects can be used for reference and usage purposes. I have yet to overhaul SDG's test suite to rebase it on STDG. If you have any questions, let me know. Hope this helps. – John Blum Feb 14 '19 at 16:18