【问题标题】:CakePHP PHPUnit Fixtures Drop Database Table Every TimeCakePHP PHPUnit Fixtures 每次都会删除数据库表
【发布时间】:2014-02-07 12:33:46
【问题描述】:

我正在尝试对我的模型运行一个简单的单元测试。问题是每次我运行测试时,我的数据库表都会被删除。我有 public $dropTables = false;任何人都可以弄清楚为什么 Mercer_rejects 表仍然被删除?正如您将看到的,我在我的夹具中尝试了许多不同的方法。

我想我将不得不单步执行代码并找出表格何时被删除。

这是我的灯具 MovieStarFixture.php 的代码:

class MovieStarFixture extends CakeTestFixture {
//  NEW TRY from http://stackoverflow.com/a/2548908/55124
var $name = 'MovieStar';
var $fields = array(
    'id'       => array(
                      'type'=>'string',
                      'null' => false,
                      'default' => NULL,
                      'length' => 36,
                      'key' => 'primary'),

    'movie_id' => array(
                      'type'=>'string',
                      'null' => false,
                      'default' => NULL,
                      'length' => 36),

    'trace' => array('type'=>'string', 'null' => false, 'default' => NULL),
    'star_date' => array(
                       'type'=>'datetime',
                       'null' => false,
                       'default' => NULL),
    'movie_star_type_id' => array(
                       'type'=>'string',
                       'null' => false,
                       'default' => NULL,
                       'length' => 36),
    'code' => array('type'=>'text', 'null' => false, 'default' => NULL),
    'amount' => array('type'=>'float', 'null' => false, 'default' => 0),
    'movie_star_recurrance_id' => array(
                       'type'=>'string',
                       'null' => false,
                       'default' => NULL, 
                       'length' => 36),
    'open' => array('type'=>'boolean', 'null' => false, 'default' => '1'),
    'loss_axia' => array('type'=>'float', 'null' => true, 'default' => 0),
    'loss_mgr1' => array('type'=>'float', 'null' => true, 'default' => 0),
    'loss_mgr2' => array('type'=>'float', 'null' => true, 'default' => 0),
    'loss_rep' => array('type'=>'float', 'null' => true, 'default' => 0)
    );
var $records = array(
                array(
                'id' => '52ab9259-0070-4583-8d6f-4ac6c0a81cd7',
                'movie_id' => '440b7d13-5618-4560-be1d-93c5a2900a5e',
                'trace' => '3331313133423',
                'star_date' => '2013-12-13',
                'movie_star_type_id' => '64f7c386-6725-4c62-83ac-ae309bec8b10',
                 'code' => 'C01',
                'amount' => '222.0000',
                'movie_star_recurrance_id' => '',
                'open' => true,
                'loss_axia' => '23.0000',
                'loss_mgr1' => '0',
                'loss_mgr2' => '0',
                'loss_rep' => '0'
));

