This post covers my notes on Intro and Tests (chapters 1-3) in Yii from the book "Web Application Development with Yii and PHP" by Jeffrey Winesett about learning Yii by taking a step-by-step approach to building a Web-based project task tracking system from conception through production deployment - software development life cycle (SDLC) issue-management application.

  • philosophy
  • DRY & MVC
  • Unit tests
  • Functional test

Yii also embraces a convention over configuration philosophy, which contributes to its ease of use. This means that Yii has sensible defaults for almost all the aspects that are used for configuring your application. Following the prescribed conventions, you can write less code and spend less time developing your application. However, Yii does not force your hand. It allows you to customize all of its defaults and makes it easy to override all of these conventions.

Yii is also designed to help you with DRY development. DRY stands for Don't Repeat Yourself, a key concept of agile application development. All Yii applications are built using the Model-View-Controller (MVC) architecture. Yii enforces this development pattern by providing a place to keep each piece of your MVC code. This minimizes duplication and helps promote code reuse and ease of maintainability. The less code you need to write, the less time it takes to get your application to market. The easier it is to maintain your application, the longer it will stay on the market.

Typically in an MVC architecture, the model is responsible for maintaining the state, and should encapsulate the business rules that apply to the data that defines this state. A model in Yii is any instance of the framework class CModel or its child class. A model class is typically comprised of data attributes that can have separate labels (something user friendly for the purpose of display), and can be validated against a set of rules defined in the model. The data that makes up the attributes in the model class could come from a row of a database table or from the fields in a user input form. Yii implements two kinds of models, namely the form model (a CFormModel class) and active record (a CActiveRecord class). The class CFormModel represents a data model that collects HTML form inputs. It encapsulates all the logic for form field validation, and any other business logic that may need to be applied to the form field data. Active Record (AR) is a design pattern used to abstract database access in an objectoriented fashion. Each AR object in Yii is an instance of CActiveRecord or its child class, which wraps a single row in a database table or view, that encapsulates all the logic and details around database access, and houses much of the business logic that is required to be applied to that data.

Typically the view is responsible for rendering the user interface, often based on the data in the model. A view in Yii is a PHP script that contains user interface-related elements, often built using HTML, but can also contain PHP statements.

The controller is our main director of a routed request, and is responsible for taking user input, interacting with the model, and instructing the view to update and display appropriately.

Web applications need the data that is held in the persistent database storage to be mapped to in-memory class properties that define the domain objects. Object-relational mapping (ORM) libraries provide this mapping of database tables to domain object classes. Much of the code that deals with ORM is about describing how fields in the database correspond to properties in our in-memory objects, and is tedious and repetitive to write. Luckily, Yii comes to the rescue and saves us from this repetition and tedium by providing an ORM layer in the form of the Active Record (AR) pattern. Yii provides great support for database programming. Yii's Data Access Objects (DAO) are built on top of the PHP Data Objects (PDO) extension (http://php.net/pdo). This is a database abstraction layer that enables the application to interact with the database through a database-independent interface. All the supported database management systems (DBMS) are encapsulated behind a single uniform interface. In this way, the code can remain database independent and the applications developed using Yii DAO can easily be switched to use a different DBMS without the need for modification.

Unit tests are the tests that focus on the smallest units within a software application. In an object-oriented application, such as a Yii web application, the smallest units are the public methods that make up the interfaces to the classes. Unit tests should focus on one single class and not require other classes or objects to run it. Their purpose is to validate that a single unit of code is working as expected.

Functional tests focus on testing the end-to-end feature functionality of the application. These tests exist at a higher level than the unit tests and typically require multiple classes or objects to run. Their purpose is to validate that a given feature of the application is working as expected.

As of version 1.1, Yii is tightly integrated with the PHPUnit (http://www.phpunit. de/) and Selenium Remote Control (http://seleniumhq.org/projects/remotecontrol/) testing frameworks.

When used the yiic webapp console command to create new web application, files relevant to writing and executing automated tests are the following: trackstar/ Contains all the files listed in the file/directory column protected/ Protected application files tests/ Tests for the application fixtures/ Database fixtures functional/ Functional tests unit/ Unit tests report/ Coverage reports bootstrap.php The script executed at the very beginning of the tests phpunit.xml/ The PHPUnit configuration file WebTestCase.php/ The base class for web-based functional tests

First test ever:

//DbTest.php
<?php
class DbTest extends CTestCase
{  
     public function testConnection()
     {
        $this->assertNotNull(Yii::app()->db->connectionString);  
     } 
 
}

Here we have added a fairly trivial test. The assertTrue() method, which is a part of PHPUnit, is an assertion that will pass if the argument passed to it is true, and it will fail if it is false. In this case, it is testing if true is true. Change the assertEquals(true) statement in the testConnection() test method to:

$this->assertNotNull(Yii::app()->db->connectionString);

Configured database connection as an application component named db, Yii::app()->db should return an instance of the CDbConnection class. If the application failed to establish a database connection, this test would return an error. Since the test still passes, we can move forward with the confidence that the database connection is set up properly.

Generated automatically functional test SiteController:

<?php
 
class SiteTest extends WebTestCase
{
    public function testIndex()
    {
        $this->open('');
        $this->assertTextPresent('Welcome');
    }
 
    public function testContact()
    {
        $this->open('?r=site/contact');
        $this->assertTextPresent('Contact Us');
        $this->assertElementPresent('name=ContactForm[name]');
 
        $this->type('name=ContactForm[name]','tester');
        $this->type('name=ContactForm[email]','tester@example.com');
        $this->type('name=ContactForm[subject]','test subject');
        $this->click("//input[@value='Submit']");
        $this->waitForTextPresent('Body cannot be blank.');
    }
 
    public function testLoginLogout()
    {
        $this->open('');
        // ensure the user is logged out
        if($this->isTextPresent('Logout'))
            $this->clickAndWait('link=Logout (demo)');
 
        // test login process, including validation
        $this->clickAndWait('link=Login');
        $this->assertElementPresent('name=LoginForm[username]');
        $this->type('name=LoginForm[username]','demo');
        $this->click("//input[@value='Login']");
        $this->waitForTextPresent('Password cannot be blank.');
        $this->type('name=LoginForm[password]','demo');
        $this->clickAndWait("//input[@value='Login']");
        $this->assertTextNotPresent('Password cannot be blank.');
        $this->assertTextPresent('Logout');
 
        // test logout process
        $this->assertTextNotPresent('Login');
        $this->clickAndWait('link=Logout (demo)');
        $this->assertTextPresent('Login');
    }
}

Download source code of chapter 1-2, chapter 3

Leave a Comment

Fields with * are required.

Please enter the letters as they are shown in the image above.
Letters are not case-sensitive.