Feb 1, 2012

Unit Tests as a Measure of Code Cleanliness


    I recently attended the 2011 Adobe MAX conference and sat in on the Unit Testing Adobe Flex session by Michael Labriola of Digital Primates.  I expected the session to be a repeat of what I already know and strongly believe.  Unit tests simplify testing, document your code, improve maintainability, blah blah blah.  The aspect of the lecture that I found most fascinating was on writing clean code and using unit tests as a means to verify code cleanliness. 

    My training regarding unit tests has been pretty informal.  Through experience I’ve found that when I write automated tests my bug count goes way down, and when I do have a bug I have a great starting point to isolate and fix issues.  I’ve always understood the difference between unit, integration, and functional testing but never much cared because all 3 have the benefits mentioned above.  The simple discovery, however, that being able to unit test your code in the strictest sense is a very good measurement as to the cleanliness of your code was pretty eye-opening.  If your code base is only testable at the integration level, your code is probably too tightly coupled and does not have good encapsulation.


    I am currently working on a very large project that charts, maps, analyzes, and performs a million other visualizations and calculations on complex data.  It follows a simple MVC architecture such that I am able to isolate specific view behavior into 1 or more specific control classes.  It generally results in pretty clean code, and I haven’t had too much trouble maintaining it.  I write automated “flexunit” test cases that verify things work properly, and give me a decent starting place when I do run into a bug.  That said, however, I still have areas of the code that cause me problems.  These areas have been refactored over time, and improved to some degree, but they still give me more pain than I’d like.

    After the Michael Labriola lecture I decided to take a look at my code and automated tests from a strict policy that automated tests must be “unit” tests, not “functional” or “integration” tests.  Michael’s presentation defined a unit test as

  • testing a single object
  • should not be affected by other objects
  • does not depend on global state
  • does not depend on server, lifecycle, display lists, or asynchronous events (these are integration level testing).
  • When a unit test fails, the source of the error should be immediately obvious

    Take a look at one of my more troublesome view controls, and the corresponding automated test.  The code below is the control logic for a view that displays a line chart representing value data.  It is possible for the user to add data to the chart for display or add filters that reduce the data being displayed.

public class LongTermProfitAnalysisToolCtrl extends DataAndFilterToolWindowCtrl
    protected override function onCreationCompleteView(event:ViewLifeCycleEvent) :
        //Set default filter and analysis calculation values
    //Private methods that listen to data changes
    //Private methods that listen to user input changes
    //Private methods that calculate Annual income analysis info based on data
    // and user input
    //Private methods that calculate monthly income analysis.
    //Private methods that calculate ROI analysis info based on data and
    //user input
    //Private methods that would update chart line series data based on
    //analysis calculations
    //Private methods that create data tips on the chart

Figure 1 – Analysis Chart Control

public class LongTermProfitAnalysisTest extends MDIToolTestBase
    Before( async, ui)]   
    public override function setUp():void   
        //register views and controls with MVC framework           
        //Create tool, and proceed on CreationComplete event   
    *  Test adds an item to be analyzed, and verifies the analysis output   
    *  is correct.   
    public function testAddItemToAnalyze():void   
        //Create test data       
        //Add test data to tool       
        /* Look at chart data and verify the values are what you are expecting with regard to ROI, Annual & Monthly income.  And when it doesn’t, raise your fist into the air and curse the developer (me) whowrote this crappy code and the fact that this unit test doesn’t help you narrow down the problem.  */ 
    *  Test when a filter object is added, the resulting data output   
    *  is correct.   
    public function testFilters():void   
        //Create test data       
        //Add test data to tool       
        //Add a filter       
        /* Look at chart data and verify the values are what you are expecting with regard to ROI, Annual & Monthly income.  And when it doesn’t, raise your fist into the air and curse the developer (me) whowrote this crappy code and the fact that this test doesn’t help you narrow down the problem.  */  

Figure 2 – Analysis Chart Control Test

