Skip to content

Instantly share code, notes, and snippets.

@joelhooks
Created July 6, 2011 02:36
Show Gist options
  • Save joelhooks/1066415 to your computer and use it in GitHub Desktop.
Save joelhooks/1066415 to your computer and use it in GitHub Desktop.
Delegating at the Boundaries to Create a Testable Service
import com.probertson.data.QueuedStatement;
public interface ISQLRunnerDelegate
{
function get numConnections():int;
function get connectionErrorHandler():Function;
function set connectionErrorHandler(value:Function):void;
function execute(sql:String, parameters:Object, handler:Function, itemClass:Class = null, errorHandler:Function = null):void;
function executeModify(statementBatch:Vector.<QueuedStatement>, resultHandler:Function, errorHandler:Function, progressHandler:Function = null):void;
function close(resultHandler:Function, errorHandler:Function = null):void;
}
public class MockStatusSQLRunnerDelegate 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 SQLStatusService.LOAD_ALL_STATUSES_SQL:
handler.call(null, loadAllStatuses());
break;
}
}
private function loadAllStatuses():SQLResult
{
return new SQLResult([new Status()], 1, true, 1);
}
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;
}
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
{
}
/**
* 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
{
}
}
/**
* 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);
}
}
public class SQLStatusService extends Actor implements IStatusService
{
[Inject]
public var sqlRunner:ISQLRunnerDelegate;
[Inject]
public var statusListModel:StatusListModel;
public function loadStatuses():void
{
statusListModel.reset();
sqlRunner.execute(LOAD_ALL_STATUSES_SQL, null, loadStatusesResultHandler, Status, databaseFaultHandler);
}
private function loadStatusesResultHandler(result:SQLResult):void
{
statusListModel.statuses = new ArrayCollection(result.data);
dispatch(new StatusesLoadedEvent());
}
private function databaseFaultHandler(error:SQLError):void
{
dispatch(new DatabaseErrorHandlerEvent(error.message));
}
[Embed(source="/assets/data/sql/statuses/LoadAllStatuses.sql", mimeType="application/octet-stream")]
private static const LoadAllStatusesStatementText:Class;
public static const LOAD_ALL_STATUSES_SQL:String = new LoadAllStatusesStatementText();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment