|
|
Presenter: Thanh Tran Email: trongthanh@gmail.com Twitter: @trongthanh Date: 18 June 2010 Test-driven Development & Flex Unit
Content Test-driven Development & Unit Testing TDD Overview Unit Testing & its benefits FlexUnit Sample Walkthrough Features Briefing Assertion Methods Authentic examples TDD Benefits & Vulnerabilities FlexUnit, when we should apply? References
Test-driven development (TDD) is a software development technique where developers: TDD Overview
related to the test-first programming concepts of Extreme Programming (XP) one of the techniques in the Agile methodologies requires write test before implement "keep it simple, stupid" (KISS) "You ain't gonna need it" (YAGNI) TDD Overview (contd.)
Unit Testing Facilitates change: allows the programmer to refactor code at a later date, and make sure the module still works correctly Simplifies integration: testing the parts of a program first and then testing the sum of its parts, integration testing becomes much easier
Unit Testing Documentation provides a sort of living documentation of the system Ex: [Test( description = "This tests replace" )] public function testReplace(): void { var str: String = "Today is Monday, Today is monday, Today is moNday, Today is monDay"; str = StringUtil.replace(str, "Monday", "Saturday"); Assert.assertEquals("Today is Saturday, Today is Saturday, Today is Saturday, Today is Saturday", str); }
Unit Testing Design-driven each unit test can be seen as a design element specifying classes, methods, and observable behavior Example: (next slide)
Unit Testing Example: this test will drive design of GetVideoItemService and VideoInfo [Test(order=2, async, description = "test get video info")] public function getInformationByID(): void { var service: GetVideoItemService = new GetVideoItemService(); var asyncHandler: Function = Async.asyncHandler(this, getVideoInfoHandler, 500, null, timeOutHandler); service.addEventListener(ServiceEvent.GOT_VIDEO_ITEM_INFO, asyncHandler); service.getVideoItemInfo("1"); } private function getVideoInfoHandler(event: ServiceEvent, passThroughData: Object): void { var videoInfo: VideoInfo = VideoInfo(event.data); Assert.assertEquals("Toyota Video 1", videoInfo.title); Assert.assertEquals("http://tdn.tv/toyota/images/thumbs/tdn.jpg", videoInfo.urlThumbnail); Assert.assertEquals("Author 6", videoInfo.author); /*...*/ }
FlexUnit
Introduction FlexUnit is a unit testing framework for Flex and ActionScript 3.0 applications and libraries. It mimics the functionality of JUnit, a Java unit testing framework, and comes with a graphical test runner.
FlexUnit walkthrough Case Study: Create a calculator.
FlexUnit walkthrough 1. First, let's setting up project's and folders:
FlexUnit walkthrough 2. Create new folder “unit_test” in project's root and create another project for unit test.
FlexUnit walkthrough For not overwriting existing files and folders of the main project, create an empty project for unit test
FlexUnit walkthrough Set the new project settings as following: Output: - bin/TestRunner.swf - 1024 x 768 Classpath: - lib - src - unit_test SWC Libraries: - lib
FlexUnit walkthrough 3. Copy FlexUnit's swc to lib folders: flexunit-4.0.0.swc (FlexUnit core) flexunit-uilistener-4.0.0.swc (Flex-based graphical runner)
FlexUnit walkthrough 4. Create test suite: TestSuite is a collection of TestCases and possibly other TestSuites. package unittest { [Suite] [RunWith("org.flexunit.runners.Suite")] public class TestSuite { } }
FlexUnit walkthrough 5. Create first test case: A TestCase is a collection of TestMethods that share a common test environment. package unittest.testcases { public class TestCalculatorUtil { } }
FlexUnit walkthrough 6. Create first test method: A TestMethod is the smallest unit of the testing framework. A test method executes code and checks an outcome. In FlexUnit4, your methods must be decorated by a piece of [Test] metadata package unittest.testcases { import org.flexunit.Assert; public class TestCalculatorUtil { [Test (description="test add")] public function testAdd(): void { Assert.assertEquals(5, CalculatorUtil.add(2, 3)); } } }
FlexUnit walkthrough Go back to TestSuite and add the TestCase: package unittest { import unittest.testcases.TestCalculatorUtil; [Suite] [RunWith("org.flexunit.runners.Suite")] public class TestSuite { public var testCase1: TestCalculatorUtil; } }
FlexUnit walkthrough 7. Create the TestRunner.mxml to run the test: <?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:ui="http://www.adobe.com/2009/flexUnitUIRunner" creationComplete="runMe()" > <mx:Script> <![CDATA[ import unittest.TestSuite; import org.flexunit.listeners.UIListener; import org.flexunit.runner.FlexUnitCore; private var core:FlexUnitCore; public function runMe():void { core = new FlexUnitCore(); //Listener for the UI core.addListener( new UIListener( uiListener )); //run test suite core.run( TestSuite); } ]]> </mx:Script> <ui:TestRunnerBase id="uiListener" width="100%" height="100%" /> </mx:Application>
FlexUnit walkthrough At this time the unit tests are not compilable because we haven't implement anything yet. However, we can consider this is also a failed test case.
FlexUnit walkthrough 8. Create the class and functions to match the design in test method but don't implement any logic yet. This step is to make the tests compilable but expect to failed. package calculator.utils { public class CalculatorUtil { public static function add(a: Number, b: Number): Number { return NaN; } } }
FlexUnit walkthrough Compile and run the test runner, see the test fails:
FlexUnit walkthrough 9. Implement the logic to pass the test: public static function add (a: Number, b: Number): Number { return a + b; }
FlexUnit walkthrough 10. Run the test to see if you pass:
FlexUnit walkthrough This concludes a UnitTest life cycle. Continue adding new features by repeating the steps from creating new test method.
FlexUnit features briefing Flex or ActionScript 3 FlexUnit works with both Flex or AS3-only projects. Metadata By using metadata [Test] [Suite]... tests no longer need any special name or extends special classes
FlexUnit features briefing Before and After [Before] methods will be run before each test method. [After] methods will be run after each test method. [Before] public function runBeforeEveryTest():void { simpleMath = new SimpleMath(); } [Before] public function alsoRunBeforeEveryTest():void { simpleMath1 = new SimpleMath(); } [After] public function runAfterEveryTest():void { simpleMath = null; simpleMath1 = null; }
FlexUnit features briefing BeforeClass and AfterClass [BeforeClass] and [AfterClass] allow you to define static methods that will run once before and after the entire test class [BeforeClass] public static function runBeforeClass():void { //run for one time before all test cases } [AfterClass] public static function runAfterClass():void { // run for one time after all test cases }
FlexUnit features briefing Exception Handling The expects parameter in [Test] allows you to indicate that a given test is expected to throw an exception [Test(expects="flash.errors.IOError")] public function doIOError():void { //a test which causes an IOError } //OR [Test(expects="TypeError")] public function divisionWithException():void { simpleMath.divide( 11, 0 ); }
Ignore Ignore metadata can be added before any test case you want to ignore. Unlike commenting out a test, these tests will still appear in the output for reminding. [Ignore("Not Ready to Run")] [Test] public function multiplication():void { assertEquals(15, simpleMath.multiply(3, 5)); } FlexUnit features briefing
FlexUnit features briefing Async To specify which tests need asynchronous support, use the async parameter. Async tests are necessary for some particular test like services, time-based or event-based parts. [Before(async,timeout="250")] public function setMeUp():void {/*...*/} [After(async,timeout="250")] public function allDone():void {/*...*/} [Test(async,timeout="500")] public function doSomethingAsynchronous():void { //Async.proceedOnEvent( testCase, target, eventName ); //Async.failOnEvent( testCase, target, eventName ); //Async.handleEvent( testCase, target, eventName, eventHandler ); //Async.asyncHandler( testCase, eventHandler ); //Async.asyncResponder( testCase, responder ); }
FlexUnit features briefing Ex (again): [Test(order=2, async, description = "test get video info")] public function getInformationByID(): void { var service: GetVideoItemService = new GetVideoItemService(); var asyncHandler: Function = Async.asyncHandler(this, getVideoInfoHandler, 500, null, timeOutHandler); service.addEventListener(ServiceEvent.GOT_VIDEO_ITEM_INFO, asyncHandler); service.getVideoItemInfo("1"); } private function getVideoInfoHandler(event: ServiceEvent, passThroughData: Object): void { var videoInfo: VideoInfo = VideoInfo(event.data); Assert.assertEquals("Toyota Video 1", videoInfo.title); Assert.assertEquals("http://tdn.tv/toyota/images/thumbs/tdn.jpg", videoInfo.urlThumbnail); Assert.assertEquals("Author 6", videoInfo.author); /*...*/ }
FlexUnit features briefing Hamcrest Hamcrest is a library of matcher objects allowing 'match' rules to be defined declaratively. Use assertThat() for Hamcrest assertion //Example: [Test] public function testGreaterThan():void { assertThat( 11, greaterThan(3) ); } [Test] public function isItInHere():void { var someArray:Array = [ 'a', 'b', 'c', 'd', 'e', 'f' ]; assertThat( someArray, hasItems("b", "c") ); } assertThat(valueToMatch:Object, matcher:Matcher); assertThat(assertionDescription:String, valueToMatch:Object, matcher:Matcher);
Assertion methods
Some Authentic examples
TDD Benefits & Vulnerabilities
TDD's Benefits gives greater level of confidence in the code significantly reduces the effort of QA drives the design of a program total code implementation time is typically SHORTER leads to more modularized, flexible, and extensible code
TDD's Vulnerabilities cannot be expected to catch every error in the program and will not catch integration errors or broader system-level errors Without entire organization support/belief, management may feel that time spent writing tests is wasted Writing code for a unit test is as likely to be at least as buggy as the code it is testing Unexpected gaps in test coverage may occur which leads to false confidence / bugs undetected The tests may share the same blind spots with the code due to misunderstanding of requirements
FlexUnit, when should be applied?
FlexUnit, when should it be applied? 1. Testing utilities & helpers 2. Testing GUI 3. Testing library or framework API 4. Testing components integration 5. Testing validators. 6. Testing view components 7. Testing services 8. Testing reusable blocks of code or functions. 9. Testing MVC workflow YES NO YES YES NO YES YES NO NO
References FlexUnit: http://flexunit.org Wiki: Unit Testing http://en.wikipedia.org/wiki/Unit_testing Wiki: Test-driven Development http://en.wikipedia.org/wiki/Test-driven_development Another FlexUnit tutorial: http://elromdesign.com/blog/2010/03/10/test-driven-development-tdd-with-flexunit-4-complete-tutorial/ Hamcrest-as3: http://github.com/drewbourne/hamcrest-as3
Q & A
Thank you
Summary: Slides of my presentation to introduce TDD and FlexUnit
| URL: |
No comments posted yet
Comments