     //  THESE ARE ALL OF THE OTHER METHODS I HAVE TRIED
// Loading Fixture Methods / / / / / / / / / / / / / / / / / / / / / / / /  

// #1 - Import model and records / / / / / / / / / / / / / / / / / / / / /  
//public $import = array('model' => 'MovieStar', 'records' => true);


// #2 - Use onlt table info - no model / / / / / / / / / / / / / / / / / /  
// public $import = array('table' => 'movie_stars', 'records' => true);


// #3 - Specify Model and Create Records - Binds Data to Database/ / / / / 
/*
    public $records = array(
                 array(
        'MovieStar' => array(
        'id' => '52ab917d-549c-493b-9ef5-54a1c0a81cd7',
        'movie_id' => '440b7d13-5618-4560-be1d-93c5a2900a5e',
        'trace' => '3331313133',
        'star_date' => '2013-12-13',
        'movie_star_type_id' => '64f7c386-6725-4c62-83ac-ae309bec8b10',
        'code' => 'C01',
        'amount' => '122.0000',
        'movie_star_recurrance_id' => '',
        'open' => true,
        'loss_axia' => null,
        'loss_mgr1' => null,
        'loss_mgr2' => null,
        'loss_rep' => null
        )
    )
    );
public $import = array('model' => 'MovieStar', 'records' => false);
    */  

// #4 - Specify Model and Create Records in Init / / / / / / / / / / / / / 
// public $import = 'MovieStar';

/* public function init() {
        $records = array(
        array(
            'MovieStar' => array(
            'id' => '52ab917d-549c-493b-9ef5-54a1c0a81cd7',
            'movie_id' => '440b7d13-5618-4560-be1d-93c5a2900a5e',
            'trace' => '3331313133',
            'star_date' => '2013-12-13',
            'movie_star_type_id' => '64f7c386-6725-4c62-83ac-ae309bec8b10',
            'code' => 'C01',
            'amount' => '122.0000',
            'movie_star_recurrance_id' => '523525',
            'open' => true,
            'loss_axia' => null,
            'loss_mgr1' => null,
            'loss_mgr2' => null,
            'loss_rep' => null
              )
        )
        );
        parent::init();
}*/

// #5 - Try Model Setup / / / / / / / / / / / / / / / / / / / / / / / / 
// This drops all records after the first test
/*public $records = array(
     array(
        'MovieStar' => array(
        'id' => '52ab917d-549c-493b-9ef5-54a1c0a81cd7',
        'movie_id' => '440b7d13-5618-4560-be1d-93c5a2900a5e',
        'trace' => '3331313133',
        'star_date' => '2013-12-13',
        'movie_star_type_id' => '64f7c386-6725-4c62-83ac-ae309bec8b10',
        'code' => 'C01',
        'amount' => '122.0000',
        'movie_star_recurrance_id' => '',
        'open' => true,
        'loss_axia' => null,
        'loss_mgr1' => null,
        'loss_mgr2' => null,
        'loss_rep' => null
    )
       )
    );

   }*/

这是我的 MovieStarTest.php:

<?php
App::uses('Controller', 'Controller');
App::uses('View', 'View');
App::uses('MovieStar', 'Model');

/**
 * MovieStar Test Case
 *
 */
class MovieStarTest extends CakeTestCase {

/**
 * Fixtures
 *
 * @var array
 */
public $fixtures = array(
    'app.movie_star'//,
    //'app.movie_star_recurrance',
    //'app.movie_star_type',
    //'app.movie',
    //'app.user',
    //'app.movie_star_line',
    //'app.movie_star_status'
);

public $autoFixtures = false;
public $dropTables = false; 

/**
 * setUp method
 *
 * @return void
 */
public function setUp() {
    parent::setUp();

    $this->MovieStar =& ClassRegistry::init('MovieStar');
    $this->MovieStar->useDbConfig = 'test';

    //$this->MovieStar->query("SELECT truncate_tables('axia')");

    // load data
    $this->loadFixtures('MovieStar');
}

/**
 * tearDown method
 *
 * @return void
 */
public function testFixtures() {
    $numberOfResults = $this->MovieStar->find('count');
    debug($numberOfResults);
    $resultGreaterThanMinimumValue = $numberOfResults > 2;
    $this->assertTrue($resultGreaterThanMinimumValue);
}

public function testFixtures2() {
    $numberOfResults = $this->MovieStar->find('count');
    debug('$numberOfResults');
    debug($numberOfResults);
    $resultIsZero = $numberOfResults == 0;
    $this->assertTrue($resultIsZero);
}

public function testFindStarsByMovieId() {
    $movieId = '440b7d13-5618-4560-be1d-93c5a2900a5e';
    $result = $this->MovieStar->findStarsByMovieId($movieId);
    $expected = array(
        array(
'MovieStar' => array(
    'id' => '52ab9259-0070-4583-8d6f-4ac6c0a81cd7',
    'movie_id' => '440b7d13-5618-4560-be1d-93c5a2900a5e',
    'trace' => '3331313133423',
    'star_date' => '2013-12-13',
    'movie_star_type_id' => '64f7c386-6725-4c62-83ac-ae309bec8b10',
    'code' => 'C01',
    'amount' => '222.0000',
    'movie_star_recurrance_id' => '',
    'open' => true,
    'loss_axia' => '23.0000',
    'loss_mgr1' => null,
    'loss_mgr2' => null,
    'loss_rep' => null
        )
    )
    );

    debug("Expected");
    debug($expected);
    debug("Result");
    debug($result);

    $this->assertEquals($expected, $result);
}

public function tearDown() {
    //$this->MovieStar->deleteAll(true, true);
    //unset($this->MovieStar);

    parent::tearDown();
}

}

我应该不能通过添加来删除表格

 public $dropTables = false; 

但我在 lib/Cake/TestSuite/Fixture/CakeFixtureManager.php 中没有看到在此方法之前检查的 dropTables 的值

实际上这会截断整个数据库。我的桌子掉到哪里去了?

【问题讨论】:

    标签: database cakephp phpunit fixtures


    【解决方案1】:

    看起来 lib/Cake/TestSuite/Fixture/CakeFixtureManager.php 有一个关闭数据库的函数。我很想知道在到达这个函数之前应该在代码中的哪个位置检查 $dropTables 的值。

    【讨论】:

    【解决方案2】:

    CakePHP 在测试时创建和删除所有表是默认行为。

    为了为每个测试(重新)创建它们,您有以下选择:

    1. 在 Fixture 中创建架构和记录。
    2. 从数据库中导入架构并在 Fixture 中创建记录。
    3. 从数据库中导入架构和记录。

    我使用选项号。 2 因为我有太多数据需要 CakePHP 为每个测试导入(和删除)。

