FlexUnit: Spark component test issue (UIImpersonator) - apache-flex

FlexUnit 4.1
FlashBuilder 4.5.1
BACKGROUND
I'm trying to test a custom Flex 4 skinnable component, using the FlexUnit UIImpersonator class. If I run my tests from a FlashBuilder Spark only project everything works fine. If I try to test from a project with the mx component set on the classpath I get a "getElementIndex not available in non Flex 4 projects" error.
QUESTION
Can I unit test spark components in FlexUnits visual test environment while still having the mx component set on the classpath?
RESEARCH
UIImpersonator delegates it's method calls to a "testEnvironment".
The implementation used for this "testEnvironment" is decided by the VisualTestEnvironmentBuilder class and the FlexEnvironmentBuilder class. If the FlexEnvironmentBuilder class can find the "mx.core.Container" on the classpath it returns a MX environment, else a Spark environment. Only the spark environment has valid implementations for Flex 4 relevant method calls on the UIImpersonator – like the addElement method.

I have the same problem, and didn't found any solution. I suggest to file a bug, but since FlexUnit is in the process of being adopted by Apache Flex, I don't think it would be resolved anytime soon.
As a workaround. Just use UIImpersonator.addChild() and add a reference to mx.core.Container in your TestRunner. This ensures that a MX container is used as testEnvironment and you won't receive a 'getElementIndex...' error.
import mx.core.Container
public function runTests():void {
// reference to mx container
var containerRef:Container = new Container();
// Run your testsuite as usual. eg:
var core:FlexUnitCore = new FlexUnitCore();
core.run(MyTestSuite);
}

Related

How can I reuse containers during integration tests with quarkus?

I currently have a few integration tests that are running fine, however I'm using the annotation #QuarkusTestResource to launch containers via testcontainers before quarkus is launched and I adapt my properties to the random test container port by overriding the start method of my container class extending QuarkusTestResourceLifecycleManager.
To optimize a little bit I'd like for example to reuse my container that launches kafka since it takes 6-8 seconds to start it and reuse it for several of my integration tests. I've not been able to do so. Since Quarkus manages the lifecycle every times it stops, between every test class, it also stops every container. I tried mixing singleton containers from testcontainers with the Quarkus test resources but it doesn't work. Here's a snippet of the start of one of my integration test class :
#QuarkusTest
#Tag(INTEGRATION_TEST)
#QuarkusTestResource(value = KafkaNode.class, restrictToAnnotatedClass = true, parallel = true)
class MyIntegrationTest { ... }
And my KafkaNode class :
public class KafkaNode implements QuarkusTestResourceLifecycleManager {
static KafkaContainer kafka = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:6.2.1"))
.withNetworkAliases("kafkaNode")
.withNetwork(CommonNetwork.getInstance());
#Override
public Map<String, String> start() {
kafka.start();
return Collections.singletonMap("myapp.kafka.servers",
kafka.getBootstrapServers());
}
#Override
public void stop() {
kafka.close();
}
}
I'm open to ideas to reuse of even rework my tests so that I can reuse containers in another way.
You can use the experimental reusable mode for your container via .withReuse(true).
Please note that in order for this to work you need to:
set the testcontainers.reuse.enable=true property in the ~/.testcontainers.properties file
NOT call .close() (which programmatically stops the container in any way)
Once you've setup everything accordingly your container should
not be stopped/garbage collected at the end of the test run anymore
be picked up again at the next test run - avoiding a new container start
Please note that all runtime modifications (configuration, data, topics etc.) will still be there at the next test run. You need to take suitable measures to avoid non-deterministic tests - like using different / random topic names for each test run.
Also note that you'll need to manually stop/delete the container when you're done with it. Testcontainers will not remove the container for you anymore.
You can also read this article that describes how to do it from spring for inspiration: Reuse Containers With Testcontainers for Fast Integration Test
When using multiple QuarkusTestProfile's, the test context will be reseted completely, so that even static variables are not preserved.
I have not yet found a way to use e.g. a single database across all QuarkusTest's in one test when using different QuarkusTestProfiles.
Even if it seems that the container exists across test run boundaries, I can't reference it in later test runs to determine the mapped port, etc.

Flex conditional compilation-Is it possible to have conditional compilation in flex library project?

Using Flex, created a desktop and web application in that used conditional compilation. It runs successfully. Now, I Would like to have the single swc file for both Desktop and web. So created the library project for satisfying that condition. While using conditional compilation in flex library project getting many issues like conflicts variable name and duplicate functions and so on, which I haven't faced while using flex projects without swc file.
So the question arises now: Is it possible to have conditional compilation on a flex library project?
When compiling your SWC, you can specify compile constants by passing them in a -define argument. This will however, only include whatever code you've added using that constant - i.e. you can't reset the const in a project that includes the SWC to get a different result.
Below is the code for a bat file for creating an SWC. Copy it into a new file, and save it with the extension .bat. Replace the file paths as necessary.
#echo off
set flexroot=D:\Program Files\FlashDevelop\Tools\flexsdk\
set proj=D:\Dev\TestSWC\
cd %flexroot%bin\
compc.exe -source-path %proj%src -is %proj%src -optimize -define CONFIG::debug false -define CONFIG::release true -output %proj%bin\TestSWC.swc
pause
I used this to build a SWC file containing a single class, like so:
package
{
public class TestClass
{
public function sayHello():void
{
CONFIG::debug
{
trace( "Hello debug" );
}
CONFIG::release
{
trace( "Hello release" );
}
}
}
}
I then created another project, included the SWC and set the CONFIG::debug flag to true, and called the sayHello() function. It traced "Hello release" as the SWC was compiled with the CONFIG::release flag as true.
Quoting the docs:
The mxmlc compiler lets you pass the values of constants to the application at compile time
It does not mention compc, so I'm pretty sure it is not possible. I also wonder how that would work technically since you include/exclude parts of the code during compilation. The compiled SWC would no longer contain the conditional code.

