Stubbing a Command's Injected Event Dependency

132 views
Skip to first unread message

dougrdotnet

unread,
Oct 10, 2011, 9:45:54 PM10/10/11
to mock...@googlegroups.com
Hey Drew,

I'm kinda stumped on this one.  I thought it should be a simple matter of creating a stub for the injected event but it appears that I am missing something.
I have a command which injects an event:

[Inject]
public var event:UserEvent;

override public function execute():void

{

if ( event.usersCollection ) {

if ( !event.usersCollection.length ) {

eventDispatcher.dispatchEvent ( new ViewEvent ( ViewEvent.SET_VIEW_STATE, false, false, StateConstants.NEW_USER ) );

} else if ( event.usersCollection.length ) {

eventDispatcher.dispatchEvent ( new ViewEvent ( ViewEvent.SET_VIEW_STATE, false, false, StateConstants.SELECT_USER ) );

}

}

}

I am testing the individual conditions of the command.  Here is my setup:

[Rule]
public var rule:MockolateRule = new MockolateRule();

[Mock]
public var instanceEvent:UserEvent;

private var instanceCommand:ViewStateDecisionCommand;

private var collection:ArrayCollection;

[Before]
public function setUp():void

{

collection = new ArrayCollection();

instanceCommand = new ViewStateDecisionCommand();

instanceCommand.eventDispatcher = new EventDispatcher();

}

[After]

public function tearDown():void

{

instanceCommand = null;

}

I don't even make it into my test method before I get the following runtime exception (I've highlighted the actual error below, Line 40 is actually the event's call to super(type, bubbles, cancelable);):


[SWF] FlexUnitApplication.swf - 3,994,601 bytes after decompression

FloxyMockolateFactory prepareClassRecipes [class UserEvent],

ProxyRepository.prepareClasses 1 [class UserEvent],

ProxyRepository.prepareClasses classToPrepare [class UserEvent]

ProxyRepository.prepareClasses namespacesToProxy 

ProxyRepository.prepareClasses proxy mockolate.generated:UserEventD88B6B79B0FD8F435ED6E256A55B83AFEBF5F500

ProxyGenerator.addSuperClassMembers dynamicClass addSuper UserEventD88B6B79B0FD8F435ED6E256A55B83AFEBF5F500 

ProxyGenerator.addSuperClassMembers superClass UserEvent

ProxyGenerator.addSuperClassMembers method  clone true

ProxyGenerator.addSuperClassMembers property  usersCollection true -1

ProxyGenerator.addSuperClassMembers property  faultMessage true -1

ProxyGenerator.addSuperClassMembers superClass Event

ProxyGenerator.addSuperClassMembers method  stopPropagation true

ProxyGenerator.addSuperClassMembers method  stopImmediatePropagation true

ProxyGenerator.addSuperClassMembers method  preventDefault true

ProxyGenerator.addSuperClassMembers method  isDefaultPrevented true

ProxyGenerator.addSuperClassMembers method  formatToString true

ProxyGenerator.addSuperClassMembers method  toString true

ProxyGenerator.addSuperClassMembers method  clone false

ProxyGenerator.addSuperClassMembers property  currentTarget true -1

ProxyGenerator.addSuperClassMembers property  eventPhase true -1

ProxyGenerator.addSuperClassMembers property  type true -1

ProxyGenerator.addSuperClassMembers property  bubbles true -1

ProxyGenerator.addSuperClassMembers property  cancelable true -1

ProxyGenerator.addSuperClassMembers property  target true -1

[SWF] FlexUnitApplication.swf/[[DYNAMIC]]/1 - 5,344 bytes after decompression

ProxyRepository.prepareClasses loaded item [class UserEvent],,mockolate.generated:UserEventD88B6B79B0FD8F435ED6E256A55B83AFEBF5F500

ProxyRepository.prepareClasses loaded mockolate.generated::UserEventD88B6B79B0FD8F435ED6E256A55B83AFEBF5F500

ProxyRepository.prepareClasses loaded [class UserEvent] [class UserEventD88B6B79B0FD8F435ED6E256A55B83AFEBF5F500]

FloxyMockolateFactory prepareClassRecipes prepared [class UserEvent] [class UserEventD88B6B79B0FD8F435ED6E256A55B83AFEBF5F500]

FloxyMockolateFactory prepareClassRecipes classRecipe [ClassRecipe classToPrepare=[class UserEvent] namespacesToProxy=[] proxyClass=null]

