
|
Version Control and Unit TestingIn many software houses version control and unit testing are the poor relatives of software development. Everyone knows that a programmer needs a text editor and a compiler (or interpreter) to build an application. Modern IDEs have demonstrated that they can have a phenomenal impact on the development cycle, despite their high learning curves. But what are the advantages of learning about, and using version control or unit testing?
This article explains the advantages of using these development tools, and will, hopefully, convince you that they are quite as essential in the development process as the compiler is.
Version ControlAlmost everything I write on my computer gets stored in my version control repository. I just committed this web site project, since I'm starting this article, and there were 7 additions, and 8 changes since I last committed the project. There were also two images I'd forgotten to add to the project from the previous article. Later on today, I'll backup the repository, which will save all the projects that I'm currently working on.
I convert this OpenOffice.org document into a web page using the odm_to_xhtml program, which has changed considerably since I first started working on it. I also know exactly how many changes, and where they are. That's because I use Subversion to keep the source files under version control. Let me show you the textparser.rb file history:
![]() Since I am a lone developer for this project, you'll only see me as the author. On multi-developer projects, you'd see the login names of each author making the commits. The messages are stored for each commit, and give a brief reminder of why the commit was made. The bottom window shows all the other files that were committed at the same time as textparser.rb.
I can also pull out some statistics, which is why I know exactly how many changes there are:
![]() I can see how the file looks today, showing each line by latest revision (it's called blame, because I can see who is responsible for each line of code in the current version):
![]() I can also see what the changes are, from any specific revision to another:
![]() The left hand side shows revision 15 (the first time the file was committed to the repository), and the right hand side shows my current working copy on my hard disk. Where the cursor is pointing, I can see the specific changes on a character by character basis in the bottom text box.
That's enough of pictures, but version control doesn't stop there by any means. When a version is released I can create a branch at that point. I'll probably continue working on the project in the future, because I tend to add features, as and when needed. But if I get a bug report for the released version, I can restore that version to my working copy. So despite the fact that I may now have a heavily modified development version of the program, which may or may not be working properly, I can set up my working copy with exactly the version specified in the bug report.
When I've finished fixing the bug, I can update the version branch, and re-release it. I can then merge the bug fix back into later version releases, and my development version, by checking exactly what code I modified to fix the bug, and then adding those modifications into each version branch, and the trunk (the development branch).
Subversion has become a successful open source project, with plugins for IDEs such as Subclipse, GUIs for Windows, and multi-platform, integration with Apache, and web based issue tracking software. There is also a freely available book from O'Reilly.
Unit TestingWhenever you make a software product available to others, whether it be a library, command line, desktop or web application, it will be tested – by the user. The user expects that every test he makes will succeed – the program will perform correctly. When the user's test fails, you have a problem, and probably an upset user.
Naturally enough, the development team will also test their software. The usual practice is to write code corresponding to a particular problem to be solved, then execute the code feeding in valid and possibly invalid values. Once the tests have been checked, and the code modified to work correctly, the programmer will move on to the next problem.
Although this may seem a satisfactory process, it has several flaws.
Unit testing is a process of applying general programming practices to develop suites of tests which can be repeated, and eventually automated. Ron Jeffries' site provides a list of unit testing frameworks for a variety of languages, as does the opensourcetesting.org web site. Tim Burns has written a brief article entitled “Effective Unit Testing”, which takes a balanced view of the topic.
Many articles have been written on unit testing, practices, design patterns, and philosophy. Fortunately, the original ideas presented by Kent Beck in his article “Simple Smalltalk Testing: With Patterns”, still hold true, irrespective of the great deal of hype subsequently generated.
Unit testing is not a recipe for perfect “bug free” software. It does require extra effort by the programmer, which is an extra cost. It is perfectly possible that the unit testing code itself contains bugs. In some circumstances, it becomes necessary to construct mock objects, particularly for database transactions. Modifications to the code may require changes to the unit testing code. Unit testing can produce over confidence, especially where the tests do not cover 100% of the code. Code coverage of unit tests can be difficult to analyse.
Apart from the obvious task of creating a controlled, documented (the unit testing code itself), consistent and repeatable set of tests, the process of writing unit testing code can also produce other benefits. The extra effort required increases disciplined programming, and induces the programmer to write the minimum necessary to complete the task. The effort of isolating the code, to increase testability, makes the final code more loosely coupled, which increases re-usability. The unit tests provide a reference which allows the programmer to refactor code more confidently and aggressively. Unit testing code becomes the first “user” of publicly defined interfaces and libraries, thus guaranteeing the consistency of those public definitions, both during compilation, and at run-time. Finally, unit testing code can also provide a historical database of previously fixed bugs, thus ensuring that those bugs do not reappear in future releases.
ContactsSyger can be contacted for consultancy work on any of the topics mentioned in this article, by sending an email to info@syger.it.
|
Tag cloud: |