Cakephp 3的PHPUnit插入测试在测试之前失败

时间:2018-07-30 16:57:38

标签: cakephp phpunit cakephp-3.0

我有三个记录的UsersFixture。 测试方法first()second()都在guest_can_login()之前,pr分别显示“ Joe”,“ Joe”。但是使用测试方法third(),它在之后 guest_can_login()之后,我得到了通知错误:尝试获取非对象的属性。

因此,由于某种原因,guest_can_login()中的某些内容破坏了其余的测试方法。我也尝试过复制guest_can_login()。

我认为这很奇怪,因为tearDown应该在每次测试后“重置”所有内容。我没主意了。在阅读Cakephp测试文档之后,我无法解决它。

任何帮助我解决此问题的建议都将受到赞赏。

以下代码(如果需要,请根据以下原则:https://gist.github.com/chris-andre/2eb3ad053073caf4f1c81722428a900b):

public $fixtures = [
    'app.users',
    'app.tenants',
    'app.roles',
    'app.roles_users',
];

public $Users;

public function setUp()
{
    parent::setUp();
    $config = TableRegistry::getTableLocator()->exists('Users') ? [] : ['className' => UsersTable::class];
    $this->Users = TableRegistry::getTableLocator()->get('Users', $config);
}

/**
 * tearDown method
 *
 * @return void
 */
public function tearDown()
{
    unset($this->Users);
    TableRegistry::clear();
    parent::tearDown();
}

/** @test */
public function guest_can_register()
{
    $this->enableCsrfToken();
    $this->enableSecurityToken();

    $this->configRequest([
        'headers' => [
            'host' => 'timbas.test'
        ]
    ]);

    $data = [
        'email' => 'chris@andre.com',
        'first_name' => 'Christian',
        'last_name' => 'Andreassen',
        'password' => '123456',
        'tenant' => ['name' => 'Test Company AS', 'domain' => 'testcomp', 'active' => true],
        'active' => true
    ];

    $this->post('/register', $data);

    $this->assertResponseSuccess();
    $this->assertRedirect(['controller' => 'Users', 'action' => 'login', '_host' => 'testcomp.timbas.test']);

    $user = $this->Users->find()
        ->contain(['Tenants', 'Roles'])
        ->where(['Users.email' => 'chris@andre.com'])
        ->first();


}

/** @test */
public function first()
{
    $users = $this->Users->find()->first();

    pr($users->first_name);
}

/** @test */
public function second()
{
    $users = $this->Users->find()->first();

    pr($users->first_name);
}

/** @test */
public function guest_can_login()
{
    $this->enableCsrfToken();
    $this->enableSecurityToken();

    $this->configRequest([
        'headers' => [
            'host' => 'testcomp.timbas.test'
        ]
    ]);

    $data = [
        'email' => 'chris@andre.com',
        'first_name' => 'Christian',
        'last_name' => 'Andreassen',
        'password' => '123456',
        'tenant' => ['name' => 'Test Company AS', 'domain' => 'testcomp', 'active' => true],
        'active' => true,
        'roles' => ['_ids' => [ADMINISTRATOR_ROLE_ID]]
    ];

    $user = $this->Users->newEntity($data, [
        'associated' => ['Tenants', 'Roles']
    ]);

    $this->Users->save($user);

    $getNewUser = $this->Users->find()
        ->contain(['Roles'])
        ->where(['Users.email' => 'chris@andre.com'])
        ->first()
        ->toArray();

    // pr($getNewUser->id);

    $this->post('/users/login', [
        'email' => 'chris@andre.com',
        'password' => '123456'
    ]);

    $this->assertSession($getNewUser, 'Auth.User');
}

/** @test */
public function third()
{
    $users = $this->Users->find()->first();

    pr($users->first_name);
}

编辑2018-08-06: Users :: register()是全局上下文,无法使用子域从url访问。例如。 tenant1.domain.com/register将引发badRequest,而domain.com/register是有效的url。注册成功后,将转发用户从正确的URL登录。登录网址= Tenants.domain +域+后缀,例如tenant1.domain.com。当用户位于承租人范围(带有子域的URL)上时,将在所有查询中将附加行为的模型中的Tenants.id其中Tenants.domain = tenant1添加到where子句中。

现在,在third()中发生的是,在查询中添加了来自guest_can_login()中新创建的Tenant的tenant_id,这意味着当{{1 }}运行。那就是问题所在。

另一个问题是,在third()之外的所有测试方法上都调用了setUp()。每个测试方法都会调用testDown()

App \ Middleware \ TenantMiddleware.php:

use InstanceConfigTrait;

/**
 * Default config.
 * Options:
 * - globalScope: tells the middleware what controller and action tenant scope is not being used
 * Example
 * 'globalScope' => [
 *      'Pages' => ['*'], // All actions in PagesController is global
 *      'Users' => ['register'] // Register action in UsersController is global
 * ]
 * @var array
 */
protected $_defaultConfig = [
    'globalScope' => [
        'Users' => ['register'],
        'Landing' => ['*'],
        'Pages' => ['*']
    ],
];

public function __construct($config = [])
{
    if (!isset($config['primaryDomain'])) {
        $config['primaryDomain'] = Configure::read('Site.domain');
    }

    $this->setConfig($config);
}

/**
 * Invoke method.
 *
 * @param \Cake\Http\ServerRequest $request The request.
 * @param \Psr\Http\Message\ResponseInterface $response The response.
 * @param callable $next Callback to invoke the next middleware.
 * @return \Psr\Http\Message\ResponseInterface A response
 */
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next)
{
    // Get subdomains
    $subdomains = $request->subdomains();
    // If subdomains not empty, the first is always the tenants domain
    $subdomain = !empty($subdomains) ? $subdomains[0] : '';
    Tenant::setDomain($subdomain);

    // Get params of current request
    $params = $request->getAttribute('params');
    $controller = $params['controller'];
    $action = $params['action'];

    // Set tenantScope as default
    Tenant::setScope('tenant');
    $globalScope = $this->getConfig('globalScope');
    // If Controller and action is a global scope
    if (array_key_exists($controller, $globalScope)) {
        if (in_array($action, $globalScope[$controller]) || in_array('*', $globalScope[$controller])) {
            Tenant::setScope('global');
        }
    }

    if (
        (Tenant::getScope() === 'tenant' && Tenant::tenant() === null)
        || (Tenant::getDomain() === '' && Tenant::getScope() === 'tenant')
        || (Tenant::getDomain() !== '' && Tenant::getScope() === 'global')
    ) {
        throw new NotFoundException('The page you are looking for does not exists.');
    }

    $primaryDomain = $this->getConfig('primaryDomain');
    if (array_key_exists($controller, $globalScope)) {
        if (in_array($action, $globalScope[$controller]) && Tenant::getScope() === 'global') {
        }
    }

    return $next($request, $response);
}

