Implement Testing on Yii Framework 1

How to Implement Testing on Yii Framework 1

So you’ve finally realised to verify the quality of the code in releases and make sure everything is working before you deploy to production you need to test.

First we’ll use phpunit for unit testing.

Installing PHPUnit with Composer

We will only handle installing the required packages with composer because PEAR and PHP Archives are old news. Check out composer first, if you haven’t used it.

Before we get going make sure your composer is updated


composer self-update

First of all it is easier to use the global composer and install phpunit globally:


composer global require "phpunit/phpunit=4.8.*"
composer global require "phpunit/phpunit-selenium": ">=1.2"

But make sure you update composer.json


 {
      "require": {
          "php": ">=5.3.2",
          "yiisoft/yii": "1.1.*",
          "yiiext/migrate-command": "*",
          "phpunit/phpunit": "4.8.*",
          "phpunit/phpunit-selenium": ">=1.2"
      }
  }

But you will still get warnings like:

PHP Warning:  include(PHP_Invoker.php): failed to open stream: No such file or directory in /var/www/site/vendor/yiisoft/yii/framework/YiiBase.php on line 432
PHP Warning:  include(): Failed opening 'PHP_Invoker.php' for inclusion in /var/www/site/vendor/yiisoft/yii/framework/YiiBase.php on line 432
PHP Warning:  include(PHPUnit_Extensions_Database_TestCase.php): failed to open stream: No such file or directory in /var/www/site/vendor/yiisoft/yii/framework/YiiBase.php on line 432
PHP Warning:  include(): Failed opening 'PHPUnit_Extensions_Database_TestCase.php' for inclusion in /var/www/site/vendor/yiisoft/yii/framework/YiiBase.php on line 432
PHP Warning:  include(PHPUnit_Extensions_Story_TestCase.php): failed to open stream: No such file or directory in /var/www/site/vendor/yiisoft/yii/framework/YiiBase.php on line 432
PHP Warning:  include(): Failed opening 'PHPUnit_Extensions_Story_TestCase.php' for inclusion in /var/www/site/vendor/yiisoft/yii/framework/YiiBase.php on line 432

So we actually need more stuff, namely dbunit, phpunit-story and php-invoker:
So add the following to composer.json


 "phpunit/php-invoker": "*",
 "phpunit/dbunit": ">=1.2",
 "phpunit/phpunit-story": "1.*"

If you installed globally you need to install using:

composer global require {package}

But when running the tests from a task runner or continuous integration like jenkins, it is best to use the path to the composer phpunit in vendor.

Installed, Now let us write our first unit test

The folder where the test suite (class consisting of test functions) is <code>/protected/tests/unit</code>.

The file name needs to end in Test.php so for a test class: DummyTest.php.

Now the contents of the file:

<?php
 class DummyTest extends CTestCase{
   public function testTrue(){
     $var = true;
     $this->assertTrue($var);
   }
 }
?>

Running the Test

To run the tests you need to be within the test folder:


cd protected/tests
//run all the tests
../../../vendor/bin/phpunit unit
//run the single test suite
../../../vendor/bin/phpunit unit/DummyTest.php

The test results (output) is not the best.
A single dot . means a test passed.
A letter F means the test failed.

Setting Things up and Tearing things Down

Usually you will need certain things to happen before running a test. Such as: instantiation of an object or a connection to a database. To this we use setUp(). Which is a method called once before each test. Often calling the parent setUp function is good practice. Conversely, callingtearDown() is run after a test is run to free up resources.

setUp() and tearDown()

are run before and after each test case whether or not the test succeeds.

There are also

setUpBeforeClass() and tearDownAfterCall()

which are only called once regardless of test cases.

Fixtures

What are Fixtures? They are a state of being of the test. They are mainly used when databases are involved.

How to create and use Offline Maps with an Android App?

When the app we know that the app we have proposed to make is going to be used in areas with no wifi or cellular networks nearby or when we want to offer an offline map option we make use of Offline maps. Otherwise it would be easier to implement the Android Google Map API.

Maps are made up of a collection of images often referred to as tiles. There are sets of tiles for each zoom level. So the first step in creating an offline map is selecting the area and zoom level to create the tiles.

Creating the Offline Map Tiles

We make use of the Mobile Atlas Creator.

1. Download and extract the files

2. Run the application with the .exe or the .sh script. Make sure that suitable permissions (execute) is given to the file.

3. Use the right click to navigate around the map. Use the right mouse button to select an area.

4. Make sure the MapSource is set to OpenStreetMap MapQuest. You can try others but the one that works for me is OpenStreetMap MapQuest.

5. Make sure the Atlas Format is Osmdroid ZIP.

6. Select the Zoom Levels less will make the file size smaller. Select Recreate/adjust map tiles, PNG and leave the size as deafult. Note: It has to be PNG, JPEGs won’t work on Android

7. Name the selection and click Add Selection.

8. Then Create Atlas

Adding the Offline Map Tiles to your Android Project

The Map tiles folder or .zip needs to be named specifically and placed in the correct location.

1. Rename the zip to my_map_name.zip.

2. Rename the folder in the zip to MapquestOSM. Take note of the spelling and case.