Flaws in Unit Testability & The Underlying Problems

    Looking at the tests above, it is easy to see that these are integration tests, not unit tests.   In order to even begin testing I must create a visualization, and wait for creation complete.  I then have to create a rather complicated set of interrelated test data, and at the end of it all I can only determine correctness.  The tests provide me no insight as to what could be incorrect. 

    So it looks like my code is not unit testable as written, and sure enough when you look at the code under test you can see why.  The control class has 10 different responsibilities.  It inherits from a large class hierarchy that performs a great deal of work that has nothing to do with what I am trying to test.  The encapsulation is terrible and a great deal of test data must first be setup in order to run a simple test.

Refactoring Approach

    One of the major difficulties with refactoring is that it is often difficult to figure out how to cleanly separate your code.  For many of my complex tools I had broken the code down cleanly and so it wasn’t apparent that the example above was following a different and unclean pattern.  I didn’t even realize that my approach was haphazard and less than optimal until I hit a wall.  Fortunately as I learned in the lecture, unit tests not only raise a red flag, they can guide you in how to re-factor your code.

Step 1:  Identify each method / capability that should be unit tested

    Take a look at each method / capability and identify which are worth unit testing.  In my case it is all the calculation functions, as well as the ability to update the visualized chart data when input data changes.

Step 2:  Identify why code can’t be unit tested

    In my case there are a dozen reasons this code can’t be unit tested in the strictest sense, but here are some of the big problems.

  1. The logic depends on the view and view life cycle events like CreationComplete.
  2. The logic operates on complicated internal data structures
  3. All the calculations are private, the only thing that is public is the output.

Step 3:  Group high level capabilities into units of single responsibility

    Analyzing the code, and understanding what capabilities I want to test, the code can be broken down into 3 logical components.  Note:  At this point these are “logical” components, and may translate into 1 or more classes.


Figure 3 – Logical Components

  1. ViewController – The view controller listens to user input, and triggers actions with the Analyzer_Calculator and the CalculationToLineSeriesTranslator.  This logical component is now trivial, and honestly can survive with only integration testing.
  2. Analyzer_Calculator – The analyzer / calculator logical component will need to perform mathematical calculations like “return on investment” for a given piece of data.  This logical component doesn’t depend on anything and can be written as calculation utility methods which will be very easy to unit test.
  3. CalculationToLineSeriesTranslator – The translator converts data and calculations into line series data that the chart view can draw.  This logical component will not need to depend upon anything and can be unit tested very easily.  Given some input data values, verify the output is lines series data with values that match the input.

Step 4:  Refactor existing logic into new components

    At this point you’ve already solved the hard problems.  Refining down to individual classes is fairly trivial.  For example, my “Analyzer_Calculator” logical component was implemented as several specific calculator classes that were really just groupings of related utility calculation methods.  Each was very easy to test and verify.


    Learning to use unit tests in the strictest sense of “unit” have been invaluable to me in my own development.  It is often very difficult to tell the difference between code that is “good enough” and code that is “good”, and unit testability provides a pretty good threshold measurement.  No rule is infallible, but so far in every case where I have re-factored my code around strict unit testability I’ve seen a dramatic increase in code quality. 


Desentupidora | Plumber said...

Hey! Eu só gostaria de dar uma enorme polegares para cima para os dados grande que você poderia ter aqui neste post. Eu posso estar chegando novamente ao seu blog para extra em breve.

Anonymous said...

Fine way of telling, and fastidious post to get facts
on the topic of my presentation subject matter, which i am going
to convey in university.

my blog post ... find similar songs

Muhamed ِAbd El Hamed El Qersh said...


