flexunit 4 for contributors

48
FlexUnit 4 for Contributors Michael Labriola Digital Primates twitter.com/mlabriola blogs.digitalprimates.net/codeSlinger/

Post on 12-Sep-2014

1.893 views

Category:

Technology


2 download

DESCRIPTION

Architecture overview of the FlexUnit 4 project

TRANSCRIPT

Page 1: FlexUnit 4 for contributors

FlexUnit 4for Contributors

Michael LabriolaDigital Primates

twitter.com/mlabriolablogs.digitalprimates.net/codeSlinger/

Page 2: FlexUnit 4 for contributors

Who are you?

Michael LabriolaFlexUnit 4 LeadSenior Consultant at Digital PrimatesFlex GeekTeam MentorFan of Working Code

Page 3: FlexUnit 4 for contributors

What is this session about?

FlexUnit 4 Architecture

This session is designed to give a crash course in the FlexUnit 4 architecture for those we hope will contribute to the project

Page 4: FlexUnit 4 for contributors

A few things to Note

FlexUnit 4 is an open-source project hosted by Adobe but driven by the community

FlexUnit 4 is named with the 4 suffix as it has approximate feature parity with JUnit 4. It can be used with any version of Flex

FlexUnit 4 can be compiled without Flex dependencies making it viable for AS only

projects as well

Page 5: FlexUnit 4 for contributors

Starts with a Test[Test]

public function testMe():void {

}

Test

Page 6: FlexUnit 4 for contributors

FlexUnit 4 Test Casepackage {

public class SomeTestCase {

[Test]

public function testMe():void {

}

}

}

Test Case

TestTest

Page 7: FlexUnit 4 for contributors

FlexUnit 4 Test Casepackage {

public class SomeTestCase {

[Test]

public function testOne():void {

}

[Test]

public function testTwo():void {

}

[Test]

public function testThree():void {

}

}

}

Test Case

TestTest TestTest

TestTest TestTest

Page 8: FlexUnit 4 for contributors

FlexUnit 4 Test Suitepackage {

[Suite]

[RunWith("org.flexunit.runners.Suite")]

public class SomeTestSuite {

public var someTestCase:SomeTestCase;

}

}

Test Suite

Test Case

TestTest TestTest

TestTest TestTest

Page 9: FlexUnit 4 for contributors

FlexUnit 4 Test Suitepackage {

[Suite]

[RunWith("org.flexunit.runners.Suite")]

public class SomeTestSuite {

public var someTestCase:SomeTestCase;

public var someTestCase2:SomeOtherTest2;

public var thirdTestCase:ThidTestsCase;

public var anotherSuite:AnotherTestSuite;

}

}

Test Suite

Test Case

TestTest TestTest

TestTest TestTest

Test Case

TestTest TestTest

TestTest TestTest

Test Case

TestTest TestTest

TestTest TestTest

Test Suite

Test CaseTest Case

Test CaseTest Case

Page 10: FlexUnit 4 for contributors

Flex Unit 4 Core core = new FlexUnitCore();

core.run( SomeTestSuite );

FlexUnitCoreFlexUnitCore

Test Suite

Test Case

TestTest TestTest

TestTest TestTest

Test Case

TestTest TestTest

TestTest TestTest

Test Case

TestTest TestTest

TestTest TestTest

Test Suite

Test CaseTest Case

Test CaseTest Case

Page 11: FlexUnit 4 for contributors

Flex Unit 4 CoreThe FlexUnit core is responsible for executing

objects that implement an IRequest interface

You can pass it an IRequest directly or, if you pass it another type of class, it will attempt to create a Request on your behalf

Page 12: FlexUnit 4 for contributors

Flex Unit 4 RequestFor right now, imagine a

request as an object that wraps your tests when they are presented to the core

Requests can be filtered and sorted to control the subset and order of tests executed

Request Runner:Suite

Test Suite

Test Case

TestTest TestTest

TestTest TestTest

Test Case

TestTest TestTest

TestTest TestTest

Test Case

TestTest TestTest

TestTest TestTest

Test Suite

Test CaseTest Case

Test CaseTest Case

Page 13: FlexUnit 4 for contributors

Flex Unit 4 CoreWhen creating your own

requests, you generally use the methods of the static Request class

Common methods include:

aClass()classes()runner()method()sortWith()filterWith()

