Unit Testing Apache Flex Applications Justin Mclean Class Software Email: justin@classsoftware.com Twitter: @justinmclean Blog: http://blog.classsoftware.com
Who am I? • Programming for 25 years • Developing and creating web applications for 15 years • Apache Flex PMC, Incubator PMC, Apache member • Release manager for Apache Flex, FlexUnit, Tour De Flex, Squiggly • Run IoT meetup in Sydney Australia
Software Decays There are many factors that can contribute to software rot. The most important one seems to be the psychology, or culture, at work on a project. It's all too easy to slip into the mindset of "All the rest of this code is crap, I'll just follow suit.” Andy Hunt + David Thomas
The Mess We’re In In the last 40 years we have written billions of lines of code that will keep programmers employed for trillions of man hours in the next few thousand years to clean up this mess we’ve made. Joe Armstrong
Risk Teams take serious chances when they try to make large changes without tests. It is like doing aerial gymnastics without a net. It requires incredible skill and a clear understanding of what can happen at every step. Michael Feathers
Bad Code Code without tests is bad code. It doesn’t matter how well written it is; it doesn’t matter how pretty or object-oriented or well-encapsulated it is. With tests, we can change the behaviour of our code quickly and verifiably. Without them, we really don’t know if our code is getting better or worse Michael Feathers
Refactoring
Legacy Code
Why Write Unit Tests? • Fast way to find bugs • Document code and share knowledge • Find regression issues • Quickly and confidently make changes • Speed up development
Unit Tests vs Other Tests • Are fast to run (1/10 sec or less!) • Give consistent results • Are self checking • Show where errors are • Can be automated
Not Unit Tests • Code that: • Makes network calls • Touches the file system • Queries a database • But still useful tests • Aim for 80%/20% split
Start Small It is better to write and run incomplete tests than not to run complete tests. Martin Fowler
XUnit Style Tests • Multiple language and IDE support • One test class tests a unit (usually a class) • Setup and teardown methods • Multiple tests • Tests via asserts
Apache FlexUnit • Based on JUnit 4 • Donated to Apache by Digital Primates • Apache release 4.2 in April 2014 • Stable and mature • Has IDE, ant and maven support
Mustella • Used to test Flex SDK • Stable • Minor improvements since coming to Apache • Good for testing events and UI
Differences from JUnit • A few extra bits - Fluent and Hamcrest • Takes care of the asynchronous nature of Flex • UI impersonators and Sequences
Test Suites • Array of test cases to run • A way to organise groups of test cases • Can have multiple suites • IDE support
Test Suite public function currentRunTestSuite():Array { var testsToRun:Array = new Array(); testsToRun.push(tests.AppTests); testsToRun.push(tests.PeopleTests); testsToRun.push(tests.PersonDetailTests); testsToRun.push(tests.PersonTests); testsToRun.push(tests.SelectPersonTests); return testsToRun; }
Basic Tests • Uses metadata to define tests • [Before] runs before test • [After] runs after test • [Test] defines a test to be run
Basic Test [Test] public function checkPerson():void { assertTrue("Name is correct", person.name == "Justin Mclean"); assertTrue("Email is correct", person.email == "justinmclean@apache.org"); assertTrue("ID is correct", person.apacheID == "jmclean"); }
Testing Exceptions • Can test for exception via metadata • expects=[error thrown]
Exception Test [Test(expects="Error")] public function loadBadXML():void { people.loadXML("unknown.xml"); }
Flex is Asynchronous • Loading XML, web service calls • Timers and events • UI Components
Async Class • Used for testing asynchronous code • Static Methods: • proceedOnEvent • failOnEvent • handleEvent
failOnEvent Test [Test(async)] [Test(expects="Error")] public function loadBadXML():void { Async.failOnEvent(this, people, Event.COMPLETE, 1000); people.loadXML("unknown.xml"); }
handleEvent Test [Test(async)] public function loadXML():void { Async.handleEvent(this, people, Event.COMPLETE, xmlLoaded, 1000); people.loadXML(); }
Testing loading XML • Setup() • Create instance • tearDown() • null instance • Asynchronous Test • [Test(async)] • proceedOnEvent or handleEvent
SetUp [Before] public function setUp():void { people = new People(false); }
tearDown [After] public function tearDown():void { people = null; }
Async Test and Handler [Test(async)] public function loadXML():void { Async.handleEvent(this, people, Event.COMPLETE, xmlLoaded, 1000); people.loadXML(); } public function xmlLoaded(event:Event, data:*):void { // Asserts go here }
Testing UI and Data Binding • Setup() • Create instance and addChild on UIImpersonator • tearDown() • removeChild and null instance • Test in normal way
SetUp [Before(async,ui)] public function setUp():void { … Async.proceedOnEvent(this, personDetails, FlexEvent.CREATION_COMPLETE, 100); UIImpersonator.addChild(personDetails); }
tearDown [After] public function tearDown():void { UIImpersonator.removeAllChildren(); personDetails = null; person = null; }
Test [Test] public function initialSelected():void { assertTrue("Name correct", personDetails.personName.text == "Justin Mclean"); assertTrue("Email correct", personDetails.email.text == "justinmclean@apache.org"); assertTrue("ApacheID correct", personDetails.apacheID.text == "jmclean"); }
Testing Components • setup() and teardown() same as previous tests • Async timeouts and failure callbacks • Async handleEvent data passing • May need to create helper classes to simulate user interactions
SetUp [Before(async,ui)] public function setUp():void { … Async.proceedOnEvent(this, selectPerson, FlexEvent.CREATION_COMPLETE, 100); UIImpersonator.addChild(selectPerson); }
tearDown [After] public function tearDown():void { UIImpersonator.removeAllChildren(); selectPerson = null; people = null; }
Async Test and Handler [Test(async,ui)] public function onChange():void { Async.handleEvent(this, selectPerson, PersonEvent.SELECT_PERSON, personSelected, 100); helper.changeList(selectPerson.persons, people[2]); } public function personSelected(event:PersonEvent,data:*):void { assertTrue("Event contains correct person", event.person == people[2]); }
Sequences • Allow easy chaining of events together • Part of Fluint • Setup up a series of steps then run steps • Code easy to read - avoids nested callbacks • Debugging can be a little tricky
Sequences • Create new SequenceRunner • Call addStep passing each step • Type of Steps include: • SequenceSetter • SequenceWaiter • SequenceEventDispatcher • SequenceCaller
Sequences var seq:SequenceRunner = new SequenceRunner(this); seq.addStep(new SequenceCaller(…)); seq.addStep(new SequenceWaiter(…)); seq.addStep(new SequenceCaller(…)); seq.addStep(new SequenceWaiter(…)); seq.run();
Fluent • Provides all the Async functionality • Support for UI component testing • Sequences support
Hamcrest • Allows matching of rules • More descriptive failure messages
Matching Rules [Test] public function testGreaterThan():void { assertThat(11, greaterThan(3)); } [Test] public function isItInHere():void { var array:Array = [ 'a', 'b', 'c', 'd', 'e', 'f' ]; assertThat(array, hasItems('b', 'c')); }
Advanced Testing • Test coverage • Mocks and Spies • Parameterised testing • Theories
Continuous Integration • Create ant build file using FlexUnit task • Test ant script • Setup in Jenkins
Get Involved • Download and have a play give us feedback • Sign up and contribute to the mailing list • Look through JIRA there’s fair amount of simple issues to fix
Links • Apache Flex site http://flex.apache.org • Apache Flex Unit http://flex.apache.org/download-flexunit.html • Tutorial http://flex.apache.org/flexunit/tutorial/ • Further Information https://cwiki.apache.org/confluence/display/FLEX/ FlexUnit • Apache Flex GitHub mirror https://github.com/apache/flex-flexunit
Questions? Ask now, see me after the session, follow me on twitter @justinmclean or email me at justin@classsoftware.com . Slides can be found at Conference Site. Code can be found at GitHub https://github.com/justinmclean/ ApacheConFlexExample
Recommend
More recommend