عامل مكافحة حشرات
شركة مكافحة حشرات بالرياض
تعتبر العناكب من الحشرات الصغيرة التي تعيش في كل أنحاء العالم ويقدر عددها بنحو الثلاثين ألف نوع والكثير من أنواع العناكب تعتبرغير سامةً ولكن يوجد أقلية منها سامة وقد تؤذى الإنسان لدرجة كبيرة قد تؤدي إلى الوفاة في الكثير من الأحيان، بالنسبة إلي البعض الآخر فيعتبرمفيد للإنسان فيستخدم للتخلص من الكثير من الحشرات الضارة، وكما تستخدم العناكب في الكثير من المجالات منها مجال البحوث حيث يستخدمها العديد من العلماء لتبين أثر المخدر عليها، ويتكون جسم العنكبوت من قسمين القسم الأول هو الرأس أما القسم الثاني فهو البطن، كما يحظي العنكبوت بثمانية أعين تمكنه من رؤية كل شئ يدور حوله، فى حال فقد العنكبوت إحدى أرجله فتنمو له رجل أخرى مكانها ويتكون بيت العنكبوت من مجموعة من أربعة خيوط وكل خيط يتكون من أربعة خيوط أدق منها وكل خيط يخرج من أنبوبة خاصة في جسد العنكبوت، كما أنه تستخدم العناكب بيتها لصيد الفرائس فيجب أن تحترس من لدغة العنكبوت التي تكون مؤلمه ومؤذية جداً فى نفس الوقت، ومعظم العناكب ليست سامة أو ضارة كما قلنا سابقاً ولكنه يوجد نوعين لا بد وأن نحترس منهما ألا وهما العنكبوت البني والعنكبوت الأسودحيث أن لدغتهما سامةً للغاية وتسبب الكثير من الألم وتؤدي إلي الوفاة، أما بالنسبة إلي الإسعافات الأولية لمن لدغه العنكبوت البني أو الأسود فيتم غسل المكان الذي لدغة العنكبوت جيداً بالماء والصابون ومن ثم يتم وضع كمادات على مكان اللدغة بالإضافة إلي إستخدام مضاد حيوي للوقاية من العدوى والإلتهاب، الذهاب إلى أقرب وحدة صحية هوا أمر ضروري جداً لتلقى العلاج المناسب فى أسرع وقت.
شركات مكافحة الحشرات بالرياض
حقائق عن العناكب: يمكن للدغة العنكبوت الأسترالي أن تتسبب فى قتل إنسان في أقل من ساعة، كما أن له أنياب تستطيع اختراق احذية الجلد والبلاستيك ولكي يتم إستخلاص السم من العناكب يتم أولاً تخديرها ب (ثاني أكسيد الكربون) ومن ثم يتم تمرير الكهرباء إلي جسدها والتي بدورها تدفع السم من أنيابها إلى شعيرات رفيعة جداً إلي داخل أنابيب صغيرة جدا، وتقتل أنثى العنكبوت (الذكر) بعد الزواج في ليلة الدخلة وبعد عملية التزاوج ومن ثم تأكله، أيضاً تستطيع العناكب أن تأكل الحشرات أكثر مما تأكلها الوطاويط والطيور عامةً و معظم العناكب تستطيع السباحة على سطح المياة وعندما تشعر بوجود فريسة لها أسفل الماء فبإمكانها أن تغوص لأسفل الماء وتصطادها، وتستخدم العناكب شعيرات دقيقة جداً تغطى جسمها وذلك للتعرف على مناطق الخطر.

إذا فكيف تتخلص من العناكب ؟ : إستخدام المبيدات الكيماوية قد يكون من الطرق الفعالة ولكن تلك الطريقة خطيرة جداً كما أنها سوف تحتاج إلى مساحة كبيرة وواسعة، يمكن أيضاً أن يتم إستخدام محلول (النيكوتين) بعد أن يتم إضافته إلي الماء الدافئ: وهو من المبيدات الجيدة جداً خاصةً في عملية التخلص من العناكب ولكنه أيضاً سام جداً ويتم تحضيره عن طريق إضافة كوب مملوء ب (المعسل) أو (تبغ السجائر) إلى لتر ماء دافئ ومن ثم يتم تركها لمدة لا تقل عن النصف ساعة، ثم تسكب ملعقة من الصابون السائل إلى الخليط، ثم نقوم بتصفية الخليط بإستخدام قطعة قماش، ويكون الخليط صالحاً لعدة أسابيع إذا تم حفظه في مكان آمن، ومن مميزات هذا الخليط أنه من الممكن تصنيعه في المنزل وبأسعار رخيصة، كما أنه غير قاتل للحشرات الأخرى مثل النحل لأن يفقد صلاحيته بعد بضع ساعات من رشه على النباتات.