    public $import = array('model' => 'YourModel', 'records' => false, 'connection' => 'default');
    

    我指定了“默认”连接,以便 CakePHP 使用我的默认连接来导入模式信息。我认为问题在于您的 $import 变量正在尝试从测试连接导入表。这些将在第一次运行后停止存在,当它们被删除时。

    【讨论】:

      【解决方案3】:

      接受的答案不能解决问题:

      我有 public $dropTables = false;谁能弄清楚为什么 Mercer_rejects 表还在被删除吗?

      来自 cakephp 文档:

      $dropTables

      在每个测试方法上创建/删除控制表。

      将此设置为 false 以避免表已存在时被删除 在每个测试方法之间。表仍将在结束时被删除 每个测试运行程序执行。

      注意这部分:

      在每个测试运行程序执行结束时仍会删除表

      因此,即使您public $dropTables = false; 绑定到夹具的表在每次测试运行后仍然会被丢弃。接受的答案不会阻止这一点。

      为防止这种情况发生,请执行以下操作...

      像这样创建你的夹具:

      class MovieStarFixture extends CakeTestFixture {
      
          public $import = 'MovieStar';
      
          //data will be loaded into this fixture by the test cases
      
          // do not truncate movie_stars table between tests
          public function truncate($db){
      
              return null;
          }
      
          // do not drop movie_stars table between tests
          public function drop($db){
      
              return null;
          }
      }
      

      这可以防止夹具在每个测试方法后截断和丢弃。这是通过使夹具“假装”它在测试之间丢弃并截断表格来完成的。然而,这意味着夹具会认为该表已被删除并尝试在每个测试方法运行开始时重新创建它,这将导致错误(有关创建现有表的警告),因此您还必须...

      这样做:

      在 MovieStarTest 中添加 public $dropTables = false;。 这将防止夹具尝试删除表,如果它 已经存在。由于一开始没有删除表 在测试中,fixture 不会尝试创建表。这是 我们想要什么,因为表已经存在。

      现在您的数据将在 CakeTestCase::test*() 方法调用之间持久化。

      【讨论】:

      • 我认为我的回答确实解决了这个问题。任何单元/集成测试的关键部分是隔离。如果您在测试中需要数据,您应该遵循我概述的三个选项之一。您的回答可能会使您的测试运行得更快,但它牺牲了集成测试的意义:您的测试不再孤立地运行。
      • 你好 Hal9k。是的,单元/集成测试的一个关键部分是隔离。然而,在许多用例中,为了正确 / 彻底测试组件,需要在几个测试方法之间保持数据持久性。例如测试 OIDC 实现的一个组件。你回答了为什么他有他的问题。因此,您的回答实际上并不能解决他的问题。我回答了如何避免他遇到的问题。这就是我们的反应之间的客观差异。我很高兴人们似乎发现我的回答很有用。我的回答对测试速度没有影响。
      • 我的回答允许测试之间的数据持久性,这有时是正确测试组件的要求。在现实世界中就是这样。我的答案所获得的任何速度提升都无关紧要。答案的重点是解决提出问题的问题,而不是揭示问题的原因。干杯。
      • 要以您在现实世界中描述的方式正确/彻底地测试组件,我会使用 Selenium 或类似的,并将其称为系统测试。对于集成测试,可以并且应该模拟任何外部要求(OAuth 等)。原来的问题只是想创建一个集成测试,需要隔离。
      • 原问题想知道如何在测试方法之间持久化数据。我告诉他怎么做。你没有。这就是我所说的。您正在解释为什么不这样做,我想这很好。
      【解决方案4】:

      基于 Xavier 的回答,您可以通过执行以下操作确保表只填充一次,而无需在测试级别设置 dropTables = false:

      class MovieStarFixture extends CakeTestFixture
      {
      
        public $import = ['model' => 'MovieStar', 'connection' => 'default', 'records' => true];
      
        private $created_flag = false;
      
        public function create($db)
        {
          $success = null;
          if (!$this->created_flag) {
            $success = parent::create($db);
          }
          return $success;
        }
      
        public function truncate($db)
        {
          $success = null;
          if (!$this->created_flag) {
            $success = parent::truncate($db);
          }
          return $success;
        }
      
        public function insert($db)
        {
          $success = null;
          if (!$this->created_flag) {
            $success = parent::insert($db);
            $this->created_flag = true;
          }
          return $success;
        }
      
        public function drop($db)
        {
          $success = null;
          if (!$this->created_flag) {
            $success = parent::drop($db);
          }
        }
      }
      

      始终需要考虑隔离和速度之间的权衡,但如果您有一个在整个测试生命周期内都不会更改的大表,那么这可以大大提升性能。

      【讨论】:

        猜你喜欢
        • 2013-07-23
        • 1970-01-01
        • 1970-01-01
        • 2022-01-13
        • 1970-01-01
        • 2017-12-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多