Request.method( TestAssert, "testAssertTrue” )

Request.classes( TestAssert, TestAsynchronous)

.filterWith( someDescription )

.sortWith( mySortFunction );

Page 14: FlexUnit 4 for contributors

Flex Unit 4 CoreIf you pass FlexUnit 4’s core anything other than

an IRequest, the core uses these methods to generate a Request object before processing continues

Ultimately, FlexUnit4 runs 1 and only 1 request per run. So, if you pass it multiple classes, etc. these are all wrapped in a single Request before execution begins

Page 15: FlexUnit 4 for contributors

Flex Unit 4 RequestThe key property of the IRequest that the FlexUnit

core needs is the IRunner.

IRunner is an interface implemented by any object capable of executing a specific type of test

For example, there is a runner that understands the work needed to execute a suite. A separate runner understands how to execute test cases and methods.

Page 16: FlexUnit 4 for contributors

Flex Unit 4 RequestTo determine the correct

IRunner for the request, the Request object kicks off a recursive process of introspecting each class

Identification ProcessIntrospect the classIdentify the runner for the classIdentify any childrenRun this process on each child to

identify their correct runnerReturn the top level runner for

the Request

Page 17: FlexUnit 4 for contributors

The Correct RunnerFlexUnit 4 determines the

correct runner for each class using builders

Builders are objects responsible for building the correct runner for a class

We try multiple possible builders until a compatible one is identified

Default PossibilitiesIgnored BuilderMetaData BuilderSuite Method BuilderFlex Unit 1 BuilderFluint 1 BuilderFlexUnit 4 Builder

Page 18: FlexUnit 4 for contributors

Ignored BuilderLooks for the [Ignore] metadata on top of the class

If this metadata is found, it creates an instance of the IgnoredClassRunner, which simply knows how to report that this class has been ignored back to the core

Page 19: FlexUnit 4 for contributors

MetaData BuilderLooks for the [RunWith] metadata on top of the class

(remember our suite example?)

If this metadata is found and the runner specified can be found, the builder checks that it implements the IRunner interface. If so, it creates an instance of the specified runner to run this class

This provides a huge hook for extensibility

Page 20: FlexUnit 4 for contributors

Suite Method BuilderChecks if the given class specifies its tests with a static

suite() method

If this method is found, it creates an instance of the SuiteMethodBuilder which will later decipher the contained tests

Page 21: FlexUnit 4 for contributors

FlexUnit 1 BuilderChecks if the given class descends from

flexunit.framework.TestCase in the FlexUnit .9 library

If this is a subclass of TestCase, it creates an instance of the FlexUnit1ClassRunner.

Page 22: FlexUnit 4 for contributors

Fluint 1 BuilderChecks if the given class descends from

net.digitalprimates.fluint.tests.TestCase or net.digitalprimates.fluint.tests.TestSuite in the Fluint (presently 1.2) library

If this is a subclass of either fluint class, it creates an instance of the Flunit1ClassRunner.

Note, possibility is only checked *if* we are compiled for Flex compatibility. Fluint has Flex dependencies

Page 23: FlexUnit 4 for contributors

FlexUnit 4 BuilderA catch-all for any remaining classes not identified by

one of the previous builders

Creates an instance of the BlockFlexUnit4ClassRunner()

Page 24: FlexUnit 4 for contributors

CoreCore

Corrected View with Core

The core executes the runner contained within the Request.

In this example, the outer most runner would be a Suite class. It would then create a new Suite runner for each of the suites inside. Each of those suites would create a BlockRunner for their test cases and another Suite runner for their contained suite.. Etc.

Request Runner:Suite

Test Suite A

Test Case 1

TestTest TestTest

TestTest TestTest

Test Case 2

TestTest TestTest

TestTest TestTest

Test Case 3

TestTest TestTest

TestTest TestTest

Test Suite C

Test Case 7Test Case 7

Test Case 8Test Case 8

Test Suite B

Test Case 4

TestTest TestTest

TestTest TestTest

Test Case 5

TestTest TestTest

TestTest TestTest

Test Case 6

TestTest TestTest

TestTest TestTest

Test Suite D

Test Case 9Test Case 9

Test Case 10Test Case 10

Request.iRunner(Suite)

Request.iRunner(Suite)

Suite ASuite A Suite BSuite B

BlockRunner 1BlockRunner 1

BlockRunner 2BlockRunner 2

BlockRunner 3BlockRunner 3

Suite CSuite C

BlockRunner 7BlockRunner 7

BlockRunner 8BlockRunner 8

TestsTests

TestsTests

TestsTests

TestsTests

TestsTests

Page 25: FlexUnit 4 for contributors

OutputAs test run within the FlexUnit 4 runners, we need

some way to monitor progress

This is accomplished via two types of classes. An implementation of IRunNotifierAn implementation of IRunListener

Page 26: FlexUnit 4 for contributors

IRunNotifierIRunNotifiers are a type of class that FlexUnit 4 uses to

notify others of an event that occurred during testing. You can draw parallels with IEventDispatcher

IRunNotifiers are used by the IRunner classes to notify others of the following conditions:

TestRun: Started/FinishedTest: Started/Failed/Ignored/Finished/

AssumptionFailures

Page 27: FlexUnit 4 for contributors

IRunNotifierUnlike an event dispatcher IRunNotifiers have

limitations on what types of objects can listen to their events.

IRunNotifiers have addListener() and removeListener() methods that accept an IRunListener as an argument

Page 28: FlexUnit 4 for contributors

IRunListenerWhile there is generally only one IRunNotifier present

in the system at any given time, there can be multiple IRunListeners

IRunListeners listen to these messages from the notifier and optionally perform some action. Example of IRunNotifiers built into FlexUnit include TraceListener, TextListener, CIListener, XMLListener and others for FlashBuilder integration

Page 29: FlexUnit 4 for contributors

CoreCore

With ListenersRequest Runner:Suite

Test Suite A

Test Case 1

TestTest TestTest

TestTest TestTest

Test Case 2

TestTest TestTest

TestTest TestTest

Test Case 3

TestTest TestTest

TestTest TestTest

Test Suite C

Test Case 7Test Case 7

Test Case 8Test Case 8

Test Suite B

Test Case 4

TestTest TestTest

TestTest TestTest

Test Case 5

TestTest TestTest

TestTest TestTest

Test Case 6

TestTest TestTest

TestTest TestTest

Test Suite D

Test Case 9Test Case 9

Test Case 10Test Case 10

Request.iRunner(Suite)

Request.iRunner(Suite)

Suite ASuite A Suite BSuite B

BlockRunner 1BlockRunner 1

BlockRunner 2BlockRunner 2

BlockRunner 3BlockRunner 3

Suite CSuite C

BlockRunner 7BlockRunner 7

BlockRunner 8BlockRunner 8

TestsTests

TestsTests

TestsTests

TestsTests

TestsTests

UIListenerUIListener

XMLListenerXMLListener

CIListenerCIListener

RunNotifierRunNotifier

Update a UIUpdate a UI

Send Data to ServerSend Data to Server

Send Data to Flash BuilderSend Data to Flash Builder

Page 30: FlexUnit 4 for contributors

Running TestsThe BlockFlexUnit4ClassRunner is the heart of running

FlexUnit 4 tests.

The block runner is responsible for iterating through tests in a given class and executing them

While doing this it uses two very important concepts: recursive sequences and decoration

Page 31: FlexUnit 4 for contributors

Before/Test/AfterJust to be sure everyone is on the same page FlexUnit 4

has several pieces of Metadata which, when present, control the flow of test execution.

These pieces of metadata include BeforeClass, Before, Test, After and AfterClass

Multiple methods can be marked by any of these pieces of metadata

Page 32: FlexUnit 4 for contributors

Before/Test/AfterBeforeClass

Beforetest1

AfterBefore

test2After

AfterClass

[BeforeClass]

public static function meFirst():void {

}

[Before]

public function beforeEachTest():void {

}

[Test]

public function test1():void {

}

[Test]

public function test2():void {

}

[After]

public function afterEachTest():void {

}

[AfterClass]

public static function afterEverything():void {

}

Page 33: FlexUnit 4 for contributors

First SequenceBeforeClassActual Test ClassAfterClass

Sequences are managed by a class called a StatementSequencer internal to FlexUnit 4

Sequences are responsible for ensuring that step 1 finishes before step 2, etc.

The first sequence in this example is BeforeClass, ActualTestClass, AfterClass

Page 34: FlexUnit 4 for contributors

Second Sequencetest1test2

Sequences can also have entire additional sequence as steps

For example, in our last set of steps, we listed Actual Test Class as a step.

Running the Actual Test Class, however, means sequencing through each child to execute it

Page 35: FlexUnit 4 for contributors

Third SequenceBefore Sequence

test1After Sequence

Executing an actual test, requires that we run any methods marked Before, the test, then any methods marked After

However, multiple methods can actually be marked Before or After

So, in reality, the Before and After calls are actually sequences themselves with a test in the middle

Page 36: FlexUnit 4 for contributors

RealityBefore Class Sequence

BeforeClass 1..nTest Class Sequence

Test 1 SequenceBefore Sequence

Before 1..nTestAfter Sequence

After 1..nTest 2 Sequence

Before SequenceBefore 1..n

TestAfter Sequence

After 1..nAfter Class Sequence

AfterClass 1..n

Sequences (and the StatementSequencer) are so important because there are thousands of sequences in even a moderately complication test suite

In our simple two test example here, we use at least 10

Page 37: FlexUnit 4 for contributors

Why?The reason we go through all of this trouble with

sequencer is that we simply can’t run methods in order

We have two things going against us, frames and lack of threads.

If anything async has to happen… EVER.. We can no longer rely on method completion to act as an indication that we can move forward

Page 38: FlexUnit 4 for contributors

TokensTo handle some of this async nature, we use a

concept called an AsyncTestToken

A token is simply a small object that is passed around the testing system. Object A passes a token to Object B when Object B has work to do. When Object B is done with its work, it notifies Object A by calling a method on this token

Page 39: FlexUnit 4 for contributors

TokensThis allows every operation and every runner to

be potentially asynchronous. Further, it keeps very recursive items decoupled

It also has great implications for future support of multiple security domains, etc.

Page 40: FlexUnit 4 for contributors

DecorationSequencers are used for every operation that

might be potentially asynchronous

Decoration are the second concept I mentioned; Decorations are used to ‘wrap’ the invocation of a method with code that should be executed before or after the method, synchronously

Page 41: FlexUnit 4 for contributors

Wrappingvar statement:IAsyncStatement = methodInvoker( method, test );

statement = withPotentialAsync( method, test, statement );

statement = withPotentialTimeout( method, test, statement );

statement = possiblyExpectingExceptions( method, test, statement );

statement = withStackManagement( method, test, statement );

Stack Management

Expecting Exception

Potential Timeout

Potential Async

Method Invoker

Stack Management Expecting Exception Potential Timeout Potential Async Method Invoker Potential Async Potential Timeout Expecting ExceptionStack Management

Page 42: FlexUnit 4 for contributors

Why?This actually enhances performance and

extensibility. Each of these level independently checks the test to see if it is needed

If it is not, it acts as a noop. If the layer is needed, for example, we specify async, the logic for that layer is instantiated and enabled to ensure the test is handled properly

Page 43: FlexUnit 4 for contributors

Stack And FrameThe stack and frame management decorator

implements green threading to deal with flash frames

It tracks how much time in each frame we have used up. When we get close to the end of a frame, we defer the test until the next frame to ensure we never timeout or lock the player during testing

Page 44: FlexUnit 4 for contributors

ExceptionThe expecting exception decorator implements

logic to catch exceptions thrown during the test.

If the user indicates that they are expecting an exception then this code compares any thrown exception against the specified one. If they match, all is good. If they don’t or the test doesn’t throw an exception, then this decorator causes the test to fail

Page 45: FlexUnit 4 for contributors

Potential TimeoutThe potential timeout decorator implements a

timer to ensure the test fails if it exceeds the specified number of milliseconds.

Page 46: FlexUnit 4 for contributors

Potential AsyncThe potential async decorator creates the

infrastructure to monitor an asynchronous test.

As this infrastructure carries overhead, we only create it when the user specifics a given test is asynchronous

Page 47: FlexUnit 4 for contributors

Method InvokerThe method invoker actually executes the test

method and captures any errors that occur during the method invocation

Page 48: FlexUnit 4 for contributors

ResourcesFlex Unit 4!http://opensource.adobe.com/wiki/display/flexunit/FlexUnit

Labriola’s Bloghttp://blogs.digitalprimates.net/codeSlinger/

Follow us on twittermlabriola and flexunit