3. Place the my_map_name.zip file into app/src/main/assets

Copying the Map Files to the SD Card

We need to copy the Map Tiles to the SD Card (External Storage) on the Android Device.

1. Add a function preferably in the application class to make sure the file is copied across to the device.


public static void copyTilestoSDCard(){
//save zip to sd
AssetManager am = BaseApp.getmContext().getAssets();
InputStream is;
//zip file in assets root
String sourceFileName = "mp_map_name.zip";
//zip file in SD card
String destinationFileName = "tiles.zip";
String osmDirName = "osmdroid";

File osmDir = new File(Environment.getExternalStorageDirectory() + File.separator + osmDirName);

if (!osmDir.exists()){
osmDir.mkdir();
}
String filePath = Environment.getExternalStorageDirectory() + File.separator +
osmDirName + File.separator + destinationFileName;
File destFile = new File(filePath);
if (!destFile.exists()){
//file doesn't exist
try {
is = am.open(sourceFileName);
FileOutputStream fo = new FileOutputStream(filePath);

byte[] b = new byte[1024];
int length;
while ((length = is.read(b)) != -1){
fo.write(b, 0, length);
}
fo.flush();
fo.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

Setting Required Permissions for the Offline Map

 <uses-permission android:name="android.permission.INTERNET"/>
 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
 <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
 <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

Add Open Street Maps Droid with Gradle

You need the OSMDroid, you can add to gradle with:

compile 'org.osmdroid:osmdroid-android:4.2'
compile 'org.slf4j:slf4j-simple:1.6.1'

Now to Display the Offline Map on the Android Device

To display in the app make sure your layout file contains:

<org.osmdroid.views.MapView
    android:id="@+id/offlineMapView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:visibility="gone"
    android:clickable="true"
    android:enabled="true"
    android:layout_alignParentTop="true"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="true"
    />

The controller Fragment or Activity should then intialise the map:


org.osmdroid.views.MapView mOfflineMapView = findById(R.id.offlineMapView);
//Make sure you don't get the map from online
mOfflineMapView.setUseDataConnection(false);
//Set the Tile Source (That is MapqueueOSM)
mOfflineMapView.setTileSource(TileSourceFactory.MAPQUESTOSM);

Then set all your required settings for the MapView:


setMultiTouchControls();
setClickable();
getController().setZoom();
getController().setCenter();

And there we go.

Sources:

Using an Existing SQLite Database with an Android Project

This article highlights how to use an Existing SQLite database with an Android Project.

Prerequisites

A sqlite3 database is required as it is the only db that android supports (probably because it is a single simple file). Converting between databases can be tricky sometimes. In this case I needed to convert a MSSQL database to SQLite so I made use of the following repos:

Important Note Regarding ORM’s

Now you need to make a choice. Will you a using an ORM, an Object Relational Mapper. An ORM is a package that converts database records into objects. A popular approach is active record.

Some Android ORM’s:

Now if you have chosen to use an ORM take note of the following.

  • It will create the database for you based on your models and hence can’t use existing data
  • It will create tables exactly based on your models
Note: If the above is not true please comment

So basically use of an ORM is not applicable in this case as we want to ship the app with existing data.

Where is the Database Stored

The database is stored in /data/data/za.co.mydomain.myapp/databases/mydb.db.

So the database has to be copied over from the app to that location. We do this using the following package: Android SQLite Asset Helper. I am unaware of the ability to do this with the native Android SDK.

Where should I put the Existing SQLite file in the Android Project Structure

You need to place the database in: Project/app/src/main/assets.databases/mydb.db

Shipping your App with an existing SQLite database

First we need to add the dependency in build.gradle


dependencies {
    compile 'com.readystatesoftware.sqliteasset:sqliteassethelper:+'
}

So do the steps above. Now we need to create a java class that uses the SQLiteAssetHelper:


public class MyDb extends SQLiteAssetHelper{
  private static final String DB_NAME = "mydb.db";
  private static final int DB_VERSION = 1;

  public MyDB(Context context){
    super(context, DB_NAME, null, DB_VERSION);
  }
}

Awesome. Now to do the initial copy across. This is done when we first call getReadableDatabase().

So in MainActivity.java or wherever you want to do this.


MyDb db = new MyDb(this);
db.getReadableDatabase();

Querying – Getting Data with a Cursor

Now to Query data and get a Cursor (not an Object) add a function to MyDb.java:


public Cursor getData(){
      SQLiteDatabase db = getReadableDatabase();
        Cursor mCur = db.query(
                TABLE, //table
                //We Need a _id (It's required for CursorAdapter)
                new String[] {COLUMN_TREEID + " as _id", COLUMN_TREEGUID, COLUMN_ENDANGERED, COLUMN_FAMILY, COLUMN_ID, COLUMN_KRUGER, COLUMN_NAME, COLUMN_POISONOUS},
                null, //where clause
                null, //where parameters
                null, //groupby
                null, //having
                COLUMN_NAME + " ASC"
        );
        mCur.moveToFirst();
        return mCur;
    }

Now for getting the data:


Cursor myCursor = db.getTrees();

Alright, so that is that. It’s not the best solution so any comments please discus below.