UIImpersonator.addChild() doesn't dispatch the right events

I am running FlexUnit tests through Ant. The test test1 fails with the message "Timeout Occurred before expected event" but test2 passes. The only difference between the two tests is that one uses UIImpersonator.addChild() whereas the other uses FlexGlobals.topLevelApplication.addElement().
test1 fails even if I listen for "addedToStage" event. Listening for "added" event, however, makes test1 pass.
[Test(async, ui, description="Fails")]
public function test1():void
{
var c:UIComponent = new UIComponent;
Async.proceedOnEvent(this, c, FlexEvent.CREATION_COMPLETE);
UIImpersonator.addChild(c);
}
[Test(async, ui, description="Passes")]
public function test2():void
{
var c:UIComponent = new UIComponent;
Async.proceedOnEvent(this, c, FlexEvent.CREATION_COMPLETE);
FlexGlobals.topLevelApplication.addElement(c);
}
when adding a child, it will not initiate the flex component lifecycle, because displayobject is flash core element, not flex.
assuming Flex4/spark. addChild adds a MovieClip, not a Flex UIElement, it doesn't even know FlexEvent type as it is a flash.core object. It will only throw addedToStage or added events (Event.added), but in unit test, it is not added to stage, because UIImpersonator is not part of the stage
I ran into this same issue today using the new Apache FlexUnit 4.2.0 release.
When trying to run the sampleCIProject included in the binary distribution, none of the tests that used Async would succeed. The exception was exactly as described above.
After looking at the source code for a while, I noticed that the core FlexUnit libraries have two flavours: flexunit-4.2.0-20140410-as3_4.12.0.swc and flexunit-4.2.0-20140410-flex_4.12.0.swc.
The first of these is intended for pure AS3 projects, while the latter is intended for projects that use Flex. The sampleCIProject included both of these libraries in the library path, and I'm assuming that it was using the UIImpersonator class from the pure AS3 library rather than the Flex-supporting one. I removed flexunit-4.2.0-20140410-as3_4.12.0.swc from the project, and lo and behold, the Async tests started working again.
Probably a bit late for you, but I hope this helps someone else.

Flex 3 to Flex 4 warning

Hello i'm migration flex 3 to flex 4 and i have 1 warning and i try to fix it but nothing work.
The warning is: data binding will not be able to detect assignments to toplevelapplication
In flex 3 i had application.Application and now i have FlexGlobals.topLevelApplication
I have 1400 FlexGlobals.topLevelApplication and +- 150 .AS files.
I tried to use Application(FlexGlobals.topLevelApplication), mainAPP(FlexGlobals.topLevelApplication), and nothing works, The only think that clean the warning is if i add [Bindable] public var myApplication:Object = FlexGlobals.topLevelApplication; to all the .AS files but when i run the project and try to log in the application i have Action Script Errors
Error #1009: Cannot access a property or method of a null object reference.
You're right. The clean way to access the Application is via FlexGlobals.topLevelApplication.
[Bindable]
public var app:mainApp = mainApp(FlexGlobals.topLevelApplication); // fail fast to find erros
Should to the work, if that is not the case, you seem to access that instance too early. You should bootstrap the Application (and move on from there) after FlexEvent.CREATION_COMPLETE has been dispatched. Also, i suggest that if you introduce a field in your classes like
private const app:mainApp = mainApp(FlexGlobals.topLevelApplication); // fail fast to find errors
protected final get app():mainApp { return app; }
to access the field cleanly in your application and avoid too many heavy dependencies to you application.
PS: Class names, even if suffixed .mxml should start with a capital letter.

Finding Display of an RCP App

I'm writing a testing Framework which is starting a GUI Application. To be able to test this GUI in the case of an SWT application I need to know it's display. In general, this display is loaded by another classloader, therefore I'm using the method findDisplay(Thread t) of the swt Display class by reflection to accomplish this task. My code looks something like this:
Thread[] threads = new Thread[10];
Thread.enumerate(threads);
Object foundObject = null;
for (Thread t : Arrays.asList(threads)){
foundObject = null;
Class<?> clazz = t.getContextClassLoader().loadClass("org.eclipse.swt.widgets.Display");
final Method method = clazz.getMethod("findDisplay", Thread.class);
foundObject = method.invoke(null, new Object[] {t});
if (foundObject != null) {
System.out.println("yeah, found it!");
break;
}
}
In my opinion this should find every Object of type Display in the current thread group. However I don't get any for the texteditor RCP example although the GUI is starting up perfectly.
Any ideas what is going wrong or how I can debug this in a reasonable way?
I figured out what the main problem was: The ContextClassloader had nothing to do with the classloader who actually loaded the classes.
To resolve my problem I took care of having the classloader which loads the swt display class both in the hierarchy of the RCP program and the hierarchy of my framework. This was possible by using the java extension classloader. (I couldn't use the application classloader since my RCP application doesn't work with it as parent, I haven't figured out yet why) It was then just a matter of adding the swt.jar to the java.ext.dirs property.
If you are using Eclipse RCP then maybe you can use:
PlatformUI.getWorkbench().getDisplay()

Resources