Propriété Cwebuser.myproperty Indéfinie Dans Les Tests Fonctionnels

Bonjour,

Je me forme au framework Yii et je suis parvenu à connecter/déconnecter un utilisateur. Lorsque l’utilisateur se connecte, son pseudo s’affiche sur la page. Jusque-là pas de problème. En revanche, dans les tests fonctionnels (avec Sélénium), le test $this->assertTextPresent( Yii::app()->user->pseudo, ‘mon_pseudo’ ) plante systématiquement avec le message “La propriété « CWebUser.pseudo » est indéfinie.”.

Je ne comprends pas pourquoi, alors que le test $this->assertTrue( Yii::app()->user->isGuest passe bien (le login s’est bien passé) et que l’affichage du pseudo sur la page se passe normalement (visuellement) pendant l’exécution de Sélénium (donc que la propriété “pseudo” existe bien dans UserIdentity).

Ci-dessous, mon code source, si vous voulez bien m’aider à comprendre.

Merci d’avance

La classe UserIdentity :


class UserIdentity extends CUserIdentity {

  private $_id; // id de l'utilisateur dans la table Account


  /**

   * Authenticates a user.

   * @return boolean whether authentication succeeds.

   */

  public function authenticate() {

    $record = Account::model()->findByAttributes( array( 'pseudo' => $this->username ) );


    if( $record === null ) {

      $this->errorCode = self::ERROR_USERNAME_INVALID;

    }

    else if( $record->password !== crypt( $this->password, $record->password ) ) {

      $this->errorCode = self::ERROR_PASSWORD_INVALID;

    }

    else {

      $this->_id = $record->id;

      $this->setState( 'pseudo', $this->username );

      $this->errorCode = self::ERROR_NONE;

    }


    return ! $this->errorCode;

  }


  public function getId() {

    return $this->_id;

  }

}

Le modèle :


class LoginForm extends CFormModel

{

  public $username;

  public $password;

  public $rememberMe;


  private $_identity;


  /**

   * Declares the validation rules.

   * The rules state that username and password are required,

   * and password needs to be authenticated.

   */

  public function rules() {

    return array(

      // username and password are required

      array( 'username, password', 'required' ),

      // rememberMe needs to be a boolean

      array( 'rememberMe', 'boolean' ),

      // password needs to be authenticated

      array( 'password', 'authenticate' ),

    );

  }


  /**

   * Declares attribute labels.

   */

  public function attributeLabels() {

    return array(

      'username' => 'Pseudo',

      'password' => 'Mot de passe',

      'rememberMe' => 'Mémoriser ces informations',

    );

  }


  /**

   * Authenticates the password.

   * This is the 'authenticate' validator as declared in rules().

   */

  public function authenticate( $attribute, $params ) {

    if( ! $this->hasErrors() ) {

      $this->_identity = new UserIdentity( $this->username, $this->password );

      if( ! $this->_identity->authenticate() )

        $this->addError( 'password', 'Incorrect username or password.' );

    }

  }


  /**

   * Logs in the user using the given username and password in the model.

   * @return boolean whether login is successful

   */

  public function login() {

    if( $this->_identity === null ) {

      $this->_identity = new UserIdentity( $this->username, $this->password );

      $this->_identity->authenticate();

    }


    if( $this->_identity->errorCode === UserIdentity::ERROR_NONE ) {

      $duration = $this->rememberMe ? 3600*24*30 : 0; // 30 days

      Yii::app()->user->login( $this->_identity, $duration );

      return true;

    }

    else

      return false;

  }

}



La fixture :


return array(


    'first_user' => array(

        'pseudo' => 'first_pseudo',

        'password' => crypt( 'first_password' ),

    ),


    'my_user' => array(

        'pseudo' => 'mon_pseudo',

        'password' => crypt( 'mon_password' ),

    ),


);

Les tests fonctionnels




class SiteTest extends WebTestCase {

  public $fixtures = array(

    'accounts' => 'Account',

  );


  public function testLoginLogout() {


    $this->open( '' );


    $this->click( 'id=login_link' );

    $this->waitForVisible( 'id=login_popup' );

    $this->type( 'id=LoginForm_username', 'mon_pseudo' );

    $this->type( 'id=LoginForm_password', 'mon_password' );


    $this->click( 'id=login_button_ok' );

    $this->waitForVisible( 'id=logout_link' );

    $this->assertTrue( Yii::app()->user->isGuest, "Utilisateur connecté" ); // => OK

    $this->assertTextPresent( $this->accounts['my_user']['pseudo'], 'mon_pseudo' ); // => OK

    $this->assertTextPresent( Yii::app()->user->pseudo, 'mon_pseudo' ); // => NOK "Propriété pseudo indéfinie"

  }


}



Salut !

Alors là, il y a 2 choses qu’il ne faut pas confondre : lors de tes tests fonctionnels, Selenium exécute ton test, à savoir qu’il effectue les actions précisées dans le test (il simule un utilisateur qui tape dans les champs, qui valide, qui vérifie les éléments de ta page). Suivant la configuration que tu as réalisée, tu vas appeler ou non le framework Yii (dans ton cas tu utilises le framework car ta classe de test hérite de WebTestCase, te permettant ainsi de profiter entre autres des fixtures). Et d’un autre côté, tu as ton application web à proprement parler (celle que tu peux voir dans le navigateur) qui elle aussi utilise le framework.

Tout ça pour dire que tu peux pas appeler “Yii::app()->user->pseudo” dans ton test car cette instance de Yii ne connaît pas ton identité. C’est celle de l’application qui la connaît. Bref, ici, la seule chose que tu peux vérifier dans ton test, c’est que le pseudo que tu as saisi juste avant est bien affiché sur la page.

J’espère avoir été clair car j’avoue que ce n’est pas très simple à expliquer.

Super, merci, d’ailleurs l’assertion


$this->assertTrue( Yii::app()->user->isGuest )

est justement vérifiée (l’utilisateur n’est pas connecté, contrairement à ce que j’avais indiqué en commentaire) ! Tout s’explique. Encore merci.

Ha, merci pour cette explication ! J’étais tombé sur le même problème il y a quelque temps, problème un peu bloquant car certaines fonctions de mon application ont besoin du webUser sinon elles renvoient des résultats faussés.

Du coup, pour débloquer les tests fonctionnels, j’ai forcé l’identification au moment d la connexion mais faire ça sans savoir d’où venait le problème ne me satisfaisait qu’à moitié - il y a toujours le risque de cacher un vrai bug avec ce genre de manoeuvres…

Me voici donc rassuré ! :)