Apache flex 如何在Flex/AS3中使用基于cookie的会话测试服务

Apache flex 如何在Flex/AS3中使用基于cookie的会话测试服务,apache-flex,cookies,service,tdd,robotlegs,Apache Flex,Cookies,Service,Tdd,Robotlegs,第一次在这里发布。感谢您抽出时间来查看此问题 正如标题所示,问题与flex应用程序的服务层有关。特别是在测试用例类中。要调用服务,用户必须首先通过基于html/CF的页面进行身份验证。完成此操作并进行身份验证后,用户将被定向到html页面,其中嵌入了flex应用程序。一旦通过身份验证,服务器端CF代码将向用户计算机写入cookie。然后flex应用程序读取此cookie,flex应用程序调用CF服务时需要此cookie 问题是:flex应用程序中服务类的测试用例类应该如何处理这个问题?基本上需要

第一次在这里发布。感谢您抽出时间来查看此问题

正如标题所示,问题与flex应用程序的服务层有关。特别是在测试用例类中。要调用服务,用户必须首先通过基于html/CF的页面进行身份验证。完成此操作并进行身份验证后,用户将被定向到html页面,其中嵌入了flex应用程序。一旦通过身份验证,服务器端CF代码将向用户计算机写入cookie。然后flex应用程序读取此cookie,flex应用程序调用CF服务时需要此cookie

问题是:flex应用程序中服务类的测试用例类应该如何处理这个问题?基本上需要执行以下步骤:使用一些帐户数据访问服务器并进行身份验证,然后需要写入cookie(已经写入),然后测试用例需要从cookie中提取会话id,并将其传递给测试用例中的服务调用。这对于一个测试用例类来说似乎太多了

那么,这类事情通常是如何处理的呢。据我所知,web服务调用通常需要这样的会话id

非常感谢您的任何想法、意见、建议等

感谢您的时间和您能提供的任何信息


Sean将在集成测试中测试cookie和实际服务调用。您认为这些位适合于单元测试是正确的

我喜欢做的是委派(假设您的集成点尚未表示接口)并创建实现我选择的接口的测试委派。cookie将被“检索”,并且对于您的测试而言,它始终是有效的。如果需要将其设置为false,则创建另一个测试委托并将其作为依赖项提供

Mockolate可以走很长的路,但构建实体Mock是关键

这就是一个例子。它不会直接回答你的问题,但我认为它代表了一些相同的问题,因此可能会帮助你解决特定的情况

