Showing posts with label Test Driven Development. Show all posts
Showing posts with label Test Driven Development. Show all posts

Tuesday, August 31, 2010

Unit Testing Perils

Adding unit tests to existing code is nowadays applauded for being progressive, almost borderline pedestrian, for most software development shops. This was not the case when I began writing automated unit tests several years ago. It now feels otherworldly to see greater acceptance of unit testing primarily because I'd greatly toned down my advocacy for it. This shift in attitude includes a (substantial) decrease practicing test driven development (TDD). Unit testing can not simply be applied on blind faith hoping to cure all of one's software ills.

Not long ago, I and other developers on a .NET software project were retroactively adding unit tests (no TDD) for recently produced C# code. Part of the process involved removing any cruft specifically code that was not being called by any other code. The motivation was to help increase code coverage by removing any untouched lines of code. We relied on the static analysis features of the Visual Studio plugin, ReSharper, to reveal these isolated areas of code. In ReSharper parlance, Find Usages handles the work of hunting down any dependencies for symbols and functions.

One of those areas ReSharper indicated that no usages found were for a few simple getter/setter properties of a class. This code was then confidently removed. However, it was later discovered that the removed code did indeed serve a purpose and was providing functionality to one of the GUI screens of the WinForms application. More specifically a few of the columns in a 'DataGridView' control that allowed editing no longer did so. They unexpectedly became read-only.

The GUI screens had no tests. The discovery was made via manual end-to-end testing. This 'DataGridView' control was bound to the properties of the class. It inferred from the properties whether they were getters, setters, or both. The 'Set' accessor of the properties were naively removed since the code we wrote did not seem to call it. The grid control however was binding to it and passing values to and from the properties. No setter accessors now meant the associated columns had become effectively non-editable.

Realizing our mistake we rolled back our original edits for that class and cautiously reviewed all other recent refactorings.

Joel Spolsky once stated that manual testing is all you need in developing quality software and that unit testing provides no notable value . Others taking the inevitable opposing view immediately went on the offensive denouncing his claims. Joel's view is more an over reaction to all the TDD zealots who, whether intentional or not, seem to be deemphasizing the value of old fashion manual testing. Meanwhile, the dissenting voices are manifesting as a fear that their do-no-wrong methodology (and possibly their identity) might possibly amount to nothing. Both views are too extreme and leaning one way or the other can cost you in other areas. You need to continually find and maintain a balance in testing and not become complacent with whatever approach you take.

Yes, integration-style tests might have helped in exercising and validating the correct behaviour in the UI but even that might give you a false sense of security. Manual testing does have its virtues. Without it, you might overlook the human elements of UI functionality, design, and usability. Just like it is an incorrect assumption that no compiler errors means your application is fully functional and ready for end-users.

Also, these were CRUD operations. In my experience they tend to be the least risky part of an application, least likely to be buggy, and the quickest to identify and fix. The cost of writing and maintaining these sorts of unit tests do not warrant their benefit. Is it really worth your time and effort chasing after an unrealistic 100% code coverage? You learn your lesson then you move on (part of continually defining the proper testing balance). Regardless, you should confirm that your seemingly harmless refactoring does not produce unwanted side effects. Unit tests help with that but not by themselves.

Putting the merits of unit testing aside for a moment, the inability to easily detect how the .NET framework is referencing and interacting with my code can be somewhat irritating. This datagrid binding issue is another one of those features in .NET where it does something behind the scenes on your behalf (automagically!) but it is not clear (at least not from a coding perspective) what and how it is doing it. I'd experienced this before when trying to implement paging using NHibernate in an ASP.NET GridView control and it was frustrating. Wait until the havoc WebMatrix, LightSwitch, and friends will unleash on the .NET community.

Thursday, January 15, 2009

Test Driven Blogging

Years ago when I first started learning and practicing Test Driven Development (TDD), I became extremely overzealous blindly believing all code needed to be unit tested (one of the more extreme cases were my over-the-top uses of TSQLUnit which included testing simple, low risk DDL changes that, in retrospect, was probably a bit too much with so little to gain.) This attitude was probably a common rookie mistake of becoming enamored with a new methodology (or language or framework or ....) by assuming this is how all software should be written. It is as if I had discovered the elusive magic bullet that a lot of programmers spend most of their careers searching for. Unit testing, along with its subset TDD, has steadily grown in popularity spanning the diverse programming communities spectrum. How could I possibly go wrong in my new found beliefs?

