Thanks Matt!
Here's a real-world example of where the fixed ordering of the
addAsync calls just bit me:
I'm testing what is essentially an HTTPService, that could throw a
result or a fault. I instinctively wrote the following:
public function testChangeUser():void {
// cp is my service object that throws results and faults
var tempfunc:Function = addAsync(handleChangeUserPart1,TIMEOUT);
cp.faultCallback = addAsync(
handleUnexpectedFault,
TIMEOUT,
"testChangeUserPart1",
onNotCalledIgnore);
// NOTE: I had to use a temporary function because I don't expect
// the fault handler to be called, but the error must be caught if
// it is
cp.addUser("userToBeAdded",tempfunc);
}
private function handleChangeUserPart1(data:ResultEvent):void {
assertEquals("true",data.[EMAIL PROTECTED]);
// ok, user has been added, verify that I can change the username
var tempfunc:Function = addAsync(handleChangeUserPart2,TIMEOUT);
cp.faultCallback = addAsync(
handleUnexpectedFault,
TIMEOUT,
"testChangeUserPart2",
onNotCalledIgnore);
cp.changeUserName("oldname","newname",tempfunc);
}
private function handleChangeUserPart2(data:ResultEvent):void {
assertEquals("true",data.[EMAIL PROTECTED]anged);
}
private function handleUnexpectedFault(e:*,d:*=null):void
{fail("blah");}
private function onNotCalledIgnore(d:*):void {}
The above seems pretty straightforward. I add the user, when the
response comes back and I confirm that the user was added, I change
the username and then confirm that the username was changed.
However, it doesn't work like that. Because I have a second call to
addAsync() in testChangeUser, the Unit Test framework expects that
the next async callback to come is my fault event, which is not the
case -- my next event is the receipt of my call to change the
username. I don't care if it gets skipped nor do I want to require
that it gets called. Thus, my testcase errors out because of
ordering.
I can't remove the call or reference to addAsync(myFaultCallback)
because I need it to handle faults....
I haven't been able to come up with a workaround for this case --
any suggestions?
Matt, I'll gladly try to help fix these issues, but I need a bit
more direction on where to look (ok... I could figure it out, but
I'm not that zealous nor do I have quite that much time ;).
BTW, I have wondered many times if FlexUnit would work better if the
whole thing was structured on async events. Of course, that would
require a major re-work, but it seems a lot more natural given the
number of async events that need to be dealt with in Flex....
Thanks for the help everyone.
--Kaleb
--- In [EMAIL PROTECTED]ups.com,
"Matt Chotin" <[EMAIL PROTECTED]>
wrote:
>
> Doing a while loop won't work, if it did we wouldn't have all the
> complications of this system :-) Your idea of #1 is exactly
right,
> setup now needs to become more like the async calls.
>
>
>
> Sorry, I haven't looked at this code in a year, you'll need to
muddle
> through but it should be doable.
>
>
>
> Matt
>
>
>
> ________________________________
>
> From: [EMAIL PROTECTED]ups.com
[mailto:[EMAIL PROTECTED]ups.com]
On
> Behalf Of kaleb_pederson
> Sent: Tuesday, August 22, 2006 12:19 PM
> To: [EMAIL PROTECTED]ups.com
> Subject: [flexcoders] RE: Asynchronous TestCase.setUp in FlexUnit
(and
> another use case)
>
>
>
> Matt and all,
>
> I took a quick stab at this as it didn't look too bad, but
> unfortunately, it isn't quite that simple. FlexUnit is written
such
> that it expects a number of things to happen synchronously, which
> causes some other problems in this case.
>
> Let me explain. After modifying TestCase::runMiddle() to wait for
a
> flag indicating that the testcase setup has completed, runMiddle
> fires off a timer and waits for the flag to change. At this point,
> runMiddle, as called by ProtectedMiddleTestCase::protect(),
returns
> and the next TestCase is fired off. In the end, this results in
the
> setup function being called many times before the test cases
> actually fire off (as the test cases are now waiting for the flag
to
> change) and many test functions being called in parallel when the
> flag changes.
>
> There are a couple of different ways that seem like obvious
> solutions:
>
> 1) Change the setup process so that it worked kind of like the
async
> calls as handled by addAsync.
> 2) Instead of allowing runMiddle to return (as its synchronous),
put
> it in a while loop waiting for the flag to change [!BAD HACK!]
>
> I don't fully understand the interactions between all the
different
> classes and objects at this point, so I'm not sure if there is an
> easy way to get to a reasonable solution, so I'm asking for
> feedback. Is there an easier way to make this work correctly?
> Other suggestions?
>
> Another sample use case:
>
> Unfortunately, when callbacks are registered with addAsync(), the
> order in which the calls are made imposes a restriction on the
order
> in which the callbacks can return.
>
> For example, assume my test requires two different XML files
loaded
> from externally:
>
> public function testValidateExternalRefs():void {
> var loader1:URLLoader = new URLLoader();
> var loader2:URLLoader = new URLLoader();
> loader1.addEventListener(Event.COoverride
> MPLETE,addAsync(onComplete1,TIMEOUT));
>
>
loader2.addEventListener(Event.COMPLETE,addAsync(onComplete2,TIMEOUT));
>
> loader1.load(new URLRequest(myUrl1));
> loader2.load(new URLRequest(myUrl2));
> }
>
> The use case above presents a problem because I cannot guarantee
> which response is going to come back first. Admittedly, in this
> case I could load the files in the setUp call (or chain a bunch of
> events so that the first one has loaded, and then the second one
has
> loaded, and so on and so forth). The problem that I'm running into
> is because of the required strict ordering of asynchronous
> responses, I'm having to chain a lot of events for something that
> could be quite simple. Although the above use case is fabricated,
I
> hope it shows the problem.
>
> Does anybody have some suggestions on how I might work around
these
> issues? I'll gladly modify code if I need to -- I'm just trying to
> avoid having to understand all the code from the ground up to come
> up with a solution.
>
> Thanks for the help.
>
> --Kaleb
>
> -----Original Message-----
> From: Matt Chotin [EMAIL PROTECTED]
> Sent: Mon, 06 Mar 2006 22:40:46 -0800
> Subject: [flexcoders] Asynchronous TestCase.setUp in FlexUnit
>
> My thought is that this is a good use-case and you should look
into
> proposing how a class should indicate asynchronous setup and
> teardown
> and then go ahead and implement. Then submit as a proposal :-)
>
> One idea is that we could add a variable called ready to the
> TestCase
> that is by default true. We would then adjust the runMiddle()
> method to
> check the ready variable and if it is true go ahead and call
> runTestOrAsync. If it is false it simply would wait and call
itself
> again a little later. Then in your setup you set ready to false,
> and
> when your data loads you set ready to true.
>
> I haven't thought it all the way through, but something like this
> could
> work. Check the source out and maybe override some of those
methods
> in
> your own testcase to see how it works out.
>
> Matt
>
> -----Original Message-----
> From: bringrags [EMAIL PROTECTED]
> Sent: Monday, March 06, 2006 2:02 PM
> Subject: [flexcoders] Asynchronous TestCase.setUp in FlexUnit
>
> Hi,
>
> I've been using FlexUnit with one of my projects and ran into a
> problem. In one of my test cases, I want to load a bunch of data
> from
> an XML file in the TestCase.setUp() method, and have all of the
> individual tests use this as a fixture.
>
> The problem is that loading a file is an asynchronous operation,
but
> there's no mechanism to make setUp asynchronous. Consider the
> following excerpt:
>
> public function setUp():void
> {
> var loader:URLLoader = new URLLoader();
> loader.addEventListener(Event.COMPLETE,onComplete);
> loader.load(new URLRequest("myfile.xml"));
> }
>
> private function onComplete(event:Event):void
> {
> var loader:URLLoader = URLLoader(event.target);
> myXML = new XML(loader.data);
> }
>
> public function testSomething():void
> {
> assertTrue("Should pass", myXML.someElement = "some
value");
> }
>
> The problem is that testSomething() gets called before
onComplete().
> Ideally I would like to prevent the tests from running until the
> onComplete() method is called.
>
> FlexUnit provides support for having a chain of asynchronous
tests,
> but it doesn't seem to provide support for chaining asynchronous
> setup
> tasks. One possible workaround is to have each test method
> explicitly
> prepare the setup fixture:
>
> public function testSomething():void
> {
> beginTestSetup(testSomethingHandler);
> }
>
> private function testSomethingHandler(event:Event):void
> {
> endTestSetup(event);
>
> assertTrue("Should pass", xml.someElement = "some
value");
> }
>
> private function beginTestSetup(eventHandler:Function):void
> {
> var loader:URLLoader = new URLLoader();
>
>
loader.addEventListener(Event.COMPLETE,addAsync(eventHandler,1000));
> loader.load(new URLRequest("myfile.xml"));
> }
>
> private function endTestSetup(event:Event):void
> {
> var loader:URLLoader = URLLoader(event.target);
> myXML = new XML(loader.data);
> }
>
> But this is less than ideal. Any thoughts?
>
> -- Brian
>