App \ Model \ Behavior \ TenantScopeBehavior.php:

protected $_table;

/**
 * Default configuration.
 *
 * @var array
 */
protected $_defaultConfig = [];

public function __construct(Table $table, array $config = [])
{
    parent::__construct($table, $config);
}

public function beforeFind(Event $event, Query $query, ArrayObject $options)
{
    $model = $this->_table->getAlias();
    $foreig_key = 'tenant_id';
    if (!isset($options['skipTenantCheck']) || $options['skipTenantCheck'] !== true) {
        if (Tenant::getScope() === 'tenant') {
            if ($model === 'Tenants') {
                $query->where(['Tenants.id' => Tenant::tenant()->id]);
            } else {
                $query->where([$model . '.' . $foreig_key => Tenant::tenant()->id]);
            }
        }
    }

    return $query;
}

public function beforeSave(Event $event, Entity $entity, $options)
{
    if (Tenant::getScope() === 'tenant') {
        if ($entity->isNew()) {
            $entity->tenant_id = Tenant::tenant()->id;
        } else {
            // Check if current tenant is owner
            if ($this->_table->getAlias() === 'Tenants') {
                if ($entity->id != Tenant::tenant()->id) {
                    throw new BadRequestException();
                }
            } else {
                if ($entity->tenant_id != Tenant::tenant()->id) {
                    throw new BadRequestException();
                }
            }
        }
    }

    return true;
}

public function beforeDelete(Event $event, Entity $entity, $options)
{
    if (Tenant::getScope() === 'tenant') {
        if ($entity->tenant_id != Tenant::tenant()->id) { //current tenant is NOT owner
            throw new BadRequestException();
        }
    }

    return true;
}

App \ Tenant \ Tenant.php:

/**
 * $_domain will be empty or comtain the domain (subdomain from url)
 * @var string can be empty
 */
protected static $_domain;

/**
 * $_scope shall be 'global' or 'tenant'.
 * @var string 
 */
protected static $_scope;

/**
 * @var null|object \App\Model\Entity\Tenant
 */
protected static $_tenant;

/**
 * Gets domain from $_domain and returns the string
 * @return string
 */
public static function getDomain()
{
    return self::$_domain;
}

/**
 * Set the tenant scope domain. Will be set in the TenantMiddleware, and shall not be set anywhere else
 * @param string $domain 
 * @return string empty or with domain
 */
public static function setDomain($domain)
{
    self::$_domain = $domain;
}

/**
 * Tenant method
 * Return the object \App\Model\Table\Tenants ro null
 * @return type
 */
public static function tenant()
{
    $tenant = static::_getTenant();

    return $tenant;
}

protected static function _getTenant()
{
    if (self::$_tenant === null) {
        $cachedTenants = Cache::read('tenants');
        if($cachedTenants !== false) {
            // do something
        }

        $tenantsTable = TableRegistry::get('Tenants');
        $tenant = $tenantsTable->find('all', ['skipTenantCheck' => true])
            ->where(['Tenants.domain' => self::getDomain()])
            ->where(['Tenants.active' => true])
            ->first();

        self::$_tenant = $tenant;
    }

    return self::$_tenant;
}

public static function getScope()
{
    return self::$_scope;
}

/**
 * Description
 * @param type $scope 
 * @return type
 */
public static function setScope($scope)
{
    self::$_scope = $scope;
}

0 个答案:

没有答案