Tuesday, January 6, 2015

CakePHP3 testing behavior - how do I mock a query?

I have the following test for my behavior in

tests\TestCase\Model\Behavior\SluggableBehaviorTest.php

<?php
namespace App\Test\TestCase\Model\Behavior;

use App\Model\Behavior\SluggableBehavior;
use Cake\TestSuite\TestCase;
//use ArrayObject;
use Cake\Core\Plugin;
use Cake\ORM\Entity;
use Cake\ORM\Table;
use PHPUnit_Framework_TestCase;

/**
 * App\Model\Behavior\SluggableBehavior Test Case
 */

class SluggableBehaviorTest extends TestCase
{

   
private $config = array();
   
   
/**
     * setUp method
     *
     * @return void
     */

   
public function setUp()
   
{
        parent
::setUp();
       
        $this
->config = [
           
'field' => 'title',
           
'slug' => 'slug',
           
'scope' => array(),
           
'separator' => '-',
           
'unique' => true,
           
'update' => false,
           
'trigger' => false
       
];

   
}

   
/**
     * tearDown method
     *
     * @return void
     */

   
public function tearDown()
   
{
        unset
($this->Sluggable);

        parent
::tearDown();
   
}

   
/**
     * Test initial setup
     *
     * @return void
     */

   
public function testInitialization()
   
{
        $this
->markTestIncomplete('Not implemented yet.');
   
}
   
   
/**
     * Data provider method for testing valid file uploads
     *
     * @return array
     */

   
public function slugEntityProvider()
   
{
       
return [
           
[
               
[],
               
['title' => 'Test', 'slug' => ''],
               
['title' => 'Test', 'slug' => 'test']
           
],
           
[
               
['field' => 'other_title', 'slug' => 'other_slug'],
               
['other_title' => 'Test Name', 'other_slug' => ''],
               
['other_title' => 'Test Name', 'other_slug' => 'test-name']
           
],
           
[
               
['separator' => '_'],
               
['title' => 'Test Name', 'slug' => ''],
               
['title' => 'Test Name', 'slug' => 'test_name']            
           
]            
       
];
       
   
}
   
/**
     * Test that correct field is slugged
     *
     * @dataProvider slugEntityProvider
     */
   
   
public function testSlug(array $configUpdates, array $entityData, array $expected)
   
{
        $config
= $this->config;
       
foreach($configUpdates as $key => $value) {
            $config
[$key] = $value;
       
}          
       
        $table
= $this->getMock('Cake\ORM\Table', null);
        $Sluggable
= $this->getMockBuilder('App\Model\Behavior\SluggableBehavior')
           
->setConstructorArgs([$table, $config])
           
->setMethods(['makeUniqueSlug'])
           
->getMock();
               
        $Sluggable
->expects($this->once())
           
->method('makeUniqueSlug')
           
->will($this->returnValue($expected[$config['slug']]));
           
        $entity
= new Entity($entityData);
       
        $Sluggable
->slug($entity);
       
        $this
->assertEquals($expected[$config['slug']], $entity[$config['slug']]);
   
}
   
   
/**
     * Data provider method for testing valid file uploads
     *
     * @return array
     */

   
public function beforeSaveEntityProvider()
   
{
       
return [
           
[
               
['update' => false],
               
['slug' => 1],
               
['title' => 'Test Name', 'slug' => ''],
               
['title' => 'Test Name', 'slug' => 'test-name']            
           
],
           
[
               
['update' => false],
               
['slug' => 0],
               
['id' => 1, 'title' => 'Test Name', 'slug' => 'test'],
               
['id' => 1, 'title' => 'Test Name', 'slug' => 'test']            
           
],
           
[
               
['update' => true],
               
['slug' => 1],
               
['id' => 1, 'title' => 'Test Name', 'slug' => 'test'],
               
['id' => 1, 'title' => 'Test Name', 'slug' => 'test-name']            
           
],            
           
[
               
['trigger' => false],
               
['slug' => 1],
               
['title' => 'Test Name', 'slug' => '',     'trigger' => false],
               
['title' => 'Test Name', 'slug' => 'test', 'trigger' => false]            
           
],
           
[
               
['trigger' => 'use_slug'],
               
['slug' => 0],
               
['title' => 'Test Name', 'slug' => '', 'use_slug' => false],
               
['title' => 'Test Name', 'slug' => '', 'use_slug' => false]            
           
]
       
];
   
}
   
   
/**
     * Test that field is only slugged when config specifies
     *
     * @dataProvider beforeSaveEntityProvider
     */
   
   
public function testBeforeSave(array $configUpdates, array $methodCalls, array $entityData, array $expected)
   
{
        $config
= $this->config;
       
foreach($configUpdates as $key => $value) {
            $config
[$key] = $value;
       
}          
       
        $table
= $this->getMock('Cake\ORM\Table', null);
        $Sluggable
= $this->getMockBuilder('App\Model\Behavior\SluggableBehavior')
           
->setConstructorArgs([$table, $config])
           
->setMethods(['slug'])
           
->getMock();
       
       
if ($methodCalls['slug'] == 1) {
            $Sluggable
->expects($this->once())
               
->method('slug')
               
->will($this->returnValue($expected[$config['slug']]));            
       
} elseif ($methodCalls['slug'] == 0) {
            $Sluggable
->expects($this->never())
               
->method('slug')
               
->will($this->returnValue($expected[$config['slug']]));              
       
}  
           
        $entity
= new Entity($entityData);
       
        $Sluggable
->beforeSave($this->getMock('Cake\Event\Event', null, ['beforeSave']), $entity);
   
}
   
   
}