我正在使用第三方库处理此应用程序中的SQL操作。该库有一个名为SQLRunner的核心类。碰巧,这个类没有接口(这会使事情变得更简单)。我有两个选择

  • 分叉并修改库以表示接口
  • 在我自己的委托类中包装SQLRunner
  • 碰巧,我两者都做了,但出于一些原因,我更喜欢第二种方法。我能够完全定义第三方库的API和功能。这太棒了。我实际上没有更改API,但是如果您不喜欢它命名方法的方式。。。别担心,把它换一下。它还允许我表达一个界面!我最初这样做是因为我想用mokolate来模拟它的用法。它还可以方便地创建我自己的测试模拟,这些模拟具有更强大的功能,而且更清晰。这就是服务:

    public class SQLTaskService extends Actor implements ITaskService
    {
        [Inject]
        public var sqlRunner:ISQLRunnerDelegate;
    
        [Inject]
        public var statusListModel:StatusListModel;
    
        [Inject]
        public var taskListModel:TaskListModel;
    
        public function loadAllTasks():void
        {
            statusListModel.removeAllTasks();
            sqlRunner.execute(LOAD_ALL_TASKS_SQL, null, loadAllTasksResultHandler, Task, databaseErrorHandler);
        }
    
        private function loadAllTasksResultHandler(result:SQLResult):void
        {
            for each(var task:Task in result.data)
            {
                var taskStatus:Status = statusListModel.getStatusFromId(task.statusId);
                statusListModel.addTaskToStatus(task, taskStatus);
                taskListModel.addTask(task);
            }
        }
    
        public function loadTaskById(id:int):void
        {
            sqlRunner.execute(LOAD_TASK_SQL, {taskId:id}, loadTaskResultHandler, Task);
        }
    
        private function loadTaskResultHandler(result:SQLResult):void
        {
            var task:Task = result.data[0] as Task;
            var taskStatus:Status = statusListModel.getStatusFromId(task.statusId);
    
            task = taskListModel.updateTask(task);
    
            statusListModel.addTaskToStatus(task, taskStatus);
        }
    
        public function save(task:Task):void
        {
            var params:Object = task.toParamObject();
    
            sqlRunner.executeModify(Vector.<QueuedStatement>(
                    [new QueuedStatement(SAVE_TASK_SQL, params)]), saveTaskResultHandler, databaseErrorHandler);
        }
    
    
        private function saveTaskResultHandler(results:Vector.<SQLResult>):void
        {
            var result:SQLResult = results[0];
            if (result.rowsAffected > 0)
            {
                var id:Number = result.lastInsertRowID;
                loadTaskById(id);
            }
        }
    
        public function deleteTask(task:Task):void
        {
            sqlRunner.executeModify(Vector.<QueuedStatement>([new QueuedStatement(DELETE_TASK_SQL, {taskId:task.taskId})]),
                    deleteTaskResult, databaseErrorHandler);
        }
    
        private function deleteTaskResult(results:Vector.<SQLResult>):void
        {
            //pass
        }
    
        private function databaseErrorHandler(error:SQLError):void
        {
            dispatch(new DatabaseErrorHandlerEvent(error.message));
        }
    
        [Embed(source="/assets/data/sql/tasks/SaveTask.sql", mimeType="application/octet-stream")]
        private static const SaveTaskStatementText:Class;
        public static const SAVE_TASK_SQL:String = new SaveTaskStatementText();
    
        [Embed(source="/assets/data/sql/tasks/DeleteTask.sql", mimeType="application/octet-stream")]
        private static const DeleteTaskStatementText:Class;
        public static const DELETE_TASK_SQL:String = new DeleteTaskStatementText();
    
        [Embed(source="/assets/data/sql/tasks/LoadTask.sql", mimeType="application/octet-stream")]
        private static const LoadTaskStatementText:Class;
        public static const LOAD_TASK_SQL:String = new LoadTaskStatementText();
    
        [Embed(source="/assets/data/sql/tasks/LoadAllTasks.sql", mimeType="application/octet-stream")]
        private static const LoadAllTasksStatementText:Class;
        public static const LOAD_ALL_TASKS_SQL:String = new LoadAllTasksStatementText();
    }
    

    旁注:这里我想指出的是,没有一个异步测试。很少需要异步运行单元测试。有些边缘案例可能确实需要,但它们是例外。

    哇,Joel,综合回答:)我也同意服务测试不是单元测试(正如你所说的,集成测试)。我很好奇为什么你需要Flex来了解cookie。你不能只使用http头吗?他们应该在Flex的呼叫中保持不变。
    /**
     * This is a delegate for the SQLRunner class that allows us to utilize an interface
     * for the purposes of creating mocks. The actual SQLRunner class does not express
     * an interface. This approach also allows us to encapsulate the usage of a 3rd party
     * library into this single delegate.
     *
     * <p>An alternative would be to fork and modify the original library, which would
     * definitely be a viable option and would help others in the future.</p>
     */
    public class SQLRunnerDelegate implements ISQLRunnerDelegate
    {
        private var sqlRunner:SQLRunner;
    
        public function SQLRunnerDelegate(dataBaseFile:File, maxPoolSize:int = 5)
        {
            sqlRunner = new SQLRunner(dataBaseFile, maxPoolSize);
        }
    
        public function get numConnections():int
        {
            return sqlRunner.numConnections;
        }
    
        public function get connectionErrorHandler():Function
        {
            return sqlRunner.connectionErrorHandler;
        }
    
        public function set connectionErrorHandler(value:Function):void
        {
            sqlRunner.connectionErrorHandler = value;
        }
    
        public function execute(sql:String, parameters:Object, handler:Function, itemClass:Class = null, errorHandler:Function = null):void
        {
            sqlRunner.execute(sql, parameters, handler, itemClass, errorHandler);
        }
    
        public function executeModify(statementBatch:Vector.<QueuedStatement>, resultHandler:Function, errorHandler:Function, progressHandler:Function = null):void
        {
            sqlRunner.executeModify(statementBatch, resultHandler, errorHandler, progressHandler);
        }
    
        public function close(resultHandler:Function, errorHandler:Function = null):void
        {
            sqlRunner.close(resultHandler, errorHandler);
        }
    }
    
    /**
     * This is a more robust mock for the SQLRunnerDelegate to test for
     * side effects that occur when methods are called on SQLTaskService
     */
    public class MockTaskSQLRunnerDelegate extends MockSQLRunnerDelegateBase implements ISQLRunnerDelegate
    {
        public function execute(sql:String, parameters:Object, handler:Function, itemClass:Class = null, errorHandler:Function = null):void
        {
            lastStatementExecuted = sql;
            allStatementsExecuted.push(lastStatementExecuted);
            parametersSent = parameters;
    
            switch (sql)
            {
                case SQLTaskService.LOAD_ALL_TASKS_SQL:
                    handler.call(null, loadTask());
    
                    break;
    
                case SQLTaskService.LOAD_TASK_SQL:
                    handler.call(null, loadTask());
                    break;
                default:
                    break;
            }
        }
    
        private function loadTask():SQLResult
        {
            var task:Task = new Task();
            var data:Array = [task];
            var result:SQLResult = new SQLResult(data);
    
            task.taskId = 1;
            task.statusId = 1;
    
            return result;
        }
    
        public function executeModify(statementBatch:Vector.<QueuedStatement>, resultHandler:Function, errorHandler:Function, progressHandler:Function = null):void
        {
            lastStatementExecuted = statementBatch[0].statementText;
            allStatementsExecuted.push(lastStatementExecuted);
            parametersSent = statementBatch[0].parameters;
    
            switch (lastStatementExecuted)
            {
                case SQLTaskService.SAVE_TASK_SQL:
                    resultHandler.call(null, saveTask());
                    break;
            }
        }
    
        private function saveTask():Vector.<SQLResult>
        {
            var task:Task = new Task();
            var result:SQLResult = new SQLResult([task], 1, true, 1);
            var results:Vector.<SQLResult> = new Vector.<SQLResult>();
    
            task.taskId = task.statusId = 1;
            results.push(result);
    
            return results;
        }
    
        public function get numConnections():int
        {
            return 0;
        }
    
        public function get connectionErrorHandler():Function
        {
            return null;
        }
    
        public function set connectionErrorHandler(value:Function):void
        {
        }
    
        public function close(resultHandler:Function, errorHandler:Function = null):void
        {
        }
    }
    
    public class SqlTaskServiceTest
    {
        private var taskService:SQLTaskService;
    
        [Before(async)]
        public function setup():void
        {
            taskService = new SQLTaskService();
            taskService.statusListModel = new StatusListModel();
            taskService.taskListModel = new TaskListModel();
            initializeModels();
            prepareMockolates();
        }
    
        public function prepareMockolates():void
        {
            Async.proceedOnEvent(this, prepare(ISQLRunnerDelegate), Event.COMPLETE);
        }
    
        [Test]
        public function loadAllTasks_executesSqlStatement_statementEqualsLoadAll():void
        {
            var runner:MockTaskSQLRunnerDelegate = new MockTaskSQLRunnerDelegate();
            taskService.sqlRunner = runner;
    
            taskService.loadAllTasks();
    
            assertThat(runner.lastStatementExecuted, equalTo(SQLTaskService.LOAD_ALL_TASKS_SQL));
        }
    
        [Test]
        public function loadAllTasks_clearsTasksFromStatusListModel_lengthIsEqualToZero():void
        {
            var status:Status = new Status();
            var task:Task = new Task();
    
            initializeModels(status, task);
            taskService.sqlRunner = nice(ISQLRunnerDelegate);
    
            taskService.loadAllTasks();
    
            assertThat(status.tasks.length, equalTo(0))
        }
    
        [Test]
        public function loadAllTasks_updatesTaskListModelWithLoadedTasks_collectionLengthIsOne():void
        {
            taskService.sqlRunner = new MockTaskSQLRunnerDelegate();
    
            taskService.loadAllTasks();
    
            assertThat(taskService.taskListModel.tasks.length, equalTo(1));
        }
    
        [Test]
        public function loadAllTasks_updatesStatusWithTask_statusHasTasks():void
        {
            var status:Status = new Status();
            initializeModels(status);
            taskService.sqlRunner = new MockTaskSQLRunnerDelegate();
    
            taskService.loadAllTasks();
    
            assertThat(status.tasks.length, greaterThan(0));
        }
    
        [Test]
        public function save_executesSqlStatement_statementEqualsSave():void
        {
            var task:Task = new Task();
            var runner:MockTaskSQLRunnerDelegate = new MockTaskSQLRunnerDelegate();
            taskService.sqlRunner = runner;
            task.statusId = 1;
    
            taskService.save(task);
    
            assertThat(runner.allStatementsExecuted, hasItem(SQLTaskService.SAVE_TASK_SQL));
        }
    
        [Test]
        public function save_taskIsLoadedAfterSave_statementEqualsLoad():void
        {
            var task:Task = new Task();
            var runner:MockTaskSQLRunnerDelegate = new MockTaskSQLRunnerDelegate();
            taskService.sqlRunner = runner;
            task.statusId = 1;
    
            taskService.save(task);
    
            assertThat(runner.allStatementsExecuted, hasItem(SQLTaskService.LOAD_TASK_SQL));
        }
    
        [Test]
        public function save_taskIsAddedToModelWhenNew_tasksLengthGreaterThanZero():void
        {
            var taskListModel:TaskListModel = taskService.taskListModel;
            var task:Task = new Task();
    
            taskListModel.reset();
            taskService.sqlRunner = new MockTaskSQLRunnerDelegate();
            task.statusId = 1;
            task.taskId = 1;
    
            taskService.save(task);
    
            assertThat(taskListModel.tasks.length, equalTo(1));
        }
    
        [Test]
        public function save_existingTaskInstanceIsUpdatedAfterSave_objectsAreStrictlyEqual():void
        {
            var taskListModel:TaskListModel = taskService.taskListModel;
            var task:Task = new Task();
            var updatedTask:Task;
    
            taskListModel.addTask(task);
            taskService.sqlRunner = new MockTaskSQLRunnerDelegate();
            task.statusId = 1;
            task.taskId = 1;
    
            taskService.save(task);
            updatedTask = taskListModel.getTaskById(task.taskId);
    
            assertThat(updatedTask, strictlyEqualTo(task));
        }
    
        [Test]
        public function loadTaskById_executesLoadStatement_statementEqualsLoad():void
        {
            var runner:MockTaskSQLRunnerDelegate = new MockTaskSQLRunnerDelegate();
            taskService.sqlRunner = runner;
    
            taskService.loadTaskById(1);
    
            assertThat(runner.allStatementsExecuted, hasItem(SQLTaskService.LOAD_TASK_SQL));
        }
    
        [Test]
        public function deleteTasks_executesDeleteStatement_statementEqualsDelete():void
        {
            var task:Task = new Task();
            var runner:MockTaskSQLRunnerDelegate = new MockTaskSQLRunnerDelegate();
            taskService.sqlRunner = runner;
    
            taskService.deleteTask(task);
    
            assertThat(runner.allStatementsExecuted, hasItem(SQLTaskService.DELETE_TASK_SQL));
        }
    
        private function initializeModels(status:Status = null, task:Task = null):void
        {
            var statusListModel:StatusListModel = taskService.statusListModel;
            statusListModel.reset();
    
            //if nothing was passed in we need to default to new objects
            status ||= new Status();
            task ||= new Task();
    
            status.statusId = 1;
            task.taskId = task.statusId = 1;
    
            statusListModel.statuses.addItem(status);
            statusListModel.addTaskToStatus(task, status);
        }
    }