GUI testing with DUnit
I don’t how I didn’t know this until now but it turns out there’s a whole section of DUnit designed to help you test GUI elements. What follows is small write up about how to do it. Read more…
I don’t how I didn’t know this until now but it turns out there’s a whole section of DUnit designed to help you test GUI elements. What follows is small write up about how to do it. Read more…
Craig Stuntz recently posed the question of how to build hedges into our products and processes.
If I understand the problem correctly what we want is a way to mitigate the risk of choosing the wrong technology or technique. This is to say if we do choose the wrong technology or technique (for whatever reason) how do we recover and change direction with a minimal amount of fuss and cost.
I think the biggest guiding princple we have for this is Deferred Implmentation. Deferred Implementation demands implementation agnosticism from the client code. We’ll use the User Repository code from the post Keep you code lazy and ignorant as an example. When we wrote the code that needed to look up a user by the user name the client code was ignorant of how the data got there. This was good because it meant we could focus on the business logic of logging in and leave the details of how retrieve user information for later. All we needed to supply was an API for the client code to use. Later, in the next post, we wrote the code to retrieve the user data using an ADO connection to a SQL database. The truth is we could have used any storage medium like an XML file or web service. Either way the client code didn’t change because of the implementation details in the User Repository. Doing this created a hedge in both the barrier sense and the risk mitigation sense.
TDD played a big role in getting to the deferred implementation of the User Repository. We were able to quickly put together a test bed for our ideas and keep it to be sure we didn’t inadvertently break anything. TDD also gave us a formula for the incremental improvement of the code.
TDD by itself didn’t get the job done. We needed to be conscious of separation of concerns, not mixing object creation with business logic and design patterns that help us solve these sorts of problems.
I hope this addressed the question Craig posed. If not, I hope this was at least informative.
This post is a follow up to Busting the great TDD Myth. If you haven’t already read it, please check it out.
Lets review the code from the last post:
function TLogin.GetConnectionString : string; var reg : TRegIniFile; begin reg := TRegIniFile.Create('Software\MyApp'); Result := reg.ReadString('Database', 'ConnectionString', ''); FreeAndNil(reg); end; function TLogin.Execute(const UserName, Password : string) : Boolean; var UserQuery : TADOQuery; DBUserPassword : string; begin UserQuery := TADOQuery.Create(nil); try UserQuery.ConnectionString := GetConnectionString; UserQuery.SQL.Text := 'SELECT Password FROM Users WHERE UserName = ' + QuotedStr(UserName); UserQuery.Open; DBUserPassword := UserQuery.FieldByName('Password').AsString; UserQuery.Free; Result := DBUserPassword = Password; except Result := False; end; end;
Now lets review the problems:
All of these problems can be summed up as: Too much ceremony and too little essence. What is this class trying to do? Does creating a connection to the database, querying a table and handling exceptions have much to do with the purpose of logging in to the system?
WARNING
There’s a lot going on in this article. It is intended to be humorous and slightly inflammatory. It’s also the first of a series. I intend to show you how the average Joe digs himself into a hole and then how to get out. I hope you enjoy the read.
On with the show …
You will not learn what you need to know from a TDD course/book/lecture. When you walk away you will not be able to write better software. You will not be able to test drive all your code in a fully automated test suite. Your expectations will not be met and you will quickly dump the whole concept of Test Driven Development.
It’s true.
The problem has nothing to do with TDD. The problem is that you don’t know how to write better software. TDD will not make you a coding rock star. You will not be the envy of your peers. Your face will not be printed on a t-shirt (I don’t think that’s ever happened but it would be cool).
I just encountered a great article on Ward Cummingham’s wiki. It’s called Programming Is Not Fun. The section that I enjoyed is called “Programming Is Not Fun without Philosophy”. You can find it near the bottom of the page.
I’ve always considered programming/engineering/development to be more art than science. I guess you can chalk it up to my background in the arts. What hit home for me was the assertion that any kind of work done without an underlying philosophy is wicked boring and unfulfilling.
The article notes that other arts have established and accepted schools of thought and philosophy. For instance, painting has Cubism, Impressionism, etc. Do we have schools of thought and philosophy? Do we have Waterfall, Agile, Do-Whatever-It-Takes-To-Ship, TDD, BDD?
What about you? Do you employ any sort of philosophy in your work?
Take the following test:
1. Take a class, any class and instantiate it in a test harness.
2. Find a coworker, let them read your code and tell you what it does.
3. Count the instances of duplication in your code.
Why take this test?
Number 1.
The ability to instantiate any class in a test harness demonstrates the level of decoupling and cohesion of your code. Why is that good? Highly decoupled cohesive code promotes: seperation of concerns, low duplication and extensiblity. You end up with only as much code as you need. You also get the power to duplicate and isolate customer found defects before fixing them, giving you a collection of tests you can rely on to prove the integrity of your code and design.
Number 2.
To often we write code that is write-once/read-never. Your code should be expressive of it’s intent and easy to read. It should do what it says and say what it does. Code spends more time being read and maintained than being originated. Additionally, when you’re writing highly decoupled code the more readable your code the more likely someone can come behind you and maintain/extend your work.
Number 3.
Duplication is the bane of maintainability. You (and I) have copy-and-pasted code from one unit to another to tweak it for some slightly different functionality. Stop it. Refactor that code into a superclass or into seperate object and use a subclass for specific functionality (see the Strategy Pattern). Duplication has more than one face: copy-and-paste (as I just talked about), and reinventing the wheel. On a larger team it easy to reinvent functionality that another team member wrote prior to you. Don’t be afraid to query your peers when you get that “someone must have done this before” feeling. You’ll reduce the complexity and size of your project by eliminating functional duplication.
How do you get there?
Check out:
So how did it go? Post your test experience in the comments.
Full Disclosure
I stood on the shoulders of the following giants to write this post:
Dave Astels
Michael Feathers
Recent Comments