The function I want to test in the behavior is
src\Model\Behavior\SluggableBehavior.php
    /**
     * Generates a unique slug for that table.
     *
     * @param \Cake\ORM\Entity $entity the entity that is going to be saved
     * @return $slug
     */
   
   
protected function _makeUniqueSlug(Entity $entity)
   
{
       
        $config
= $this->config();  
        $slug
=  Inflector::slug($entity->get($config['field']), $config['separator']);
       
       
if (!$config['unique']) {
           
return $slug;
       
}
       
        $results
= $this->_scope($this->_table->find())
           
->select(['id', $config['slug']])
           
->where([$config['slug'] => $slug])
           
->orWhere([$config['slug'] . ' REGEXP' => '^' . $slug . $config['separator'] . '[0-9]+'])
           
->all();
       
        $duplicates
= array();
       
foreach ($results as $result) {
            $duplicates
[] = $result->{$config['slug']};
       
}      
       
       
if (!empty($duplicates)) {
            $startSlug
= $slug;
            $index
= 1;

           
while ($index > 0) {
               
if (!in_array($startSlug . $config['separator'] . $index, $duplicates)) {
                    $slug
= $startSlug . $config['separator'] . $index;
                    $index
= -1;
               
}
                $index
++;
           
}
       
}
       
       
return $slug;    
   
}

   
/**
     * Alters the passed query so that it only returns scoped records as defined
     * in the tree configuration.
     *
     * @param \Cake\ORM\Query $query the Query to modify
     * @return \Cake\ORM\Query
     */

   
protected function _scope($query)
   
{
        $config
= $this->config();

       
if (is_array($config['scope'])) {
           
return $query->where($config['scope']);
       
} elseif (is_callable($config['scope'])) {
           
return $config['scope']($query);
       
}

       
return $query;
   
}
 

How do I mock the query to check for duplicates, or attach a fixture to a mock table class?

Thanks.


--
Like Us on FaceBook https://www.facebook.com/CakePHP
Find us on Twitter http://twitter.com/CakePHP

---
You received this message because you are subscribed to the Google Groups "CakePHP" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cake-php+unsubscribe@googlegroups.com.
To post to this group, send email to cake-php@googlegroups.com.
Visit this group at http://groups.google.com/group/cake-php.
For more options, visit https://groups.google.com/d/optout.

No comments: