Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
              Cache cache = getCache();
              DistributedTestCase.disconnectFromDS();
              await().atMost(30, SECONDS).until(() -> {return (cache == null || cache.isClosed());});

is equivalent to:               
              WaitCriterion waitForCacheClose = new WaitCriterion() {
                @Override
                public boolean done() {
                  return cache == null || cache.isClosed();
                }

                @Override
                public String description() {
                  return "Wait for Cache to be closed";
                }
              };
              
              DistributedTestCase.waitForCriterion(waitForCacheClose, 30000, 500, true);

Using Mockito to test region class

Our region classes (LocalRegion, DistributedRegion, PartitionedRegion, BucketRegion, etc) and some other legacy gemfire classes heaily rely on cache, distribute system, distribution manager and other modules to function. Thus it's difficult to mock the class and do the junit test.

However, since it's a common issue for everyone, we introduced a template and 2 sample junit test classes to show how to write junit tests for region class.

The fix is GEODE-774: changed virtualPut(), basicDestroy(), basicInvalidate(), basicUpdateEntryVersion(), not to distribute the event if hasSeen()==true and the event does not contain a valid version tag. 

Let's see how we do junit test for above fix. 

The test logic is simple:

  • create a region
  • create an event with version tag
  • call the methods of the region
  • verify the distribution is triggered or not

 

The challenge is how to mock the required components to create region, event, version tag. 

The com.gemstone.gemfire.test.fake.Fakes class has done the major mocking for us and also defined the expected behaviors between these mock objects. You only need to use it: 

 

Code Block
GemFireCacheImpl cache = Fakes.cache();

 

In DistributedRegionJUnitTest, it gives an example on how to create a DistributedRegion using the mocking. 

Code Block
  protected DistributedRegion prepare(boolean isConcurrencyChecksEnabled) {
    GemFireCacheImpl cache = Fakes.cache(); // using the Fakes to define mock objects and their behavior
    
    // create region attributes and internal region arguments
    RegionAttributes ra = createRegionAttributes(isConcurrencyChecksEnabled);
    InternalRegionArguments ira = new InternalRegionArguments();
    
    doPRSpecific(ira); // for BucketRegion, we need to config some PR attributes
    
    // create a region object
    DistributedRegion region = createAndDefineRegion(isConcurrencyChecksEnabled, ra, ira, cache);
    if (isConcurrencyChecksEnabled) {
      region.enableConcurrencyChecks();
    }
    
    // some additional behaviors to be define for the test
    doNothing().when(region).notifyGatewaySender(any(), any());
    doReturn(true).when(region).hasSeenEvent(any(EntryEventImpl.class));

    return region;
  }

 

The region created here is not a mock object, but a real region. However, we need to monitor its behavior in mocking. 

 

Code Block
    DistributedRegion region = new DistributedRegion("testRegion", ra, null, cache, ira);
    if (isConcurrencyChecksEnabled) {
      region.enableConcurrencyChecks();
    }
    
    // since it is a real region object, we need to tell mockito to monitor it
    region = Mockito.spy(region);

    doNothing().when(region).distributeUpdate(any(), anyLong(), anyBoolean(), anyBoolean(), any(), anyBoolean());
    doNothing().when(region).distributeDestroy(any(), any());
    doNothing().when(region).distributeInvalidate(any());
    doNothing().when(region).distributeUpdateEntryVersion(any());

 

BucketRegionJUnitTest is a subclass of DistributedRegionJUnitTest to reuse most of the logic. However it needs to implement the doPRSpecific() for more mocking. 

Code Block
  protected void doPRSpecific(InternalRegionArguments ira) {
    // PR specific
    PartitionedRegion pr = mock(PartitionedRegion.class);
    BucketAdvisor ba = mock(BucketAdvisor.class);
    ReadWriteLock primaryMoveLock = new ReentrantReadWriteLock();
    Lock activeWriteLock = primaryMoveLock.readLock();
    when(ba.getActiveWriteLock()).thenReturn(activeWriteLock);
    when(ba.isPrimary()).thenReturn(true);
    
    ira.setPartitionedRegion(pr)
      .setPartitionedRegionBucketRedundancy(1)
      .setBucketAdvisor(ba);
  }

 

Overall, the Fakes.java, DistributedRegionJUnitTest, BucketRegionJUnitTest  are a good start to build a shareable template to write mock-based test cases for gemfire legacy components.