Mocking FileReference browse & load methods
Closed this issue · 5 comments
Drew
Another one for you ;-)
I have a function that loads a local file and then processes is. I want to mock the FileReference browse() and load() functions, so I can test my file logic. Been playing with mocks, but I cannot seem to get what I need to do from the source. The three steps I need to complete are:
- Mock the internal FileReference
- Mock the AddEventListener to "progressHandler"
- Dispatch the Event.COMPLETE event on the filereference object
Is there a way I can do this?
The function I am trying to mock:
public function addRateSheet():void
{
var loadFile:Function = function(event:Event):void
{
fileReference.load(); // NO NEED TO MOCK THIS FUNCTION, BUT NEED TO CALL THE EVENT ON THE fileReference
};
// Add Event listerners for the upload sequence
fileReference.addEventListener(Event.SELECT, loadFile); // NO NEED TO MOCK THIS
fileReference.addEventListener(Event.COMPLETE, fileLoadCompleteHandler); // NEED TO MOCK THIS EVENT LISTENER
// Open Dialog box and ask user for a file. Only use .txt or .csv files
fileReference.browse([textTypes]); // NO NEED TO MOCK THIS
}
The function under test takes the Event and works on the event.target
public function fileLoadCompleteHandler(event:Event):void
{
var currentFileReference:FileReference = FileReference(event.target);
}
}
As usual would appreciate your help on this.
Thanks
Des
Hi Des,
Your example doesn't show where you are creating the FileReference.
In the example below I changed it slightly so that you pass in the FileReference to the addRateSheet()
method. I've set the test up to dispatch 4 ProgressEvent
s before dispatching Event.COMPLETE
on the FileReference
. The FixtureData
is some example data that the FileReference should return when its data
property is accessed.
You'll need to add checks to verify whatever the model is doing with the FileReference
in the progress and complete listeners.
Heres the test:
package deshartman.files
{
import flash.events.Event;
import flash.events.ProgressEvent;
import flash.net.FileReference;
import mockolate.mock;
import mockolate.runner.MockolateRunner; MockolateRunner;
import org.flexunit.async.Async;
import org.hamcrest.collection.emptyArray;
import flash.utils.ByteArray;
[RunWith("mockolate.runner.MockolateRunner")]
public class RateSheetModelTest
{
public var model:RateSheetModel;
[Embed(source="RateSheetFixture.txt", mimeType="application/octet-stream")]
public var FixtureData:Class;
[Mock]
public var fileReference:FileReference;
[Before]
public function setup():void
{
model = new RateSheetModel();
}
[Test(async, timeout="1000")]
public function addRateSheetShouldLoadFile():void
{
mock(fileReference).method("browse").args(emptyArray())
.dispatches(new Event(Event.SELECT));
mock(fileReference).method("load").noArgs()
.dispatches(new ProgressEvent(ProgressEvent.PROGRESS, false, false, 0, 1000), 100)
.dispatches(new ProgressEvent(ProgressEvent.PROGRESS, false, false, 250, 1000), 200)
.dispatches(new ProgressEvent(ProgressEvent.PROGRESS, false, false, 500, 1000), 300)
.dispatches(new ProgressEvent(ProgressEvent.PROGRESS, false, false, 750, 1000), 400)
.dispatches(new Event(Event.COMPLETE), 500);
mock(fileReference).property("data").returns(new FixtureData() as ByteArray).atLeast(1);
Async.proceedOnEvent(this, model, "rateSheetAdded", 1000);
model.addRateSheet(fileReference);
}
}
}
Here is the RateSheetModel
Class I used in the above test.
package deshartman.files
{
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.ProgressEvent;
import flash.net.FileFilter;
import flash.net.FileReference;
import flash.utils.ByteArray;
[Event(name="rateSheetAdded", type="flash.events.Event")]
public class RateSheetModel extends EventDispatcher
{
public function RateSheetModel()
{
super();
}
public function addRateSheet(fileReference:FileReference):void
{
var rateSheetFileTypes:Array = [];
fileReference.addEventListener(Event.SELECT, rateSheetSelect);
fileReference.addEventListener(ProgressEvent.PROGRESS, rateSheetUploadProgress);
fileReference.addEventListener(Event.COMPLETE, rateSheetUploadComplete);
fileReference.browse(rateSheetFileTypes);
}
protected function rateSheetSelect(event:Event):void
{
var fileReference:FileReference = (event.target as FileReference);
fileReference.load();
}
protected function rateSheetUploadProgress(event:Event):void
{
var fileReference:FileReference = (event.target as FileReference);
// update progress..
}
protected function rateSheetUploadComplete(event:Event):void
{
var fileReference:FileReference = (event.target as FileReference);
// access file data
var data:ByteArray = fileReference.data;
// indicate success
dispatchEvent(new Event("rateSheetAdded"));
}
}
}
HTH
Drew
I have used the above code to mock up the test. It is slightly different to yours, in that I use the code in a presenter and a controller, rather than a model, but the same idea. Problem I am having, is that everything seems to work, except the events are not dispatched on the browse, so the EventListeners are not firing off. I debugged the code and the mocks have them there. I get to browse and then nothing? Is there something I am missing here or missing on the mock to dispatch the event?
Thanks
Des
Here is what I have in the test
Test
public function addValidRateSheetTest():void
{
var testByteArray:ByteArray = new ByteArray();
var testSize:int = 1000;
var testName:String = "testFile.csv"
var textTypes:FileFilter = new FileFilter("Rate Files Files (csv, txt)", "*.csv; *.txt");
mock(fileReferenceMock).method("browse").args([textTypes]).dispatches(new Event(Event.SELECT),100);
mock(fileReferenceMock).method("load").noArgs()
.dispatches(new ProgressEvent(ProgressEvent.PROGRESS, false, false, 0, 1000), 100)
.dispatches(new ProgressEvent(ProgressEvent.PROGRESS, false, false, 750, 1000), 200)
.dispatches(new Event(Event.COMPLETE), 300);
mock(fileReferenceMock).property("data").returns(testByteArray);
mock(fileReferenceMock).property("size").returns(testSize);
mock(fileReferenceMock).property("name").returns(testName);
fixture.currentFileReference = fileReferenceMock;
var checkEvent:Function = function(fileEvent:FileEvent, passThrough:Object):void
{
Assert.assertEquals("The FileEvent references the FileReference ByteArray", fileEvent.byteArray, testByteArray);
Assert.assertEquals("The FileEvent's RateSheet name is correct", fileEvent.rateSheet.name, testName);
Assert.assertEquals("The FileEvent's RateSheet size is correct", fileEvent.rateSheet.size, testSize);
}
Async.handleEvent(this,fixture.eventDispatcher,FileEvent.FILE_CONVERT_TO_RATESHEET,checkEvent,1000,null,noEventFired);
/////////////////////////////////////////////
fixture.addRateSheet();
/////////////////////////////////////////////
}
and here is what I have in the function being tested:
fixture
public class RateSheetAssignPresenter extends EventDispatcher
{
[Inject]
public var rateSheetModel:RateSheetModel;
[Dispatcher]
public var eventDispatcher:IEventDispatcher;
// Reference class to the file on disk (selected by user)
private var _currentFileReference:FileReference;
public function addRateSheet():void
{
var loadFile:Function = function(event:Event):void
{
_currentFileReference.load();
};
// Add Event listerners for the upload sequence
_currentFileReference.addEventListener(Event.SELECT, loadFile);
_currentFileReference.addEventListener(Event.OPEN, openHandler);
_currentFileReference.addEventListener(ProgressEvent.PROGRESS, progressHandler);
_currentFileReference.addEventListener(Event.COMPLETE, fileLoadCompleteHandler);
_currentFileReference.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
// Open Dialog box and ask user for a file. Only use .txt or .csv files
_currentFileReference.browse([textTypes]);
}
public function fileLoadCompleteHandler(event:Event):void
{
var currentFileReference:FileReference = FileReference(event.target);
rateSheetModel.name = currentFileReference.name;
rateSheetModel.size = currentFileReference.size;
rateSheetModel.byteArray = currentFileReference.data;
eventDispatcher.dispatchEvent(new FileEvent(FileEvent.FILE_CONVERT_TO_RATESHEET));
}
Hi Des,
I found the problem, and I think it might be a bug in Mockolate.
The issue is that the mock doesn't think it is being called with the correct arguments and so doesn't dispatch the events. The two lines that are affected are:
In the test
mock(fileReferenceMock).method("browse").args([textTypes]).dispatches(new Event(Event.SELECT),100);
In RateSheetAssignPresenter
_currentFileReference.browse([textTypes]);
I think Mockolate is incorrectly handling the [textTypes]
Array given to args()
.
For now in the test you can change the [textTypes]
to array(textTypes)
:
mock(fileReferenceMock).method("browse").args(array(textTypes)).dispatches(new Event(Event.SELECT),100);
By explicitly using the array()
matcher from hamcrest-as3 Mockolate does the right thing.
Thanks for finding this issue. I'll be looking to fix that after replying here.
cheers,
Drew
Drew
Sweet. I'll try it and let you know
Des
Just pushed the fix too,
cheers,
Drew