blob: 5d568310f3c9cae00c3578d3f7eab98c3a0a2128 [file] [log] [blame]
<!DOCTYPE html>
<!--[if IE 7 ]><html class="ie ie7" lang="en"><![endif]-->
<!--[if IE 8 ]><html class="ie ie8" lang="en"><![endif]-->
<!--[if (gte IE 9)|!(IE)]><!--><html lang="en"><!--<![endif]-->
<head>
<meta charset="utf-8">
<title>Apache Flex® - Unit 6 - Working with the Test Fixture</title>
<!-- For Mobiles -->
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link href='https://fonts.googleapis.com/css?family=Carrois+Gothic' rel='stylesheet' type='text/css'>
<!-- CSS -->
<link rel="stylesheet" type="text/css" href="/css/bootstrap.css">
<link rel="stylesheet" type="text/css" href="/css/fixed-width.css" id="layout">
<link rel="stylesheet" type="text/css" href="/css/style.css">
<!-- favicon -->
<link rel="icon" type="image/x-icon" href="/images/favicon.ico"/>
<link rel="apple-touch-icon-precomposed" href="/images/apple-touch-icon-60.png">
<link rel="apple-touch-icon-precomposed" sizes="76x76" href="/images/apple-touch-icon-76.png">
<link rel="apple-touch-icon-precomposed" sizes="120x120" href="/images/apple-touch-icon-120.png">
<link rel="apple-touch-icon-precomposed" sizes="152x152" href="/images/apple-touch-icon-152.png">
<!-- Java Script -->
<script src="/js/jquery.js"></script>
<script src="/js/custom.js"></script>
<script src="/js/selectnav.js"></script>
<script src="/js/flexslider.js"></script>
<script src="/js/twitter.js"></script>
<script src="/js/fancybox.js"></script>
<script src="/js/isotope.js"></script>
<script src="/js/bootstrap.js"></script>
<script src="/js/showcase.js"></script>
<!-- Google Analytics -->
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-37926454-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</head>
<body>
<!-- Wrapper Start -->
<div id="wrapper" class="container-fluid">
<!-- Header -->
<div class="ie-dropdown-fix" >
<!-- Header -->
<div class="row-fluid" id="header">
<!-- Logo -->
<div class="span5">
<a href="#"><img src="/images/logo_01_fullcolor-sm.png" alt="Apache Flex®" title="Apache Flex®" /></a>
</div>
<!-- Social / Contact -->
<div class="span3 pull-right">
<!-- Social Icons -->
<ul class="social-icons">
<li class="facebook"><a href="https://www.facebook.com/pages/Apache-Flex/174249699342648">Facebook</a></li>
<li class="twitter"><a href="http://twitter.com/ApacheFlex">Twitter</a></li>
<li class="linkedin"><a href="http://www.linkedin.com/groups/Apache-Flex-Developers-4296888">LinkedIn</a></li>
</ul>
<!-- Apache Logo -->
<a href="http://www.apache.org" id="contact-top"><img src="http://www.apache.org/images/feather-small.gif" title="An Apache Project" alt="An Apache Project" /> </a>
</div>
</div>
<!-- Header / End -->
<!-- Navigation -->
<div id="navigation" class="margintop">
<ul id="nav">
<li><a href="/index.html">Home</a></li>
<li><a href="#">About Flex</a>
<ul>
<li><a href="/about-whatis.html">What is Flex?</a></li>
<li><a href="/about-features.html">Features</a></li>
<li><a href="/tourdeflex/explorer.html">Tour de Flex</a></li>
<li><a href="/about-licensing.html">License &amp; Trademarks</a></li>
<li><a href="/about-people.html">The Team</a></li>
<li><a href="/about-history.html">Project History</a></li>
<li><a href="/about-assets.html">Logo and Assets</a></li>
</ul>
</li>
<li><a href="#">Community</a>
<ul>
<li><a href="/community-getinvolved.html">How to get involved</a></li>
<li><a href="/community-mailinglists.html">Mailing Lists</a></li>
<li><a href="/community-showcase.html">Flex Showcase</a></li>
<li><a href="/community-3rdparty.html">Third-Party</a></li>
<li><a href="https://cwiki.apache.org/confluence/display/FLEX/Apache+Flex+Wiki">Wiki <i class="icon-share icon-white"></i></a></li>
<li><a href="http://blogs.apache.org/flex/">Blog <i class="icon-share icon-white"></i></a></li>
</ul>
</li>
<li><a href="#">Development</a>
<ul>
<li><a href="/dev-faq.html">Developer FAQ</a></li>
<li><a href="/dev-sourcecode.html">Source Code</a></li>
<li><a href="https://issues.apache.org/jira/browse/FLEX">Bug-Tracker <i class="icon-share icon-white"></i></a></li>
</ul>
</li>
<li><a href="#">Documentation</a>
<ul>
<li><a href="/doc-getstarted.html">Getting Started</a></li>
<li><a href="/doc-videos.html">Videos</a></li>
<li><a href="/asdoc/">Flex ASDocs</a></li>
<li><a href="http://apacheflexbuild.cloudapp.net:8080/job/FlexJS_ASDoc_Example/lastSuccessfulBuild/artifact/examples/flexjs/ASDoc/bin/js-debug/index.html">FlexJS ASDocs</a></li>
<li><a href="http://help.adobe.com/en_US/flex/using/index.html">Documentation Reference (Old) <i class="icon-share icon-white"></i></a></li>
<li><a href="/flexunit/tutorial/">FlexUnit Tutorials</a></li>
<li><a href="/flexunit/asdoc/">FlexUnit ASDocs</a></li>
</ul>
</li>
<li><a href="#">About Apache</a>
<ul>
<li><a href="http://www.apache.org">The Apache Software Foundation Website <i class="icon-share icon-white"></i></a></li>
<li><a href="http://www.apache.org/foundation/contributing.html">Donations <i class="icon-share icon-white"></i></a></li>
<li><a href="http://www.apache.org/foundation/sponsorship.html">Sponsorship <i class="icon-share icon-white"></i></a></li>
<li><a href="http://www.apache.org/foundation/thanks.html">Thanks <i class="icon-share icon-white"></i></a></li>
</ul>
</li>
<li><a href="#" class="download">Download Flex</a>
<ul>
<li><a href="/installer.html">SDK Installer (For Application Developers)</a></li>
<li><a href="/download-source.html">SDK Source Code (For SDK Developers)</a></li>
<li><a href="/download-binaries.html">SDK Binaries (For SDK Developers)</a></li>
<li><a href="/download-flexjs.html">FlexJS 'beta' (For Application Developers)</a> </li>
<li><a href="/download-flexunit.html">FlexUnit (For Application Developers)</a> </li>
<li><a href="/download-blazeds.html">Blaze DS Source Code</a></li>
<li><a href="/download-tourdeflex.html">Tour De Flex Source Code</a></li>
<li><a href="/download-squiggly.html">Squiggly Spell Checker</a></li>
<li><a href="/download-utilities.html">Flex Utilities</a></li>
<li><a href="/download-archive.html">Previous Versions</a></li>`
</ul>
</li>
</ul>
</div>
<div class="clear"></div>
</div>
<!-- Navigation / End -->
<!-- Content -->
<div class="row-fluid">
<!-- Page Title -->
<div id="page-title">
<h2>Unit 6 - Working with the Test Fixture</h2>
</div>
<!-- Page Title / End -->
</div>
<div class="row-fluid">
<!-- Home Page Exception -->
<p><a href="../code/Unit6.zip"><img alt="Download" src="../images/DownloadIcon.png"/> Download Unit Project Files</a></p>
<p>Much like any significant amount of code that you develop, your tests will eventually begin to show signs of duplicate code. Fortunately, repeat operations can be factored into discrete methods allowing you to create an understandable test fixture. Instantiating objects, running methods, destroying objects, and loading data are all operations that can be factored into methods and classes with the help of FlexUnit 4.x features.</p>
<h3>Objectives:</h3>
<p>After completing this lesson, you should be able to:</p>
<ul>
<li>Use [Before] and [After] metadata to set up and tear down your test fixture</li>
<li>Use [BeforeClass] and [AfterClass] metadata to set up and tear down portions of the fixture that persist across tests and cases</li>
</ul>
<h3>Topics</h3>
<p>In this unit, you will learn about the following topics:</p>
<ul>
<li>Setting up a fixture</li>
<li>Refactoring tests to remove duplication</li>
<li>Understand Before as an indication of cohesion</li>
<li>The importance of cleaning up the fixture</li>
</ul>
<h2>Setting up the test fixture</h2>
<p>In the last few lessons you have briefly learned about the concept of a test fixture, which is the collection of all of the state required to run a test repeatedly. So far you have created that state in each test by creating objects and setting properties. Unfortunately, this leads to a fair amount of duplication when many tests share the same setup requirements.</p>
<p>FlexUnit offers several ways to factor this repeated setup out into new methods that can be run to create your fixture.</p>
<h3>Before methods</h3>
<p>FlexUnit allows developers to mark any method inside of a test case with a special metadata tag named <code>[Before]</code>. Methods marked with <code>[Before]</code> metadata run before each test method in the test case class.</p>
<ul>
<li>A class may contain any number of <code>[Before]</code> methods.</li>
<li>Unless an order is specified, the order of multiple methods marked with <code>[Before]</code> is indeterminate.</li>
<li>Methods must be public, accept no arguments and return void.</li>
<li>To use, simply decorate the method with <code>[Before]</code> metadata.</li>
</ul>
<pre><code>[Before]
public function runMeBeforeEveryTest():void {
}
</code></pre>
<h3>After methods</h3>
<p>The logical opposite of the <code>[Before]</code> metadata is the <code>[After]</code> metadata. Methods marked with <code>[After]</code> run after each test method in the test case class.</p>
<ul>
<li>A class may contain any number of <code>[After]</code> methods.</li>
<li>Unless an order is specified, the order of multiple methods marked with <code>[After]</code> is indeterminate.</li>
<li>Methods must be public, accept no arguments and return void.</li>
<li>To use, simply decorate the method with <code>[After]</code> metadata.</li>
</ul>
<pre><code>[After]
public function runMeAfterEveryTest():void {
}
</code></pre>
<h3>Order of Execution</h3>
<p>As indicated above Before and After methods run before and after every test. Therefore, the execution of a method with three tests is indicated below:</p>
<pre><code>[Before]
[Test1]
[After]
[Before]
[Test2]
[After]
[Before]
[Test3]
[After]
</code></pre>
<p>The setup of your test fixture occurs before each test to ensure the test runs in isolation from others in the test case.</p>
<h2>Refactoring to remove duplication</h2>
<p>The methods decorated with <code>[Before]</code> and <code>[After]</code> can be used to remove duplicate code from tests. Once extracted to an independent method, this duplicated code no longer needs to be maintained in each test but rather it can be maintained once for all tests in the test case.</p>
<p>Ideally, the majority of your test fixture is established through the use of the Before methods, and destroyed through the use of the After methods. This is particularly important. For a test to truly be a unit test it must be isolated. The use of the Before and After methods ensure that a new test fixture is built, tested against and destroyed for each test.</p>
<h2>Walkthrough 1: Creating a Fixture for the Circle Tests</h2>
<p>In this walkthrough you will perform the following tasks:</p>
<ul>
<li>Create <code>setMeUp()</code> and <code>tearMeDown()</code> functions for the test case.</li>
<li>Remove circle instantiation from individual tests.</li>
</ul>
<h3>Steps</h3>
<ol>
<li>
<p>Open the BasicCircleTest.as file from the previous exercise.</p>
<p>Alternatively, if you didn't complete the previous lesson or your code is not functioning properly, you can import the FlexUnit4Training_wt1.fxp project from the Unit 6/Start folder. Please refer to Unit 2: Walkthrough 1 for instructions on importing a Flash Builder project.</p>
<h3><br/>[Before] &amp; [After] metadata</h3>
</li>
<li>
<p>Add a private variable with the name of <code>circle</code> and a data type of <code>Circle</code> to the class.</p>
<pre><code>public class BasicCircleTest {
private static const TOLERANCE:Number = .0001;
private var circle:Circle;
...
}
</code></pre>
</li>
<li>
<p>Add a public function named <code>setMeUp()</code> to the class. Mark the function with a <code>[Before]</code> metadata tag. This function will set the <code>circle</code> property to a new <code>Circle</code> instance with arguments <code>new Point( 0, 0 )</code> and <code>5</code>.</p>
<pre><code>[Before]
public function setMeUp():void {
circle = new Circle( new Point( 0, 0 ), 5 );
}
</code></pre>
<p>Note, the name of the function is unimportant. It is only the Before metadata that makes this a Before method.</p>
</li>
<li>
<p>Similarly, add another public function named <code>tearMeDown()</code>. In this case you are going to mark the function with an <code>[After]</code> metadata tag. This function will set the class <code>circle</code> variable to null.</p>
<pre><code>[After]
public function tearMeDown():void {
circle = null;
}
</code></pre>
</li>
<li>
<p>Remove the <code>circle</code> variable instantiations from each of the test methods. Using the first test as a model:</p>
<pre><code>[Test]
public function shouldReturnProvidedRadius():void {
var circle:Circle = new Circle( new Point( 0, 0 ), 5 );
assertEquals( 5, circle.radius );
}
</code></pre>
<p>Becomes:</p>
<pre><code>[Test]
public function shouldReturnProvidedRadius():void {
assertEquals( 5, circle.radius );
}
</code></pre>
<h3><br/>Running the test case</h3>
</li>
<li>
<p>After the instantiations have been removed, the test class should read as follows:</p>
<pre><code>public class BasicCircleTest {
private static const TOLERANCE:Number = .0001;
private var circle:Circle;
[Before]
public function setMeUp():void {
circle = new Circle( new Point( 0, 0 ), 5 );
}
[After]
public function tearMeDown():void {
circle = null;
}
[Test]
public function shouldReturnProvidedRadius():void {
assertEquals( 5, circle.radius );
}
[Test]
public function shouldComputeCorrectDiameter():void {
assertEquals( 10, circle.diameter );
}
[Test]
public function shouldReturnProvidedOrigin():void {
assertEquals( 0, circle.origin.x );
assertEquals( 0, circle.origin.y );
}
[Test]
public function shouldReturnTrueForEqualCircle():void {
var circle2:Circle = new Circle( new Point( 0, 0 ), 5 );
assertTrue( circle.equals( circle2 ) );
}
[Test]
public function shouldReturnFalseForUnequalOrigin():void {
var circle2:Circle = new Circle( new Point( 0, 5 ), 5);
assertFalse( circle.equals( circle2 ) );
}
[Test]
public function shouldReturnFalseForUnequalRadius():void {
var circle2:Circle = new Circle( new Point( 0, 0 ), 7);
assertFalse( circle.equals( circle2 ) );
}
[Test]
public function shouldGetTopPointOnCircle():void {
var point:Point = circle.getPointOnCircle( 0 );
assertThat( point, new CloseToPointMatcher ( new Point( 5, 0 ), TOLERANCE ) );
}
[Test]
public function shouldGetBottomPointOnCircle():void {
var point:Point = circle.getPointOnCircle( Math.PI );
assertThat( point, new CloseToPointMatcher ( new Point( -5, 0 ), TOLERANCE ) );
}
[Test]
public function shouldGetRightPointOnCircle():void {
var point:Point = circle.getPointOnCircle( Math.PI/2 );
assertThat( point, new CloseToPointMatcher ( new Point( 0, 5 ), TOLERANCE ) );
}
[Test]
public function shouldGetLeftPointOnCircle():void {
var point:Point = circle.getPointOnCircle( (3*Math.PI)/2 );
assertThat( point, new CloseToPointMatcher ( new Point( 0, -5 ), TOLERANCE ) );
}
[Test(expects="RangeError")]
public function shouldThrowRangeError():void {
var someCircle:Circle = new Circle( new Point( 10, 10 ), -5 );
}
}
</code></pre>
<p>Other circles, such as <code>circle2</code> are still instantiated in its respective methods, because it is unique in each case.</p>
</li>
<li>
<p>Save BasicCircleTest.as.</p>
</li>
<li>
<p>Run the FlexUnit4Training.mxml file.</p>
<p>If FlexUnit4Training.mxml ran successfully you should see the following output in your browser window:</p>
<img alt="TestsPassed" id="shift" src="../images/unit6/image1.png"/>
<p class="caption" id="shift">Figure 1: FlexUnit tests passed</p>
</li>
</ol>
<h2>Understanding Before as an indication of cohesion</h2>
<p>In general, it is a good practice to group similar tests together. One advantage gained by using Before and After methods is that it quickly becomes apparent when tests do not share the same test fixture.</p>
<p>If you find yourself with methods that need significantly different setup, or are not using much of the setup created in the Before methods, then it is likely that your tests are grouped in a way that is not particularly cohesive.</p>
<p>If your methods successfully share the test fixture, then each new method added to a case requires less duplicate code and hence less effort. Ideally, your tests become a line or two of code and an assertion.</p>
<h2>The importance of cleaning up the fixture</h2>
<p>Methods marked with <code>[After]</code> are generally intended to mirror the instantiations or creations of the <code>[Before]</code> method. They are responsible for destroying the test fixture to ensure open connections are closed, references and listeners are removed, and objects are made available for garbage collection.</p>
<p>Destroying the fixture correctly lowers the overhead of the test run by ensuring memory leaks are resolved and objects are appropriately collected. Left uncollected during large test runs, this additional overhead can become significant quickly.</p>
<h2>BeforeClass and AfterClass Metadata</h2>
<p>In rare cases, you may wish to create a portion of the test fixture that remains constant or maintains desired state across all tests. For example, perhaps each of your tests needs to read data from the same large XML file. Rather than reload and reparse this file in each of your [Begin] methods, you might decide to do this once for all tests in a given test case or suite.</p>
<p>FlexUnit allows you to specify static methods to run before the test case (or suite) is instantiated and after the execution is complete. These methods allow you to perform operations that should only occur once per case or suite execution, as opposed to the test by test manipulation that [Before] and [After] offer.</p>
<p>These methods, marked with [BeforeClass] and [AfterClass] metadata, are often used to facilitate tests that connect to a live system, such as a database. However, it is important to note a few pitfalls of this approach.</p>
<p>First, as soon as you use BeforeClass or AfterClass, your tests are no longer completely independent of each other. A side effect of one test has the potential to manipulate the test fixture, creating a situation where the order of tests now influences their success or failure. Therefore, it is best to try to limit items created in BeforeClass to items that will be consumed or read and avoid items which will be manipulated in any way.</p>
<p>Second, if you do find yourself heavily relying upon items created in this way, you may have moved into the territory of integration tests and away from unit tests. Remember, a unit is the smallest piece of testable code. The combination of your code, the network stack, an application server, a database connection, and a database server rarely qualifies.</p>
<p>As items created in BeforeClass are created before the test case constructor is called, they must be static. Unlike items created in Before which have the possibility of being garbage collected, these static items will not. Therefore, the cleanup performed by the AfterClass methods is extremely important.</p>
<p>Static functions marked with [BeforeClass] and [AfterClass] can exist on a test case or a test suite and are run once per execution. The method execution of a three-test hierarchy is illustrated below:</p>
<pre><code>[BeforeClass]
[Before]
[Test]
[After]
[Before]
[Test]
[After]
[Before]
[Test]
[After]
[AfterClass]
</code></pre>
<h2>Walkthrough 2: Using BeforeClass and AfterClass</h2>
<p>In this walkthrough you will perform the following tasks:</p>
<ul>
<li>Use <code>[BeforeClass]</code> and <code>[AfterClass]</code> to demonstrate test hierarchy.</li>
</ul>
<h3>Steps</h3>
<ol>
<li>
<p>Open the BasicCircleTest.as file from the previous exercise.</p>
<p>Alternatively, if you didn't complete the previous lesson or your code is not functioning properly, you can import the FlexUnit4Training_wt2.fxp project from the Unit 6/start folder. Please refer to Unit 2: Walkthrough 1 for instructions on importing a Flash Builder project.</p>
<h3><br/>Using [BeforeClass] &amp; [AfterClass] metadata</h3>
</li>
<li>
<p>Declare a public static function named <code>setUpClass()</code>, mark it with <code>[BeforeClass]</code> metadata. In the function body, add a trace statement that merely states "Before Class."</p>
<pre><code>[BeforeClass]
public static function setUpClass():void {
trace( "Before Class" );
}
</code></pre>
</li>
<li>
<p>Similarly, declare another public static function named <code>tearDownClass()</code>, mark it with <code>[AfterClass]</code> metadata.</p>
<pre><code>[AfterClass]
public static function tearDownClass():void {
trace( "After Class" );
}
</code></pre>
<h3><br/>Visualizing test order</h3>
</li>
<li>
<p>A similar trace statement should be added to the existing <code>setMeUp()</code> and <code>tearMeDown()</code> methods.</p>
<pre><code>[Before]
public function setMeUp():void {
circle = new Circle( new Point( 0, 0 ), 5 );
trace( "Before Test" );
}
[After]
public function tearMeDown():void {
circle = null;
trace( "After Test" );
}
</code></pre>
</li>
<li>
<p>Finally, add a trace statement to every test method on the first line.</p>
<pre><code>[Test]
public function shouldReturnProvidedRadius():void {
trace( "Test" );
assertEquals( 5, circle.radius );
}
</code></pre>
</li>
<li>
<p>Save BasicCircleTest.as</p>
</li>
<li>
<p>Run FlexUnit4Training.mxml file in Debug Mode.</p>
<img alt="DebugMode" id="shift" src="../images/unit6/image2.png"/>
<p class="caption" id="shift">Figure 1: Running in Debug Mode</p>
</li>
<li>
<p>Nothing is going to change about how the tests display in the browser, in this walkthrough we are interested in Flash Builder's Console View, which is usually located at the bottom of the screen.</p>
<img alt="ConsoleTab" id="shift" src="../images/unit6/image3.png"/>
<p class="caption" id="shift">Figure 2: Console in the tab navigator</p>
</li>
<li>
<p>Take a look at the console view; if all the functions ran to completion, you should see the following show up in order.</p>
<pre><code>Before Class
Before Test
Test
After Test
Before Test
Test
After Test
Before Test
Test
After Test
Before Test
Test
After Test
Before Test
Test
After Test
Before Test
Test
After Test
Before Test
Test
After Test
Before Test
Test
After Test
Before Test
Test
After Test
Before Test
Test
After Test
Before Test
Test
After Test
After Class
</code></pre>
</li>
<li>
<p>Based on the exhibited call hierarchy, you should be able to tell that the <code>setUpClass()</code> and <code>tearDownClass()</code> functions are being called before the first test method and after the last. Meanwhile, the <code>setMeUp()</code> and <code>tearMeDown()</code> methods are being called respectively before and after every test.</p>
</li>
</ol>
<h2>Summary</h2>
<ul>
<li><p>Test fixture metadata:</p></li>
<ul>
<li><p>[Before], marks a method that runs before each test.</p></li>
<li><p>[After], marks a method that runs after each test.</p></li>
<li><p>[BeforeClass], marks a method that is run before the test case or suite is created and run.</p></li>
<li><p>[AfterClass], marks a method that is run after the test case or suite has run completely.</p></li>
</ul>
<li><p>[Before] and [BeforeClass] methods can indicate test case cohesion, because tests should be grouped together based on their similarity.</p></li>
<li><p>[After] and [AfterClass] methods are useful for cleaning up the fixture and minimizing overhead.</p></li>
<li><p>These methods, when used correctly, allow you to factor duplicated code out of the test methods.</p></li>
</ul>
<h2>Navigation</h2>
<ul>
<li><a href="Unit-5.html">Unit 5 - Developing Static Tests</a></li>
<li><a href="Unit-7.html">Unit 7 - Using Suites</a></li>
<li><a href="../index.html">Table of Contents / Introduction</a></li>
</ul>
<!-- Home Page Exception -->
</div></div>
<!-- Wrapper / End -->
<!-- Footer -->
<!-- Footer Top -->
<div id="footer" class="container-fluid paddingbottom" >
<div class="row-fluid">
<!-- About -->
<div class="span3">
<div class="footer-headline"><h4>About Us</h4></div>
<p>Apache Flex® is a highly productive, open source application framework for building and maintaining expressive web applications that deploy consistently on all major browsers, desktops and devices (including smartphones, tablets and tv). It provides a modern, standards-based language and programming model that supports common design patterns suitable for developers from many backgrounds. Flex applications can be deployed to the ubiquitous Adobe® Flash® Player in the browser, Adobe® AIR™ on desktop and mobile or to native Android™, IOS™, QNX®, Windows® or Mac® applications.</p>
</div>
<!-- Subscribe -->
<div class="span3">
<div class="footer-headline"><h4>Subscribe</h4></div>
<p>We have two mailing lists, one for SDK developers, and one for SDK users.</p>
<p>Developers, send an email to <br>
<a href="mailto:dev-subscribe@flex.apache.org">dev-subscribe@flex.apache.org</a>
</p>
<p>Users, send an email to <br>
<a href="mailto:users-subscribe@flex.apache.org">users-subscribe@flex.apache.org</a>
</p>
</div>
<!-- Latest Releases -->
<div class="span3">
<div class="footer-headline"><h4>Latest Releases</h4></div>
<p>
Apache Flex SDK : <a href="/download-binaries.html">4.16.1 (Nov 2017)</a><br />
Apache FlexJS SDK : <a href="/download-flexjs.html">0.8.0 (Jun 2017)</a><br />
Blaze DS : <a href="/download-blazeds.html">4.8.0 (Apr 2023)</a><br />
Flex SDK Converter : <a href="https://github.com/apache/flex-utilities/tree/develop/flex-maven-tools/flex-sdk-converter"> 1.1.0 (Feb 2023)</a><br />
FlexUnit : <a href="/download-flexunit.html">4.2.0 (Apr 2014)</a><br />
SDK Installer : <a href="/installer.html">3.3.2 (Jul 2018)</a><br />
Squiggly : <a href="/download-squiggly.html">1.1 (Oct 2014)</a><br />
Tour De Flex : <a href="/download-tourdeflex.html">1.2 (Nov 2014)</a><br />
</p>
</div>
<!-- Latest Tweets -->
<div class="span3">
<div class="footer-headline"><h4>Latest Tweets</h4></div>
<a class="twitter-timeline" href="https://twitter.com/ApacheFlex" data-chrome="noheader nofooter noborders noscrollbar" data-widget-id="364567612920852480">Tweets by Apache Flex</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
<div class="clear"></div>
</div>
</div>
</div>
<!-- Footer / Bottom -->
<div id="footer" class="container-fluid" style="background: #111;">
<div class="row-fluid">
<div class="span12">
<div id="footer-bottom">
Copyright © 2024 The Apache Software Foundation, Licensed under the Apache License, Version 2.0 <br>
Apache Flex, Apache and the Apache feather logo are trademarks of The Apache Software Foundation. All other marks mentioned may be trademarks or registered trademarks of their respective owners.
Read more about our privacy policy on our <a href="about-privacy.html">Privacy Policy</a> page.
<div id="scroll-top-top"><a href="#" title="Go to Top"></a></div>
</div>
</div>
</div>
</div>
<!-- Footer / End -->
</body>
</html>