The main point when implementing this is to make sure that methods with a return type of "object" return something that can be fed to matching() and all constraint-generating methods (like logicalAnd(), equals(), like(), ...). This allows for code like $query->matching($query->equals('foo', 'bar'))->setLimit(10)->execute();
 /**
  * @test
  */
 public function getFirstReturnsNullIfResultSetIsEmptyAndQueryIsNotInitialized()
 {
     $initializedQueryResult = [];
     $queryResult = $this->getAccessibleMock(QueryResult::class, ['dummy'], [$this->query]);
     $this->query->expects($this->once())->method('setLimit')->with(1);
     $queryResult->injectPersistenceManager($this->persistenceManager);
     $mockDataMapper = $this->createMock(DataMapper::class);
     $mockDataMapper->expects($this->once())->method('mapToObjects')->with(['one', 'two'])->will($this->returnValue($initializedQueryResult));
     $queryResult->injectDataMapper($mockDataMapper);
     $this->assertNull($queryResult->getFirst());
 }
 /**
  * @param QueryInterface $query
  * @param $nodeTypeFilter
  * @return array
  */
 protected function getNodeTypeFilterConstraints(QueryInterface $query, $nodeTypeFilter)
 {
     $includeNodeTypeConstraints = [];
     $excludeNodeTypeConstraints = [];
     $nodeTypeFilterParts = Arrays::trimExplode(',', $nodeTypeFilter);
     foreach ($nodeTypeFilterParts as $nodeTypeFilterPart) {
         $nodeTypeFilterPart = trim($nodeTypeFilterPart);
         if (strpos($nodeTypeFilterPart, '!') === 0) {
             $negate = true;
             $nodeTypeFilterPart = substr($nodeTypeFilterPart, 1);
         } else {
             $negate = false;
         }
         $nodeTypeFilterPartSubTypes = array_merge([$nodeTypeFilterPart], $this->nodeTypeManager->getSubNodeTypes($nodeTypeFilterPart, false));
         foreach ($nodeTypeFilterPartSubTypes as $nodeTypeFilterPartSubType) {
             if ($negate === true) {
                 $excludeNodeTypeConstraints[] = $query->logicalNot($query->equals('nodeType', $nodeTypeFilterPartSubType));
             } else {
                 $includeNodeTypeConstraints[] = $query->equals('nodeType', $nodeTypeFilterPartSubType);
             }
         }
     }
     $constraints = $excludeNodeTypeConstraints;
     if (count($includeNodeTypeConstraints) > 0) {
         $constraints[] = $query->logicalOr($includeNodeTypeConstraints);
     }
     return $constraints;
 }