FloxyMockolateFactory prepareClassRecipes complete [class UserEvent],,mockolate.generated:UserEventD88B6B79B0FD8F435ED6E256A55B83AFEBF5F500

ProxyRepository.createWithProxyClass [class UserEventD88B6B79B0FD8F435ED6E256A55B83AFEBF5F500] ,,,,

TypeError: Error #2007: Parameter type must be non-null.

at flash.events::Event/ctor()

at flash.events::Event()

at com.fitmetrix.events::UserEvent()[/crypt/myApp/src/com/myapp/events/UserEvent.as:40]

at mockolate.generated::UserEventD88B6B79B0FD8F435ED6E256A55B83AFEBF5F500()

at org.flemit.util::ClassUtility$/createClass6()[/Users/drew/Development/workspace-burrito/flemit/flemit/src/org/flemit/util/ClassUtility.as:66]

at Function/http://adobe.com/AS3/2006/builtin::apply()

at org.flemit.util::ClassUtility$/createClass()[/Users/drew/Development/workspace-burrito/flemit/flemit/src/org/flemit/util/ClassUtility.as:57]

at org.floxy::ProxyRepository/createWithProxyClass()[/Users/drew/Development/workspace-burrito/floxy/floxy/src/org/floxy/ProxyRepository.as:94]

at mockolate.ingredients.floxy::FloxyMockolateFactory/createInstance()[/Users/abourne/Workspaces/drewbourne/mockolate/mockolate/src/mockolate/ingredients/floxy/FloxyMockolateFactory.as:122]

at mockolate.ingredients.floxy::FloxyMockolateFactory/prepareInstance()[/Users/abourne/Workspaces/drewbourne/mockolate/mockolate/src/mockolate/ingredients/floxy/FloxyMockolateFactory.as:92]

at global/asx.array::forEach()[/Users/drew/Development/workspace-oss-/asx/asx/src/asx/array/forEach.as:12]

at mockolate.ingredients.floxy::FloxyMockolateFactory/prepareInstances()[/Users/abourne/Workspaces/drewbourne/mockolate/mockolate/src/mockolate/ingredients/floxy/FloxyMockolateFactory.as:80]

at mockolate.ingredients::Mockolatier/prepareInstances()[/Users/abourne/Workspaces/drewbourne/mockolate/mockolate/src/mockolate/ingredients/Mockolatier.as:193]

at mockolate.runner.statements::InjectMockInstances/evaluate()[/Users/abourne/Workspaces/drewbourne/mockolate/mockolate/src/mockolate/runner/statements/InjectMockInstances.as:49]

at org.flexunit.internals.runners.statements::StatementSequencer/executeStep()[E:\hudson\jobs\FlexUnit4-Flex4.1\workspace\FlexUnit4\src\org\flexunit\internals\runners\statements\StatementSequencer.as:98]

at org.flexunit.internals.runners.statements::StatementSequencer/handleChildExecuteComplete()[E:\hudson\jobs\FlexUnit4-Flex4.1\workspace\FlexUnit4\src\org\flexunit\internals\runners\statements\StatementSequencer.as:141]

at org.flexunit.token::AsyncTestToken/sendResult()[E:\hudson\jobs\FlexUnit4-Flex4.1\workspace\FlexUnit4\src\org\flexunit\token\AsyncTestToken.as:107]

at Function/<anonymous>()[/Users/abourne/Workspaces/drewbourne/mockolate/mockolate/src/mockolate/runner/statements/PrepareMockClasses.as:34]

at flash.events::EventDispatcher/dispatchEventFunction()

at flash.events::EventDispatcher/dispatchEvent()

at Function/http://adobe.com/AS3/2006/builtin::apply()

at SetIntervalTimer/onTimer()

at flash.utils::Timer/_timerDispatch()

at flash.utils::Timer/tick()



Roland Zwaga

unread,
Oct 11, 2011, 3:26:38 AM10/11/11
to mock...@googlegroups.com
Hey there,

I believe the problem is that Mockolate by default passes in NULL as a
default for constructor arguments. As the stack dump indicates:


TypeError: Error #2007: Parameter type must be non-null.

The event class doesn't agree with this...

I guess if you declare your mock like this:

[Mock(inject="false")]
public var instanceEvent:UserEvent;

And in your setUp() method instantiate it like this:
instanceEvent = stub(UserEvent,"instanceEvent", "eventType", ... other
constructor args);