However, this past year, I have seriously reconsidered my views on unit testing. Instead, I have significantly curtailed the use of unit testing by being more selective as to when it should be applied in code. I have realized that while testing is an important tool, it is far from the "be all and end all" that others have proclaimed it to be. Instead, I readjusted my thinking to focus on what is genuinely the most important goal in programming which is to actually deliver good working software in an iterative manner.

On Stack Overflow (SO), Kent Beck an early pioneer of TDD and a creator of JUnit (the precursor for all modern xUnit style frameworks), provided a very interesting and perhaps unexpected response to the question: How deep are your unit tests?

I get paid for code that works, not for tests, so my philosophy is to test as little as possible to reach a given level of confidence.

The reality of what Beck wrote can not be ignored. (At least, I think it's him. The tone and content of his other responses on SO seem to indicate that it might just be.) Principally, working code is more important than the tests themselves. Tests can be just one of numerous methods to achieve the goal of delivering good quality software on a frequent basis but tests must certainly not overshadow this intent.

Along with Beck, it was unquestionably reassuring to read a recent post of Ayende (a notable .NET blogger/developer) that also tackles head on this very topic regarding software delivery and testing:

I want to make it explicit, and understood. What I am riling against isn't testing. I think that they are very valuable, but I think that some people are focusing on that too much. For myself, I have a single metric for creating successful software:

Ship it, often.

Keep in mind these statements are from the creator of Rhino Mocks, one of more popular mock testing frameworks for .NET. Subsequently, it carries a lot weight as it was written by someone who does truly and thoroughly understand the virtues of unit testing and TDD. Ayende is someone who I most certainly admire being as close to the ideal model of a 10x programmer in not just the realm of .NET but in programming in general (some of that admiration stems from being in complete awe of his unearthly prolific blogging). Having him confirm something that I myself have come to realize on my own this past year definitely does help to validate my current views. Quite simply, I have substantially toned down my TDD rhetoric and restrained my testing impulses in favor of renewing my true objectives in programming.

My learning TDD coincided with my learning of .NET and C#. At that point in time, I compulsively consumed the writings of a somewhat noted blogger in the .NET community who relentlessly championed unit testing and TDD. Practically treating this person's words as pure gospel, I considered this individual to be quite representative of the ALT.NET community by serving as one of the leading voices for all that it embodies. This is a community deeply immersed in the ways of unit testing and TDD.

However, with my now newly reformed outlook on software development, I have become much more wary of that blogger's "best practices" crusades. The blog still continues to be obsessively fanatical over the non-negotiable importance of unit testing and TDD almost to the exclusion of any other competing methodologies or tools. Their dogmatic writings assuredly fall into the "focusing on that [testing] too much" camp as described earlier in Ayende's post. Originally, this programmer's "test driven blog" once held one of the few selective spots on my blog's "Blog List" links. But, since it no longer carries the same relevance to me as it used to, I decided to remove it from the list.

Nevertheless, I still read that blog from time to time because it does have great information and observations regarding software development and best practices in the .NET ecosystem. In addition, I still consider myself a TDD practitioner despite reducing the scope and influence of unit testing in relation to my programming style. I just now better grok what my priorities are.

Friday, November 14, 2008

Unit testing databases with TSQLUnit

In preparation to doing a lot of database work (think schema/DDL changes) for the next several months, I thought it would be important to define methodologies and tools to manage the development process. Having been heavily applying a test-driven development (TDD) approach in a recent project for an ASP.NET C# web application, it seemed logical and now quite natural to continue pursuing a similar path and mindset but one now more targeted towards database development.

TSQLUnit and TDD

To facilitate a smooth flow with TDD, it was key to identify a suitable testing framework to support my goal. After some research and comparisons, I eventually settled on TSQLUnit, an xUnit framework for MS SQL Server. It is interesting to note that for every programming language that exists out there, it is expected that someone will eventually create an xUnit style testing framework for it. Why should SQL Server be any different?

Database testing could be performed from the application side but the nature of the development was primarily schema/DDL changes (e.g. mostly dropping and removing tables, columns, etc.) that involved very little or no business logic. Otherwise, app code would be a better and more appropriate candidate to handle testing the db. This particular scenario lent itself to finding and using something that was as close to the database as possible ideally where the unit tests can be written and supported by the TSQL language itself. The intent was to try and follow one of the core tenets of any xUnit framework which is to do testing in the same language as the code that requires to be changed. TSQLUnit seemed the best fit.

Regardless, it is quite probable, in the future, that I might disregard all of above and still switch to testing all aspects of the database using C#. This is not because of TSQLUnit but because the TSQL language itself can be so cumbersome to work with especially if you are accustomed to the power and flexibility of a non-declarative programming language such C#. For the moment, it is satisfies my current needs for testing so it will suffice.

A Few Key Features of TSQLUnit

One feature of TSQLUnit that is critical for any type of database testing is the ability to rollback changes after each test run completes ensuring that the database is in a nice clean state before the next test starts. This simply implies that the the data will not remain altered when the next test runs potentially tainting its results. Not only does this apply to all data/DML changes (inserts, updates, deletes) but DDL changes (creates, alters, drops) as well. For example, if Test A creates a table or alters a column for an existing table then those schema changes will most certainly rollback and be undone before Test B starts to execute. Therefore, the tests themselves are isolated from each other meaning that all of the unit tests can run independently.

In addition, TSQLUnit supports the xUnit features of Setup/Teardown for a test suite. For example, if you have five tests that are logically related and are dependent on the same preconditions then you can prepare all of your data in a single, shared 'Setup' fixture and TSQLUnit will automatically run this before each associated test thereby enforcing no co-dependencies between the tests. Note that one obvious drawback with repeating the same setup multiple times might be performance if the tests rely on loading large volumes of data for the purpose of testing. As for 'Teardown', it provides essentially the same functionality as 'Setup' with the one difference of executing at the end of each test and not at the beginning.

TSQLUnit is fairly decent but I initially struggled not with the framework itself but with the usual problems with database testing such as setup of data, rollback of changes, etc. It is a lot of work because unlike in application code it is hard to stub out dependencies in the database objects such as tables, stored procedires, etc. (Especially when dealing with a 225+ column table in the good ol' legacy db I was so fortunate to be tasked to make modifications.)

Running TSQLUnit using NAnt

Once a few initial unit tests were created using TSQLUnit, the process of running them was incorporated into the project's NAnt database build script. Just like the database build itself, the unit tests makes use of the NAntContrib <sql> task for their creation and execution. These are the high level targets defined in the build script:
<!-- after building database including applying change scripts -->

<call target="InstallTSQLUnit"/>
<call target="LoadUnitTests"/>
<call target="RunUnitTests"/>

'InstallTSQLUnit' is a single sql script that runs against your target db creating all of the necessary tables, stored procedures, etc. that the TSQLUnit framework requires to function. This is simply the basis and source code for the framework itself that is integrated with the database.

'LoadUnitTests' runs the sql scripts that contains the tests themselves located in a 'Tests' folder in the project's directory. To elaborate, it creates all of the unit test sprocs along with, if necessary, injecting any test data into the database that is not handled by the Setup fixtures.

Finally, 'RunUnitTests' calls the TSQLUnit command to run all of the unit tests inside the database:
<target name="RunUnitTests" description="Run all unit tests "
if="${installUnitTesting}">
<sql connstring="${connectionString}" transaction="true" delimiter=";" delimstyle="Line">
exec tsu_runTests;
</sql>
</target>
The NAnt db build is configurable so that you can specify whether or not to include or exclude the unit tests. This might be necessary if you just simply want to only build the database and nothing more perhaps for the sake of reducing the amount of the build time if the intent is to use database for another purpose other than testing.

In the command-line window, as NAnt logs the progress of the database build, at the end it will bubble up the output results of TSQLUnit indicating whether the tests passed or failed:
...
ExecuteSql:

[echo] loading .\Database\Tests\ut_DeleteTables_VerifyTablesDropped.sql

RunUnitTests:

[sql] ====================================================================
============
[sql] --------------------------------------------------------------------
------------
[sql]      SUCCESS!
[sql]
[sql]      14 tests, of which 0 failed and 0 had an error.
[sql]  Summary:
[sql]  Run tests ends:Feb 20 2008 11:44AM
[sql] --------------------------------------------------------------------
------------
[sql] Testsuite:  (14 tests ) execution time: 76 ms.
[sql] ====================================================================
============
[sql]  Run tests starts:Feb 20 2008 11:44AM
[sql] ====================================================================
============

BUILD SUCCEEDED

Total time: 6.1 seconds.
Currently, the overall database build does not fail if the unit tests themselves fail since NAnt can not catch and interpret TSQLUnit results. This codeproject MS SQL Server script validation with NAnt Task seems to provide that level integration between NAnt and SQL Server unit testing. Perhaps, this functionality might be implemented later, but, for now, it is good enough.

Other related posts on TSQLUnit:

It's True (And Not False): Assert Equality in TSQLUnit

Database schema changes unit tested using TSQLUnit

Monday, August 25, 2008

Applying good software development practices to VBA and MS Access (and tools, too!)

After working with TSQL almost exclusively for the last six months the time came last month where I had to make code changes to a Microsoft Access front-end application at work.

Based on what I have learned in the past several years or so regarding software development in .NET, I decided to arm myself for tackling legacy code in a platform and language notoriously known for not being ideal for software development.

IDE Tools

I accidentally came across MZ-Tool a plugin for the VB Editor IDE and wow! This made all the difference in the world. It was nice to find a free tool to inject a little ReSharper-ish support into the rigid VBA IDE. The product apparantly started in the VB\VBA world and when .NET came along it was ported to Visual Studio.NET. (It can actually be considered as a direct competitor to Re#. Note that the .NET version is not free.)

Here are the features I was using actively during coding:
  • Replace In All Projects- This was huge in that it allowed me to actually perform the refactoring technique, 'Rename', with various levels of control. What really made it possible is the actual visual tree view of your code where the text you want to change shows up as well as showing what function/sub where it can be find under. I was not afraid to actually change names of things that were either goofy, confusing or antiquated in coding style into much more meaningful names. In the end, I had significantly less fear in breaking code. (This feature is equivalent to Re# 'Find Usages Advanced')
  • Procedure Callers- Similar to Re# 'Show all usage" for a method or variable (and it's also navigable via a visual tree view)
  • Code Templates- This allows me to create and permanently store boilerplate code that I needed for testing code using VBLiteUnit (see next section). This feature opened my eyes to more actively use and create with 'Live Templates' in Re#.
  • Add Procedure- Slick and fast way to create more boiler-plate code for procs/func.
  • Add Error Handler- This was a gift from the heavens. Being able with a simple keystroke drop in code for error handling in any sub/function. Error handling is horrible in VB/VBA so this significantly helped in reducing the pain.
  • Add Module Header and Add Procedure Header- It's just like having GhostDoc. Adding comments is very important in legacy code.
  • Sort Procedures -Helps organize your code. It is equivalent to Re#'s 'File Structure Popup' which I use all the time for the same reasons: drag-n-drop your code to how you see fit.
  • Private Clipboard- This can be big in legacy code and in a language where you do end up with a lot of cutting and pasting. Actually, better than Re#'s version because you can actually control what is stored in it and can retain it for as long as you like within the duration of your session.
  • Review Source Code- A extremely limited version of code analysis in Re#. It only tells you if a variable, constant, procedure is not being used. But good nonetheless to clean up your code and get rid of cruft.
The following features were interesting but less used day to day:
  • Generate XML Documentation- produces a very nice readable xml doc about your code comparable to XML CHM output in VS
  • Statistics- You can actually see how your lines of code is distributed in your code base.
The one thing I wish MZTools had which IMHO I consider to be one of the two most important refactoring techniques is 'Extract Method' ('Rename Method' being the other). If it had that it would be quite the rock solid refactoring tool for VBA. Nonetheless, MZTool just like Re# in Visual Studio has given me control over my code instead of the code controlling me.

Unit Testing

Being a dedicated practitioner of Test Driven Development (TDD), this was important for me to do on a lot levels. After looking at two options I settled on VBLiteUnit because it is extremely lightweight and the ramp up learning time is short especially if you are familiar with any of the other xUnit frameworks (which is exactly what the author intended in creating it hence the word "Lite" in its name. My belief is that the author might have thought that the existing one out there, VBAUnit, was too bulky and cumbersome to use and maintain. Definitely a much more "pragmatic" and "agile" approach in his solution.)

The author decided to use the VB construct 'Select Case' (think 'Switch') with each leg of your 'Case' defining an individual test. Impressively it works really well (and added bonus is that your test descriptions can be more descriptive and natural in tone because it's just string text. Interesting to note as compared with a blog post on Unit Test Naming Conventions written by a developer I used to worked with)

To implement my new changes, as expected, I did have to do some refactoring of the code that required to be touched. This generally resulted in new testable classes but I had TDD with VBLiteUnit (and MZTools) to lead the way.

All in all it was very satisfying to be able to actually do TDD in Access/VBA. It gave me that same feeling that I get when doing TDD in C#. The platform for a moment felt much more real as software development than it ever had before.

Results

Some lessons were learned of course ("evolve or die"). If you have no choices, resources, etc. in a situation no matter how "trivial" then you still attack your problem 100%. Why? Because you not only get to apply and transfer knowledge and techniques to another area of software development but also you might actually learn some new things that you can in turn use later on. For example,
  • I started using the Immediate Window much more now in Visual Studio.
  • I started using features in Re# that I had not used or dismissed before (like the live templates, copy clipboard, etc.)
  • Design patterns can still be used even with what some might consider a "rudimentary" language. For example, I was actually able to implement a variation of the Repository pattern (ala Eric Evan's DDD) that hydrated a domain object and make it work. Hence if you understand design patterns and how to use them then it does not matter what OO language you are using.
However, I can see why VB developers don't know OOP. One good reason is that VB is lacking in some key features of an OOP language. The biggest one in my opinion is that it does not support implementation inheritance. That was frustrating because I was trying to use a technique that Michael Feathers described in his book, Working Effectively with Legacy Code, to test legacy code. That entails creating a seam in your code by inheriting and then overriding an external dependency. Unfortunately, VB simply just enforces the interface contract but does not carry over the implementation code from the base class to its derived ones.

Nonetheless, using VBA with VBUnitLite and MZTools had actually turn out to be a much more gratifying and, yes, may I even say aloud, "fun" experience than I expected. The main reason beyond the more obvious such as being to do TDD was that I was doing so much TSQL just prior that is was like being transported out of the stone ages. The TSQL language and Microsoft's IDE for it (SQL Server Mgt Studio) was so frustrating to use that working back in a programming language designed for applications even as "minor/toy" and with so much stigma as Access VBA was such a breath of fresh air and extremely invigorating (It was like giving a tablespoon of water to a very thirsty person).

TSQL was and still is such a struggle even with TSQLUnit. It has so much limitations when it comes to creating flow logic, reusable code, and testability. (Granted it is a DSL specialized and intended for manipulation of data and its storage structure but I feel like that the SQL language has not really evolved much in its odd 30 years of existence. In areas that it has changed it ends up resembling other non-SQL languages so what's the point? Perhaps it's time for a better replacement language(s)?) In the future, any application developer that I encounter again who says "sprocs are the way to go" will get an earful and maybe more.

TSQL is so painful in its existing form that, believe it or not, I rather be working in VBA than in TSQL if those were my only options!! I know that sounds crazy and shocking but that's how much I prefer not to have to do database development and deal with TSQL. I'll leave it to the folks who actually enjoy it. But sadly it is the bulk of my work these days. (The one thing I do like about it is it's language support for "dynamic SQL" but that is not enough of a motivation for me.)

Who knows? I went from C# --> SQL --> VBA --> SQL in the last six months. If I had gone from C# --> VBA I probably would have a different opinion and experience. Hard to tell in retrospect.

Don't worry I still do C# and have been doing it for the last six months in parallel on side projects and supporting processes at work. Unfortunately, I am back to doing TSQL for the next several months. Aargh!!!

Final Thoughts

What was the point of all of this other than to rant on about a language no one cares about if you consider yourself a "serious" programmer? Well, just like VB6, Microsoft plans on retiring VBA and possibly allowing Office apps to be supported by any.NET language including C#!! Therefore, the tools mentioned above are "dead" in the sense no new development is going be done on them especially that their respective authors have appeared to have moved on from doing any new releases.

IMHO, I think what Microsoft really should do is to allow dynamic languages to support their Office apps. That is where dynamic languages (ex. Ruby, Python, BOO, etc.) seem like such a natural fit and obvious for the nature of that work. Just imagine you can write code very quickly with ever-changing requirements and it does not have to perform fast (as compared with statically typed languages.) Seems like such a "no-brainer". (maybe in the far, not-too-distant future, power users could even be writing MS Excel or Word macros in F#? Just imagine that!)