2014年4月19日 星期六

[AS] Unit Test 的基本測試工具

因為不是每個 Flash IDE 都有簡單的 flex unit 可以使用!

所以我仿照 flex unit 作了一個自己的測試類別 BasicUnitTestCase

只要繼承這個類別

就可以仿照 flex unit 4 的方式來建構 test case

並且簡單的測試!

不需要使用 premium 版本的 IDE

也不需要特定的 IDE 等等的部分!

當然也有一些別人寫好的這種類別

例如 asUnit asUnit Git URL

因為某些原因所以  asunit 對我來說不是很適用,

所以才產出了這個類別 !

來講一下我寫這個 測試類別的基本需求

1. 可以直接和 flex unit 交互使用(test case, Assert ).
2. 提供簡易報表 和 詳細報表
3. 簡單的使用和測試.

因為需求 1,

所以有了 metadata 的分析

和 flex unit 一樣直接取 Test tag 的 test case

也和 flex unit 一樣的基本斷言函式 assertEquals

為了測試正確 所以暫時不使用 flex unit 的 Before 和 After 這兩個 metadata,

強迫使用者複寫 startUp 和 shutdown 這兩個函數

確保一定會做到這兩個動作!

因為需求 2.

整個測試完畢會使用 showLog 將基本的報告印出.

也可以使用 Detail 來取的比較完整的報告

最後需求3.

只需要 BasicUnitTestCase 就可以進行測試,

雖然目前功能還是相對簡單! 

所以對目前的幾個 IDE 介面都可以使用.

綜合以上 有了下面的類別
package com.tenchiwang.unitTest
{
    import flash.events.ErrorEvent;
    import flash.utils.describeType;

    public class BasicUnitTestCase
    {
        private var _testCaseList:Vector.<TestCaseVo>;
        private var _detail:String = '';
        private var _ErrorCount:int;
        private var _PassCount :int;
        private var _isPass:Boolean = false;
        private var _callback:Function;
       
        // 基本的測試類別!
        public function BasicUnitTestCase(callBack:Function = null)
        {
            _callback = callBack;
            _testCaseList = new Vector.<TestCaseVo>();
           
            saveDetail('Analyze Unit Test....');
           
            var accessorsList:XMLList = describeType(this).method;
            var fun:Function;
            var vo:TestCaseVo;
            var acces:XML;
            var metadataName:String;
           
            for(var i:int = 0;i<accessorsList.length();i++)
            {
                acces = accessorsList[i];
                metadataName = acces.metadata[0].@name;
                metadataName = metadataName.toLocaleLowerCase();
               
                switch(metadataName)
                {
                    case 'test':
                        fun = this[acces['@name']] as Function;
                       
                        if(fun != null)
                        {
                            vo = new TestCaseVo();
                            vo.name = acces['@name'];
                            vo.fun = fun;
                             _testCaseList.push(vo);
                        }
                        break;
                   
                    default:
                }
            }
           
            saveDetail('Analyze Unit Test End. Get ' + _testCaseList.length + ' tests');
            //
            runTest();
           
            //
            if(_callback != null)
                _callback.apply();
        }
       
        final public function assertEquals(a:*, b:*):void
        {
            if( a === b)
            {
                _PassCount++;
                _isPass = true;
            }
        }
       
        private function runTest():void
        {
            showLog('Unit Test Start.');
           
            saveDetail('Unit Test Start ....');
            saveDetail('=======================');
            var len:int = _testCaseList.length;
            var vo:TestCaseVo;
            for(var i:int = 0; i < len ;i++)
            {
                vo = _testCaseList[i];
               
                saveDetail( vo.name + ' Start.' );
               
                _isPass = false;
                startUp();
               
                try
                {
                    vo.fun.call();
                }
                catch(error:Error)
                {
                    showLog('Test Case ' + vo.name + ' Error.');
                    saveDetail('Test Case ' + vo.name + ' Error.');
                    _ErrorCount++;
                }
                catch(evt:ErrorEvent)
                {
                    showLog('Test Case ' + vo.name + ' Error.');
                    saveDetail('Test Case ' + vo.name + ' Error.');
                    _ErrorCount++;
                }

                shutdown();
                saveDetail(vo.name + ' End. ' );
            }
            showLog('Unit Test End.');
           
            showLog('Test Case ' + len);
            showLog('Pass Case '  + _PassCount );
            showLog('Error Case ' + _ErrorCount);
           
           
            //
            saveDetail('=======================');
            saveDetail('Unit Test End.');
           
            saveDetail('Test Case ' + len);
            saveDetail('Pass Case '  + _PassCount );
            saveDetail('Error Case ' + _ErrorCount);
           
        }
       
        private function saveDetail(msg:String):void
        {

           
            _detail += getDate() + msg + '\n';
        }
       
        private function getDate():String
        {
            var date:Date = new Date();
            var dateStr:String = '[' + date.getUTCHours() + ' : ' + date.getUTCMinutes() + ' : ' +
                date.getUTCSeconds() + ' ' + date.getUTCMilliseconds() +'] ' ;
           
            return dateStr;
        }
       
        /**
         *  每次 unit test 前須要做的事
         */
        protected function startUp():void
        {
            throw new Error('Plz override startUp.');
        }
       
        /**
         *  每次 unit test 後須要做的事
         */
        protected function shutdown():void
        {
            throw new Error('Plz override shutdown.');
        }
       
        /**
         * 預設的顯示 Log 的通用函數
         * @param msg
         *
         */       
        protected function showLog(msg:String):void
        {
            trace(getDate() + msg);
        }
       
        /**
         * 顯示詳細的測試資料.
         */
        public function get Detail():String
        {
            return _detail;
        }
       
    }
}

internal class TestCaseVo
{
    public var name:String;
    public var fun:Function;
}

沒有留言 :