That will prevent mockolate from instantiating your class by default
and letting you create it with valid ctor args at a later stage.

hope that helps (and I hope it's correct what I'm claiming here hehe)


cheers,

Roland Zwaga

> Hey Drew,
>
> I'm kinda stumped on this one. I thought it should be a simple matter of
> creating a stub for the injected event but it appears that I am missing
> something.
> I have a command which injects an event:
>

> [*Inject*]
> *public* *var* event:UserEvent;
>
> *override* *public* *function* execute():*void*
>
> {
>
> *if* ( event.usersCollection ) {
>
> *if* ( !event.usersCollection.length ) {
>
> eventDispatcher.dispatchEvent ( *new* ViewEvent (
> ViewEvent.SET_VIEW_STATE, *false*, *false*, StateConstants.NEW_USER ) );
>
> } *else* *if* ( event.usersCollection.length ) {
>
> eventDispatcher.dispatchEvent ( *new* ViewEvent (
> ViewEvent.SET_VIEW_STATE, *false*, *false*, StateConstants.SELECT_USER ) );


>
> }
>
> }
>
> }
>
> I am testing the individual conditions of the command. Here is my setup:
>

> [*Rule*]
> *public* *var* rule:MockolateRule = *new* MockolateRule();
>
> [*Mock*]
> *public* *var* instanceEvent:UserEvent;
>
> *private* *var* instanceCommand:ViewStateDecisionCommand;
>
> *private* *var* collection:ArrayCollection;
>
> [*Before*]
> *public* *function* setUp():*void*
>
> {
>
> collection = *new* ArrayCollection();
>
> instanceCommand = *new* ViewStateDecisionCommand();
>
> instanceCommand.eventDispatcher = *new* EventDispatcher();
>
> }
>
> [*After*]
>
> *public* *function* tearDown():*void*
>
> {
>
> instanceCommand = *null*;


>
> }
>
> I don't even make it into my test method before I get the following runtime
> exception (I've highlighted the actual error below, Line 40 is actually the

> event's call to *super*(type, bubbles, cancelable);):

dougrdotnet

unread,
Oct 11, 2011, 7:18:44 AM10/11/11
to mock...@googlegroups.com
Hi Roland!

Thanks so much for your help.  Your idea made total sense, however, it unfortunately didn't resolve the issue.
I ended up removing the Rule and Mock altogether and instantiated the event directly into the command in the test, which resolved the problem.
On the downside, I don't understand why your solution didn't work, If inject="false" prepares the class only and does not create the prepared Mockolate then prepare has to be needing additional constructor arguments in this case.  But even if I provide the required type argument in the Mock declaration:

[Mock ( inject="false")]
public var instanceEvent:UserEvent = new UserEvent ( UserEvent.GET_USERS_RESULT );

I still get the same error.  So, I have to ask, can events be stubbed with a required type constructor argument?
Even if that did work, It wouldn't be desirable for me in my case as I have several types that I wish to test in this Test class.

[Test]

public function testInstanceDispatchesEvent_dispatchesEvent_instanceCommandDispatchesViewEvent():void{

collection = new ArrayCollection();

instanceCommand.event = new UserEvent ( UserEvent.GET_USERS_RESULT, false, false, collection );

var count:Number = 0;

instanceCommand.eventDispatcher.addEventListener(ViewEvent.SET_VIEW_STATE, function ( event : ViewEvent ) : Number { return count++; } );

instanceCommand.execute();

assertEquals(1,count);

}


Roland Zwaga

unread,
Oct 11, 2011, 8:27:29 AM10/11/11
to mock...@googlegroups.com
Hey Doug,

this might be either me misunderstanding the documentation, or perhaps
this is a bug. From what I understand adding the inject=false argument
prevents Mockolate from pre-instantiating the class.

Drew, are you able to shed some light on this?

cheers,

Roland Zwaga

> Hi Roland!
>
> Thanks so much for your help. Your idea made total sense, however, it
> unfortunately didn't resolve the issue.
> I ended up removing the Rule and Mock altogether and instantiated the event
> directly into the command in the test, which resolved the problem.
> On the downside, I don't understand why your solution didn't work, If
> inject="false" prepares the class only and does not create the prepared
> Mockolate then prepare has to be needing additional constructor arguments in
> this case. But even if I provide the required type argument in the Mock
> declaration:
>

> [*Mock *( inject=*"false"*)]
> *public* *var* instanceEvent:UserEvent = *new* UserEvent (


> UserEvent.GET_USERS_RESULT );
>
> I still get the same error. So, I have to ask, can events be stubbed with a
> required type constructor argument?
> Even if that did work, It wouldn't be desirable for me in my case as I have
> several types that I wish to test in this Test class.
>

> [*Test*]
>
> *public*
> *function*testInstanceDispatchesEvent_dispatchesEvent_instanceCommandDispatchesViewEvent():


> *void*{
>
> collection = *new* ArrayCollection();
>

> instanceCommand.event = *new* UserEvent ( UserEvent.GET_USERS_RESULT, *
> false*, *false*, collection );
>
> *var* count:Number = 0;
>
> instanceCommand.eventDispatcher.addEventListener(ViewEvent.SET_VIEW_STATE,
> *function* ( event : ViewEvent ) : Number { *return* count++; } );
>
> instanceCommand.execute();
>
> assertEquals(1,count);
>
> }
>
>

Drew Bourne

unread,
Oct 11, 2011, 8:28:28 AM10/11/11
to mock...@googlegroups.com
Doug, 

Can you elaborate on why you want to mock simple objects such as Events? 

Typically using a normal instance of an Event is recommended as they should not have any complex behaviour that requires mocking or verifying. 

As for mocking objects with required constructor arguments you can do so like the examples in this gist: https://gist.github.com/1277943

cheers, 
Drew

dougrdotnet

unread,
Oct 11, 2011, 8:41:13 AM10/11/11
to mock...@googlegroups.com
Drew,

Mainly because I am experimenting with the framework and working to understand, document, and share what Mockolate can do for me in my tests.
In this case, I am not trying to Mock the event, as it is not under test - I just wanted it to fulfill the dependency of the CUT as a stub.
So given that I've got this framework implementation, why not provide a Mockolate stub rather than a direct instantiation?  I wouldn't have to make decisions about what or what not to stub, I would just have a workflow.
The question keeps coming up, "Should I stub this dependency, or provide it as a direct instantiation?"

Doug

dougrdotnet

unread,
Oct 11, 2011, 8:48:19 AM10/11/11
to mock...@googlegroups.com
Thanks for the gist example!!
Very cool, I like how you can create Mockolates for each event type using that approach and consolidate their asserts into a single related test case.

Stray

unread,
Oct 11, 2011, 9:03:00 AM10/11/11
to mock...@googlegroups.com
Hi Doug,

with events and VOs, I have a script that makes an extending class "SomeEventSupport" which automatically stubs its own values (unless provided). This isolates the test from the constructor of the event. I wouldn't go so far as to mock an event as there's no behaviour to stub / verify, and mocking with values provided for the constructor doesn't help you as you're not isolated from the constructor args for that class.

So - "direct instantiation vs mockolate stub" misses the 3rd option - a support class that populates its own constructor args. I usually pass a single property (a uint) that I use to make the other properties unique - eg passing 3 to an item with a title would give it "Title3" as that property.

For types you can either isolate that by having more than one support class per type, or using the constant itself.

Stray

Drew Bourne

unread,
Oct 11, 2011, 9:37:32 AM10/11/11
to mock...@googlegroups.com
That reminds me you could also use [Mock(type="partial", args="eventArgs")] which will inject an instance using the constructor args AND behave like the regular event unless you set Expectations for it. 

cheers, 
Drew

dougrdotnet

unread,
Oct 11, 2011, 12:11:37 PM10/11/11
to mock...@googlegroups.com
Drew,

Ya, that worked - here is my setup:

[Rule]
public var rule:MockolateRule = new MockolateRule();

[Mock ( type = "partial", args = "userEventArgs" )]
public var instanceEvent:UserEvent;

public var userEventArgs:Array = [UserEvent.GET_USERS_RESULT, false, false, collection];

private var instanceCommand:ViewStateDecisionCommand;

private var collection:ArrayCollection;

[Before]
public function setUp():void
{
collection = new ArrayCollection();


instanceCommand = new ViewStateDecisionCommand();
instanceCommand.eventDispatcher = new EventDispatcher();
}

And then in the test I needed to set the userCollection argument to the collection AC, stub the instanceEvent, and assign the instanceCommand.event property to the stub:

[Test]
public function testInstanceDispatchesEvent_dispatchesEvent_instanceCommandDispatchesViewEvent():void{

instanceEvent.usersCollection = collection;
stub(instanceEvent);
instanceCommand.event = instanceEvent;

Reply all
Reply to author
Forward
0 new messages