Hua Cai said...

ray-ban sunglasses
louis vuitton bags
michael kors outlet online
lululemon uk
louis vuitton outlet
swarovski outlet
louis vuitton outlet stores
michael kors factory outlet
burberry outlet
mulberry outlet
hermes outlet store
ray ban sunglasses
michael kors handbags
cheap oakley sunglasses
hermes outlet
air max 2015
fitflops outlet sale
louis vuitton handbags
ferragamo outlet
tiffany and co
nike tn pas cher
rolex uk
mulberry sale
replica watches
bottega veneta outlet online
cheap michael kors handbags
fitflops clearance
toms shoes
christian louboutin outlet
chaussure louboutin
celine outlet online
toms outlet
cheap jordan shoes
nike blazer pas cher
ferragamo outlet

xumeiqing said...

omega seamaster
cheap jordan shoes
tiffany and co
gucci outlet
ralph lauren outlet
rolex submariner
true religion
michael kors outlet
michael kors outlet clearance
nike store
longchamp handbag
nike air max
converse uk
louis vuitton outlet
polo outlet
jimmy choo outlet
adidas supercolor
ed hardy uk
oakley sunglasses
nike air max
instyler max
jordan shoes
nike air force white
ray ban outlet
louis vuitton outlet
kobe shoes
michael kors outlet
prada outlet store
adidas stan smith
ralph lauren outlet
fitflop uk
true religion outlet
adidas trainers
cheap nba jerseys
nfl jerseys wholesale
louis vuitton factory outlet

xjd7410@gmail.com said...

kate spade
nike air max 90
louboutin shoes
tod's shoes
louis vuitton handbags
true religion jeans
retro jordans 13
oakley sunglasses
coach outlet store online
gucci handbags
cheap oakley sunglasses
nike roshe run women
gucci outlet online
burberry bags
michael kors outlet
marc jacobs handbags
louis vuitton outlet
coach factory outlet online
nike free run
coach outlet store online clearances
coach factory outlet
kate spade handbags
oakley outlet
oakley sunglasses
coach outlet store online
michael kors outlet
oakley sunglasses
adidas nmd
adidas yeezy
louis vuitton outlet

dong dong23 said...

kate spade
louis vuitton outlet
ralph lauren polo
ugg boots uk
louis vuitton outlet
miami heat jerseys
michael kors handbags
louis vuitton outlet
nike tn pas cher
michael kors outlet clearance

raybanoutlet001 said...

instyler max 2
fitflops shoes
nike free
hugo boss sale
ralph lauren outlet online
chaussure louboutin
salomon boots
michael kors handbags
michael kors handbags

raybanoutlet001 said...

nike blazer
basketball shoes
ecco shoes outlet
yeezy boost 350 black
michael kors outlet online
chicago bulls jersey
michael kors handbags wholesale
adidas nmd runner
nhl jerseys
tiffany and co

dada24 Xu said...

louis vuitton pas cher
hermes bags
pandora charms sale clearance
cheap jordans for sale
longchamp handbags
moncler outlet online
canada goose
adidas nmd r1
kd shoes

dong dong23 said...

gucci outlet
ralph lauren outlet
moncler uk
coach outlet store online
ralph lauren outlet
beats by dre
michael kors outlet
ugg boots
ralph lauren uk
seattle seahawks jerseys