[NFC] [REF] Test class cleanup
authorEileen McNaughton <emcnaughton@wikimedia.org>
Sun, 24 Oct 2021 23:27:26 +0000 (12:27 +1300)
committerEileen McNaughton <emcnaughton@wikimedia.org>
Mon, 25 Oct 2021 00:11:00 +0000 (13:11 +1300)
The driver for this was to remove uses of the DAO->createTestObject methods which were creating
individuals with data invalid for them. However, this is a full 'margin cleanup' and
standardises the test.

I think the whole createTestObject approach is best viewed as an early attempt to get
lots of entities testable quickly but it's overwraught for creating contacts
(and generally a bit flakey)

tests/phpunit/CRM/Contact/BAO/GroupContactCacheTest.php

index 41cdc5ea91bf999a7548627d2c053641590f9597..0583354b4e231242bda378418feae8bdf1fe24fb 100644 (file)
@@ -9,6 +9,8 @@
  +--------------------------------------------------------------------+
  */
 
+use Civi\Api4\Group;
+
 /**
  * Test class for CRM_Contact_BAO_GroupContact BAO
  *
@@ -29,19 +31,19 @@ class CRM_Contact_BAO_GroupContactCacheTest extends CiviUnitTestCase {
 
     // Add $n1 to $g
     $this->callAPISuccess('GroupContact', 'create', [
-      'contact_id' => $living[0]->id,
+      'contact_id' => $living[0],
       'group_id' => $group->id,
     ]);
 
     CRM_Contact_BAO_GroupContactCache::load($group);
     $this->assertCacheMatches(
-      [$deceased[0]->id, $deceased[1]->id, $deceased[2]->id, $living[0]->id],
-      $group->id
+      [$deceased[0], $deceased[1], $deceased[2], $living[0]],
+      $this->ids['Group'][0]
     );
 
     // Remove $y1 from $g
     $this->callAPISuccess('group_contact', 'create', [
-      'contact_id' => $deceased[0]->id,
+      'contact_id' => $deceased[0],
       'group_id' => $group->id,
       'status' => 'Removed',
     ]);
@@ -49,9 +51,9 @@ class CRM_Contact_BAO_GroupContactCacheTest extends CiviUnitTestCase {
     CRM_Contact_BAO_GroupContactCache::load($group);
     $this->assertCacheMatches(
       [
-        $deceased[1]->id,
-        $deceased[2]->id,
-        $living[0]->id,
+        $deceased[1],
+        $deceased[2],
+        $living[0],
       ],
       $group->id
     );
@@ -61,12 +63,15 @@ class CRM_Contact_BAO_GroupContactCacheTest extends CiviUnitTestCase {
    * Allow removing contact from a parent group even if contact is in a child
    * group. (CRM-8858).
    *
+   * @throws \API_Exception
    * @throws \CRM_Core_Exception
+   * @throws \CiviCRM_API3_Exception
    */
   public function testRemoveFromParentSmartGroup(): void {
     // Create $c1, $c2, $c3
-    $deceased = $this->createTestObject('CRM_Contact_DAO_Contact', ['is_deceased' => 1], 3);
-
+    $deceased[] = $this->individualCreate(['is_deceased' => 1]);
+    $deceased[] = $this->individualCreate(['is_deceased' => 1]);
+    $deceased[] = $this->individualCreate(['is_deceased' => 1]);
     // Create smart group $parent
     $params = [
       'name' => 'Deceased Contacts',
@@ -74,36 +79,33 @@ class CRM_Contact_BAO_GroupContactCacheTest extends CiviUnitTestCase {
       'is_active' => 1,
       'formValues' => ['is_deceased' => 1],
     ];
-    $parent = CRM_Contact_BAO_Group::createSmartGroup($params);
-    $this->registerTestObjects([$parent]);
+    $parent = $this->createSmartGroup($params);
 
     // Create group $child in $parent
-    $params = [
+    $child = $this->callAPISuccess('Group', 'create', [
       'name' => 'Child Group',
       'title' => 'Child Group',
       'is_active' => 1,
       'parents' => [$parent->id => 1],
-    ];
-    $child = CRM_Contact_BAO_Group::create($params);
-    $this->registerTestObjects([$child]);
+    ]);
 
     // Add $c1, $c2, $c3 to $child
     foreach ($deceased as $contact) {
       $this->callAPISuccess('group_contact', 'create', [
-        'contact_id' => $contact->id,
-        'group_id' => $child->id,
+        'contact_id' => $contact,
+        'group_id' => $child['id'],
       ]);
     }
 
     CRM_Contact_BAO_GroupContactCache::load($parent);
     $this->assertCacheMatches(
-      [$deceased[0]->id, $deceased[1]->id, $deceased[2]->id],
+      [$deceased[0], $deceased[1], $deceased[2]],
       $parent->id
     );
 
     // Remove $c1 from $parent
     $this->callAPISuccess('GroupContact', 'create', [
-      'contact_id' => $deceased[0]->id,
+      'contact_id' => $deceased[0],
       'group_id' => $parent->id,
       'status' => 'Removed',
     ]);
@@ -112,8 +114,8 @@ class CRM_Contact_BAO_GroupContactCacheTest extends CiviUnitTestCase {
     CRM_Contact_BAO_GroupContactCache::load($parent);
     $this->assertCacheMatches(
       [
-        $deceased[1]->id,
-        $deceased[2]->id,
+        $deceased[1],
+        $deceased[2],
       ],
       $parent->id
     );
@@ -122,8 +124,8 @@ class CRM_Contact_BAO_GroupContactCacheTest extends CiviUnitTestCase {
     $this->assertDBQuery(1,
       'select count(*) from civicrm_group_contact where group_id=%1 and contact_id=%2 and status=%3',
       [
-        1 => [$child->id, 'Integer'],
-        2 => [$deceased[0]->id, 'Integer'],
+        1 => [$child['id'], 'Integer'],
+        2 => [$deceased[0], 'Integer'],
         3 => ['Added', 'String'],
       ]
     );
@@ -136,7 +138,7 @@ class CRM_Contact_BAO_GroupContactCacheTest extends CiviUnitTestCase {
    *   Array(int).
    * @param int $groupId
    */
-  public function assertCacheMatches($expectedContactIds, $groupId): void {
+  public function assertCacheMatches(array $expectedContactIds, int $groupId): void {
     $sql = 'SELECT contact_id FROM civicrm_group_contact_cache WHERE group_id = %1';
     $params = [1 => [$groupId, 'Integer']];
     $dao = CRM_Core_DAO::executeQuery($sql, $params);
@@ -153,68 +155,74 @@ class CRM_Contact_BAO_GroupContactCacheTest extends CiviUnitTestCase {
   /**
    * Test the opportunistic refresh cache function does not touch non-expired entries.
    */
-  public function testOpportunisticRefreshCacheNoChangeIfNotExpired() {
-    [$group, $living, $deceased] = $this->setupSmartGroup();
-    $this->callAPISuccess('Contact', 'create', ['id' => $deceased[0]->id, 'is_deceased' => 0]);
+  public function testOpportunisticRefreshCacheNoChangeIfNotExpired(): void {
+    [$group, , $deceased] = $this->setupSmartGroup();
+    $this->callAPISuccess('Contact', 'create', ['id' => $deceased[0], 'is_deceased' => 0]);
     $this->assertCacheMatches(
-      [$deceased[0]->id, $deceased[1]->id, $deceased[2]->id],
+      [$deceased[0], $deceased[1], $deceased[2]],
       $group->id
     );
     CRM_Contact_BAO_GroupContactCache::opportunisticCacheFlush();
 
-    $this->assertCacheNotRefreshed($deceased, $group);
+    $this->assertCacheNotRefreshed($deceased, $group->id, $group->cache_date);
   }
 
   /**
    * Test the opportunistic refresh cache function does refresh stale entries.
    */
-  public function testOpportunisticRefreshChangeIfCacheDateFieldStale() {
-    [$group, $living, $deceased] = $this->setupSmartGroup();
-    $this->callAPISuccess('Contact', 'create', ['id' => $deceased[0]->id, 'is_deceased' => 0]);
+  public function testOpportunisticRefreshChangeIfCacheDateFieldStale(): void {
+    [$group, , $deceased] = $this->setupSmartGroup();
+    $this->callAPISuccess('Contact', 'create', ['id' => $deceased[0], 'is_deceased' => 0]);
     CRM_Core_DAO::executeQuery('UPDATE civicrm_group SET cache_date = DATE_SUB(NOW(), INTERVAL 7 MINUTE) WHERE id = ' . $group->id);
     $group->find(TRUE);
     Civi::$statics['CRM_Contact_BAO_GroupContactCache']['is_refresh_init'] = FALSE;
     sleep(1);
     CRM_Contact_BAO_GroupContactCache::opportunisticCacheFlush();
 
-    $this->assertCacheRefreshed($group);
+    $this->assertCacheRefreshed($this->ids['Group'][0]);
   }
 
   /**
-   * Test the opportunistic refresh cache function does refresh expired entries if mode is deterministic.
+   * Test the opportunistic refresh cache function does refresh expired entries
+   * if mode is deterministic.
+   *
+   * @throws \API_Exception
    */
-  public function testOpportunisticRefreshNoChangeWithDeterministicSetting() {
-    [$group, $living, $deceased] = $this->setupSmartGroup();
+  public function testOpportunisticRefreshNoChangeWithDeterministicSetting(): void {
+    [, $deceased] = $this->setupSmartGroup();
     $this->callAPISuccess('Setting', 'create', ['smart_group_cache_refresh_mode' => 'deterministic']);
-    $this->callAPISuccess('Contact', 'create', ['id' => $deceased[0]->id, 'is_deceased' => 0]);
-    $this->makeCacheStale($group);
+    $this->callAPISuccess('Contact', 'create', ['id' => $this->ids['Contact']['dead1'], 'is_deceased' => 0]);
+    $this->makeCacheStale($this->ids['Group'][0]);
+    $cacheDate = Group::get()
+      ->addWhere('id', '=', $this->ids['Group'][0])
+      ->addSelect('cache_date')->execute()->first()['cache_date'];
     CRM_Contact_BAO_GroupContactCache::opportunisticCacheFlush();
-    $this->assertCacheNotRefreshed($deceased, $group);
+    $this->assertCacheNotRefreshed($deceased, $this->ids['Group'][0], $cacheDate);
     $this->callAPISuccess('Setting', 'create', ['smart_group_cache_refresh_mode' => 'opportunistic']);
   }
 
   /**
    * Test the deterministic cache function refreshes with the deterministic setting.
    */
-  public function testDeterministicRefreshChangeWithDeterministicSetting() {
-    [$group, $living, $deceased] = $this->setupSmartGroup();
+  public function testDeterministicRefreshChangeWithDeterministicSetting(): void {
+    $this->setupSmartGroup();
     $this->callAPISuccess('Setting', 'create', ['smart_group_cache_refresh_mode' => 'deterministic']);
-    $this->callAPISuccess('Contact', 'create', ['id' => $deceased[0]->id, 'is_deceased' => 0]);
-    $this->makeCacheStale($group);
+    $this->callAPISuccess('Contact', 'create', ['id' => $this->ids['Contact']['dead1'], 'is_deceased' => 0]);
+    $this->makeCacheStale($this->ids['Group'][0]);
     CRM_Contact_BAO_GroupContactCache::deterministicCacheFlush();
-    $this->assertCacheRefreshed($group);
+    $this->assertCacheRefreshed($this->ids['Group'][0]);
     $this->callAPISuccess('Setting', 'create', ['smart_group_cache_refresh_mode' => 'opportunistic']);
   }
 
   /**
    * Test the deterministic cache function refresh doesn't mess up non-expired.
    */
-  public function testDeterministicRefreshChangeDoesNotTouchNonExpired() {
-    [$group, $living, $deceased] = $this->setupSmartGroup();
+  public function testDeterministicRefreshChangeDoesNotTouchNonExpired(): void {
+    [$group, , $deceased] = $this->setupSmartGroup();
     $this->callAPISuccess('Setting', 'create', ['smart_group_cache_refresh_mode' => 'deterministic']);
-    $this->callAPISuccess('Contact', 'create', ['id' => $deceased[0]->id, 'is_deceased' => 0]);
+    $this->callAPISuccess('Contact', 'create', ['id' => $deceased[0], 'is_deceased' => 0]);
     CRM_Contact_BAO_GroupContactCache::deterministicCacheFlush();
-    $this->assertCacheNotRefreshed($deceased, $group);
+    $this->assertCacheNotRefreshed($deceased, $group->id, $group->cache_date);
     $this->callAPISuccess('Setting', 'create', ['smart_group_cache_refresh_mode' => 'opportunistic']);
   }
 
@@ -223,97 +231,37 @@ class CRM_Contact_BAO_GroupContactCacheTest extends CiviUnitTestCase {
    *
    * (hey it's an opportunity!).
    */
-  public function testDeterministicRefreshChangeWithOpportunisticSetting() {
-    [$group, $living, $deceased] = $this->setupSmartGroup();
+  public function testDeterministicRefreshChangeWithOpportunisticSetting(): void {
+    [, $deceased] = $this->setupSmartGroup();
     $this->callAPISuccess('Setting', 'create', ['smart_group_cache_refresh_mode' => 'opportunistic']);
-    $this->callAPISuccess('Contact', 'create', ['id' => $deceased[0]->id, 'is_deceased' => 0]);
-    $this->makeCacheStale($group);
+    $this->callAPISuccess('Contact', 'create', ['id' => $deceased[0], 'is_deceased' => 0]);
+    $this->makeCacheStale($this->ids['Group'][0]);
     CRM_Contact_BAO_GroupContactCache::deterministicCacheFlush();
-    $this->assertCacheRefreshed($group);
+    $this->assertCacheRefreshed($this->ids['Group'][0]);
   }
 
   /**
    * Test the api job wrapper around the deterministic refresh works.
    */
-  public function testJobWrapper() {
-    [$group, $living, $deceased] = $this->setupSmartGroup();
+  public function testJobWrapper(): void {
+    $this->setupSmartGroup();
     $this->callAPISuccess('Setting', 'create', ['smart_group_cache_refresh_mode' => 'opportunistic']);
-    $this->callAPISuccess('Contact', 'create', ['id' => $deceased[0]->id, 'is_deceased' => 0]);
-    $this->makeCacheStale($group);
+    $this->callAPISuccess('Contact', 'create', ['id' => $this->ids['Contact']['dead1'], 'is_deceased' => 0]);
+    $this->makeCacheStale($this->ids['Group'][0]);
     $this->callAPISuccess('Job', 'group_cache_flush', []);
-    $this->assertCacheRefreshed($group);
+    $this->assertCacheRefreshed($this->ids['Group'][0]);
   }
 
   // *** Everything below this should be moved to parent class ****
 
-  /**
-   * @var array
-   * (DAO_Name => array(int)) List of items to garbage-collect during tearDown
-   */
-  private $_testObjects;
-
   /**
    * Tears down the fixture, for example, closes a network connection.
    *
    * This method is called after a test is executed.
    */
   protected function tearDown(): void {
+    $this->quickCleanup(['civicrm_contact', 'civicrm_group', 'civicrm_saved_search', 'civicrm_group_contact']);
     parent::tearDown();
-    $this->deleteTestObjects();
-  }
-
-  /**
-   * This is a wrapper for CRM_Core_DAO::createTestObject which tracks created entities.
-   *
-   * @see CRM_Core_DAO::createTestObject
-   *
-   * @param string $daoName
-   * @param array $params
-   * @param int $numObjects
-   * @param bool $createOnly
-   *
-   * @return array|NULL|object
-   */
-  public function createTestObject($daoName, $params = [], $numObjects = 1, $createOnly = FALSE) {
-    $objects = CRM_Core_DAO::createTestObject($daoName, $params, $numObjects, $createOnly);
-    if (is_array($objects)) {
-      $this->registerTestObjects($objects);
-    }
-    else {
-      $this->registerTestObjects([$objects]);
-    }
-    return $objects;
-  }
-
-  /**
-   * Register test objects.
-   *
-   * @param array $objects
-   *   DAO or BAO objects.
-   */
-  public function registerTestObjects($objects) {
-    foreach ($objects as $object) {
-      $daoName = preg_replace('/_BAO_/', '_DAO_', get_class($object));
-      $this->_testObjects[$daoName][] = $object->id;
-    }
-  }
-
-  /**
-   * Delete test objects.
-   *
-   * Note: You might argue that the FK relations between test
-   * objects could make this problematic; however, it should
-   * behave intuitively as long as we mentally split our
-   *  test-objects between the "manual/primary records"
-   * and the "automatic/secondary records"
-   */
-  public function deleteTestObjects() {
-    foreach ($this->_testObjects as $daoName => $daoIds) {
-      foreach ($daoIds as $daoId) {
-        CRM_Core_DAO::deleteTestObjects($daoName, ['id' => $daoId]);
-      }
-    }
-    $this->_testObjects = [];
   }
 
   /**
@@ -322,85 +270,84 @@ class CRM_Contact_BAO_GroupContactCacheTest extends CiviUnitTestCase {
    * @return array
    */
   protected function setupSmartGroup(): array {
-    // Create contacts $y1, $y2, $y3 which do match $g; create $n1, $n2, $n3 which do not match $g
-    $living = $this->createTestObject('CRM_Contact_DAO_Contact', ['is_deceased' => 0], 3);
-    $deceased = $this->createTestObject('CRM_Contact_DAO_Contact', ['is_deceased' => 1], 3);
-    $this->assertCount(3, $deceased);
-    $this->assertCount(3, $living);
-
-    $params = [
-      'name' => 'Deceased Contacts',
-      'title' => 'Deceased Contacts',
-      'is_active' => 1,
-      'formValues' => ['is_deceased' => 1],
-    ];
-    $group = CRM_Contact_BAO_Group::createSmartGroup($params);
-    $this->registerTestObjects([$group]);
-
-    // Assert: $g cache has exactly $y1, $y2, $y3
-    CRM_Contact_BAO_GroupContactCache::load($group);
-    $group->find(TRUE);
-    $this->assertCacheMatches(
-      [$deceased[0]->id, $deceased[1]->id, $deceased[2]->id],
-      $group->id
-    );
-    // Reload the group so we have the cache_date & refresh_date.
-    return [$group, $living, $deceased];
+    try {
+      // Create contacts $y1, $y2, $y3 which do match $g; create $n1, $n2, $n3 which do not match $g
+      [$living, $deceased] = $this->createLivingDead();
+
+      $params = [
+        'name' => 'Deceased Contacts',
+        'title' => 'Deceased Contacts',
+        'is_active' => 1,
+        'formValues' => ['is_deceased' => 1],
+      ];
+      $group = $this->createSmartGroup($params);
+      $this->ids['Group'][0] = $group->id;
+      // Assert: $g cache has exactly $y1, $y2, $y3
+      CRM_Contact_BAO_GroupContactCache::load($group);
+      $group->find(TRUE);
+      $this->assertCacheMatches(
+        [$deceased[0], $deceased[1], $deceased[2]],
+        $group->id
+      );
+      return [$group, $living, $deceased];
+    }
+    catch (CRM_Core_Exception | API_Exception | CiviCRM_API3_Exception $e) {
+      $this->fail('failed test setup' . $e->getMessage());
+    }
+    // unreachable but it cheers up IDE analysis.
+    return [];
   }
 
   /**
-   * @param $deceased
-   * @param $group
-   *
-   * @throws \Exception
+   * @param array $deceased
+   * @param int $groupID
+   * @param string $cacheDate
    */
-  protected function assertCacheNotRefreshed($deceased, $group) {
+  protected function assertCacheNotRefreshed(array $deceased, int $groupID, string $cacheDate): void {
     $this->assertCacheMatches(
-      [$deceased[0]->id, $deceased[1]->id, $deceased[2]->id],
-      $group->id
+      [$deceased[0], $deceased[1], $deceased[2]],
+      $groupID
     );
-    $afterGroup = $this->callAPISuccessGetSingle('Group', ['id' => $group->id]);
-    $this->assertEquals($group->cache_date, $afterGroup['cache_date']);
+    $afterGroup = $this->callAPISuccessGetSingle('Group', ['id' => $groupID]);
+    $this->assertEquals($cacheDate, $afterGroup['cache_date']);
   }
 
   /**
    * Make the cache for the group stale, resetting it to before the timeout period.
    *
-   * @param CRM_Contact_BAO_Group $group
+   * @param int $groupID
    */
-  protected function makeCacheStale(&$group) {
-    CRM_Core_DAO::executeQuery('UPDATE civicrm_group SET cache_date = DATE_SUB(NOW(), INTERVAL 7 MINUTE) WHERE id = ' . $group->id);
-    unset($group->cache_date);
-    $group->find(TRUE);
+  protected function makeCacheStale(int $groupID): void {
+    CRM_Core_DAO::executeQuery('UPDATE civicrm_group SET cache_date = DATE_SUB(NOW(), INTERVAL 7 MINUTE) WHERE id = ' . $groupID);
     Civi::$statics['CRM_Contact_BAO_GroupContactCache']['is_refresh_init'] = FALSE;
   }
 
   /**
-   * @param $group
-   *
-   * @throws \Exception
+   * @param int $groupID
    */
-  protected function assertCacheRefreshed($group) {
+  protected function assertCacheRefreshed(int $groupID): void {
     $this->assertCacheMatches(
       [],
-      $group->id
+      $groupID
     );
 
-    $afterGroup = $this->callAPISuccessGetSingle('Group', ['id' => $group->id]);
-    $this->assertTrue(empty($afterGroup['cache_date']), 'cache date should not be set as the cache is not built');
+    $afterGroup = $this->callAPISuccessGetSingle('Group', ['id' => $groupID]);
+    $this->assertArrayNotHasKey('cache_date', $afterGroup, 'cache date should not be set as the cache is not built');
   }
 
   /**
    * Test Smart group search
+   *
+   * @throws \CRM_Core_Exception
    */
-  public function testSmartGroupSearchBuilder() {
+  public function testSmartGroupSearchBuilder(): void {
     $returnProperties = [
       'contact_type' => 1,
       'contact_sub_type' => 1,
       'sort_name' => 1,
       'group' => 1,
     ];
-    [$group, $living, $deceased] = $this->setupSmartGroup();
+    [$group] = $this->setupSmartGroup();
 
     $params = [
       'name' => 'Living Contacts',
@@ -408,7 +355,7 @@ class CRM_Contact_BAO_GroupContactCacheTest extends CiviUnitTestCase {
       'is_active' => 1,
       'formValues' => ['is_deceased' => 0],
     ];
-    $group2 = CRM_Contact_BAO_Group::createSmartGroup($params);
+    $group2 = $this->createSmartGroup($params);
 
     //Filter on smart group with =, !=, IN and NOT IN operator.
     $params = [['group', '=', $group2->id, 1, 0]];
@@ -418,14 +365,14 @@ class CRM_Contact_BAO_GroupContactCacheTest extends CiviUnitTestCase {
       FALSE,
       FALSE, FALSE
     );
-    $ids = $query->searchQuery(0, 0, NULL,
+    $query->searchQuery(0, 0, NULL,
       FALSE, FALSE, FALSE,
-      TRUE, FALSE
+      TRUE
     );
     $key = $query->getGroupCacheTableKeys()[0];
-    $expectedWhere = "civicrm_group_contact_cache_{$key}.group_id IN (\"{$group2->id}\")";
+    $expectedWhere = "civicrm_group_contact_cache_$key.group_id IN (\"$group2->id\")";
     $this->assertStringContainsString($expectedWhere, $query->_whereClause);
-    $this->_assertContactIds($query, "group_id = {$group2->id}");
+    $this->assertContactIDsAreInCache($query, "group_id = $group2->id");
 
     $params = [['group', '!=', $group->id, 1, 0]];
     $query = new CRM_Contact_BAO_Query(
@@ -436,9 +383,9 @@ class CRM_Contact_BAO_GroupContactCacheTest extends CiviUnitTestCase {
     );
     $key = $query->getGroupCacheTableKeys()[0];
     //Assert if proper where clause is present.
-    $expectedWhere = "civicrm_group_contact_{$key}.group_id != {$group->id} AND civicrm_group_contact_cache_{$key}.group_id IS NULL OR  ( civicrm_group_contact_cache_{$key}.contact_id NOT IN (SELECT contact_id FROM civicrm_group_contact_cache cgcc WHERE cgcc.group_id IN ( {$group->id} ) ) )";
+    $expectedWhere = "civicrm_group_contact_$key.group_id != $group->id AND civicrm_group_contact_cache_$key.group_id IS NULL OR  ( civicrm_group_contact_cache_$key.contact_id NOT IN (SELECT contact_id FROM civicrm_group_contact_cache cgcc WHERE cgcc.group_id IN ( $group->id ) ) )";
     $this->assertStringContainsString($expectedWhere, $query->_whereClause);
-    $this->_assertContactIds($query, "group_id != {$group->id}");
+    $this->assertContactIDsAreInCache($query, "group_id != $group->id");
 
     $params = [['group', 'IN', [$group->id, $group2->id], 1, 0]];
     $query = new CRM_Contact_BAO_Query(
@@ -448,9 +395,9 @@ class CRM_Contact_BAO_GroupContactCacheTest extends CiviUnitTestCase {
       FALSE, FALSE
     );
     $key = $query->getGroupCacheTableKeys()[0];
-    $expectedWhere = "civicrm_group_contact_cache_{$key}.group_id IN (\"{$group->id}\", \"{$group2->id}\")";
+    $expectedWhere = "civicrm_group_contact_cache_$key.group_id IN (\"$group->id\", \"$group2->id\")";
     $this->assertStringContainsString($expectedWhere, $query->_whereClause);
-    $this->_assertContactIds($query, "group_id IN ({$group->id}, {$group2->id})");
+    $this->assertContactIDsAreInCache($query, "group_id IN ($group->id, $group2->id)");
 
     $params = [['group', 'NOT IN', [$group->id], 1, 0]];
     $query = new CRM_Contact_BAO_Query(
@@ -460,21 +407,24 @@ class CRM_Contact_BAO_GroupContactCacheTest extends CiviUnitTestCase {
       FALSE, FALSE
     );
     $key = $query->getGroupCacheTableKeys()[0];
-    $expectedWhere = "civicrm_group_contact_{$key}.group_id NOT IN ( {$group->id} ) AND civicrm_group_contact_cache_{$key}.group_id IS NULL OR  ( civicrm_group_contact_cache_{$key}.contact_id NOT IN (SELECT contact_id FROM civicrm_group_contact_cache cgcc WHERE cgcc.group_id IN ( {$group->id} ) ) )";
+    $expectedWhere = "civicrm_group_contact_$key.group_id NOT IN ( $group->id ) AND civicrm_group_contact_cache_$key.group_id IS NULL OR  ( civicrm_group_contact_cache_$key.contact_id NOT IN (SELECT contact_id FROM civicrm_group_contact_cache cgcc WHERE cgcc.group_id IN ( $group->id ) ) )";
     $this->assertStringContainsString($expectedWhere, $query->_whereClause);
-    $this->_assertContactIds($query, "group_id NOT IN ({$group->id})");
+    $this->assertContactIDsAreInCache($query, "group_id NOT IN ($group->id)");
     $this->callAPISuccess('group', 'delete', ['id' => $group->id]);
     $this->callAPISuccess('group', 'delete', ['id' => $group2->id]);
   }
 
-  public function testMultipleGroupWhereClause() {
+  /**
+   * @throws \CRM_Core_Exception
+   */
+  public function testMultipleGroupWhereClause(): void {
     $returnProperties = [
       'contact_type' => 1,
       'contact_sub_type' => 1,
       'sort_name' => 1,
       'group' => 1,
     ];
-    [$group, $living, $deceased] = $this->setupSmartGroup();
+    [$group] = $this->setupSmartGroup();
 
     $params = [
       'name' => 'Living Contacts',
@@ -482,7 +432,7 @@ class CRM_Contact_BAO_GroupContactCacheTest extends CiviUnitTestCase {
       'is_active' => 1,
       'formValues' => ['is_deceased' => 0],
     ];
-    $group2 = CRM_Contact_BAO_Group::createSmartGroup($params);
+    $group2 = $this->createSmartGroup($params);
 
     //Filter on smart group with =, !=, IN and NOT IN operator.
     $params = [['group', '=', $group2->id, 1, 0], ['group', '=', $group->id, 1, 0]];
@@ -492,12 +442,12 @@ class CRM_Contact_BAO_GroupContactCacheTest extends CiviUnitTestCase {
       FALSE,
       FALSE, FALSE
     );
-    $ids = $query->searchQuery(0, 0, NULL,
+    $query->searchQuery(0, 0, NULL,
       FALSE, FALSE, FALSE,
-      TRUE, FALSE
+      TRUE
     );
-    $key1 = $query->getGroupCacheTableKeys()[0];
-    $key2 = $query->getGroupCacheTableKeys()[1];
+    [$key1, $key2] = $query->getGroupCacheTableKeys();
+
     $expectedWhere = 'civicrm_group_contact_cache_' . $key1 . '.group_id IN ("' . $group2->id . '") )  )  AND  (  ( civicrm_group_contact_cache_' . $key2 . '.group_id IN ("' . $group->id . '")';
     $this->assertStringContainsString($expectedWhere, $query->_whereClause);
     // Check that we have 3 joins to the group contact cache 1 for each of the group where clauses and 1 for the fact we are returning groups in the select.
@@ -512,20 +462,47 @@ class CRM_Contact_BAO_GroupContactCacheTest extends CiviUnitTestCase {
   /**
    * Check if contact ids are fetched correctly.
    *
-   * @param object $query
+   * @param \CRM_Contact_BAO_Query $query
    * @param string $groupWhereClause
    */
-  public function _assertContactIds($query, $groupWhereClause) {
+  public function assertContactIDsAreInCache(CRM_Contact_BAO_Query $query, string $groupWhereClause): void {
     $contactIds = explode(',', $query->searchQuery(0, 0, NULL,
       FALSE, FALSE, FALSE,
-      TRUE, FALSE
+      TRUE
     ));
     $expectedContactIds = [];
-    $groupDAO = CRM_Core_DAO::executeQuery("SELECT contact_id FROM civicrm_group_contact_cache WHERE {$groupWhereClause}");
+    $groupDAO = CRM_Core_DAO::executeQuery("SELECT contact_id FROM civicrm_group_contact_cache WHERE $groupWhereClause");
     while ($groupDAO->fetch()) {
       $expectedContactIds[] = $groupDAO->contact_id;
     }
     $this->assertEquals(sort($expectedContactIds), sort($contactIds));
   }
 
+  /**
+   * @return array[]
+   */
+  protected function createLivingDead(): array {
+    $living[] = $this->individualCreate();
+    $living[] = $this->individualCreate();
+    $living[] = $this->individualCreate();
+    $deceased[] = $this->ids['Contact']['dead1'] = $this->individualCreate(['is_deceased' => 1]);
+    $deceased[] = $this->ids['Contact']['dead2'] = $this->individualCreate(['is_deceased' => 1]);
+    $deceased[] = $this->ids['Contact']['dead3'] = $this->individualCreate(['is_deceased' => 1]);
+    return [$living, $deceased];
+  }
+
+  /**
+   * Creates two entities: a Group and a SavedSearch
+   *
+   * @param array $params
+   *
+   * @return CRM_Contact_BAO_Group
+   */
+  protected function createSmartGroup(array $params): CRM_Contact_BAO_Group {
+    $ssParams = ['form_values' => $params['formValues'], 'is_active' => 1];
+    $savedSearch = CRM_Contact_BAO_SavedSearch::create($ssParams);
+    $params['saved_search_id'] = $savedSearch->id;
+    return CRM_Contact_BAO_Group::create($params);
+  }
+
 }