Showing posts with label Programming practices. Show all posts
Showing posts with label Programming practices. Show all posts

Monday, 1 October 2007

TDDing a new feature into the existing code

Suppose you have managed to find out the conditions that lead to a crash. How do you fix it in a TDD fashion? Or, how do you add a new feature to the existing code?

I usually try to resist using the existing code at first. Instead, I modify my production code so that the new conditions take a new path of execution. For example, I assign a certain ID to some object that is part of my test setup, then in the production code I check for this ID, and if it fits, I take the new path. This way I make sure that all my previous tests go to the old path, and since I don't touch the old path code, they will pass for sure.

Next, I force the new test pass as usual. I don't even need to check the other tests -- they are not affected.

Now comes the interesting part. I'm refactoring the new path as usual, watching the new test, but I'm trying it the way it looks "similar" to the old path. I might factor out a new method that looks like the one that's been used by the old path. Sometimes I could even use an old method. If the new test breaks, the old method needs to be modified. Or, I could modify the old method (now I should also watch the old tests). If it's too complicated to incorporate the new behavior, I repeat the same trick: split the path into two, and try to make it one in smaller steps.

With each small step, the difference between the two paths diminishes, so at the end we have a common path of execution, and we can remove our initial split.

Thursday, 26 July 2007

Spaghetti on Rails

Yesterday I was totally enraged by the comments to this post. It was so bad I couldn't sleep until I got out of my bed and wrote a comment. Shame on me.

Anyway. People recently talk a lot about this Monorail thing, and how it encourages separation of concerns, testability, and maintainability of your code. I've been sort of curious, but not enough to dig into examples. So, judging from this post and the code example somebody provided, turns out all they can do is the PHP style spaghetti code.

But the most amazing thing is that how much effort these guys put into arguing that 1: This is the best method since it's putting presentation and presentation logic together; 2: Microsoft is bad (again) because they invented the DataGrid (and presumably forcing us to use it everywhere); and 3: "we can do it with less lines of code".
Chill, guys, peace and all that.

Sunday, 15 July 2007

My first attempts at "proper" TDD

Although I've used it several times in the past, my first serious attempt was with FreshReports. That is, I wasn't testing it manually at all.

FreshReports is a printing component (a framework wannabe). It uses xml files as the source for design and in-memory objects (in contrast to the majority of such components that use database rows) for the data.

Naturally, I quickly figured out some basic design, wrote a trivial xml file and started to write tests. In addition, I've been inheriting from System.Drawing.Printing.PrintDocument, and used the hardcoded System.Drawing.Graphics object. Just like all tutorials on printing suggested.

Like any amateur, I thought I was doing unit testing, but in fact I was doing integration tests. I thought I was doing test-first, but in fact I designed something first and tried to fix my bugs with testing. Well, I was doing just fine. I stumbled upon TypeMock, and this wonderful tool allowed me to do a lot of testing without all these IoCs and SoCs people talk about. I said I was doing just fine because, although I could spend an hour or two in the debugger trying to figure out what's going on (or lately, after I read in some blog post that I shouldn't be in the debugger, I started to log way too much to the console and also started writing tests for my private variables), I've been doing it all the time before. I just didn't know there could be a better way.

At least I learned what a mock is. Because before that I thought it was too complicated for me.

My second attempt was with Dormidontus. I've learned a lot since then, and I have heard that unit testing is about testing small isolated pieces. Armed with TypeMock, I could mock everything my methods call (even the class I've been testing). So I decided to move with small steps. I figured, for example, that my main workflow consists of three steps. So, I happily mocked three method calls (because I already figured the classes responsible for these steps), then wrote the main method and got a green. Now, each method call could be naturally broken into smaller steps, so I mocked these smaller steps, repeated the calls in the production code, got a green again.

In short, most of my tests (until I got to the lowest level) contained no asserts, but just mocked calls to other objects. I guess this is what you call interaction-based tests. All went rather smooth, however, I felt that something is wrong. First, my test code duplicated my production code. There was no way to write my production code other than it was dictated by my tests. So, in a way my design was driven by my tests, but at the same time I knew my design before I even started to write a test. Second (a direct consequence), it was impossible to refactor without breaking the tests. Given these two, I felt that my tests are pretty useless, except for the fact that I didn't have to debug my application.

I then posted a question to the testdriven yahoo group regarding testing my high level methods that don't do anything except for calling other lower-level methods. After I received a couple of very clarifying answers, I realized my mistake. Instead of trying to provide some business value as soon as possible, I was focused on my oh-so-elegant design.

I still believe that TypeMock is terrific. It is a pure magic. For example, back to FreshReports, no matter how you abstract and wrap the Graphics object, eventually you should test your wrapper, or adapter, or proxy. You just can't do it without TypeMock.

Some people dislike TypeMock for it does not force you to apply "good" practices. Not only I disagree, it drives me mad. I strongly believe that grown-ups should not be forced. But that's a good idea for another blog post. Now I admit that with regards to TDD I'm far from being a grown-up, so perhaps being forced (or I'd say, led) could be a good thing for me until I'm more confident with these things. However, I'm still thankful since TypeMock gave me a soft start with mocking, and also gave me these lessons.

With Dormidontus, I managed to switch to the right path right in the middle of the project (yes, I even managed to force myself to delete a good part of the existing code). As for FreshReports, with some hesitation I decided to build it again from scratch. I've got the second beta of Orcas Express (too lazy to download and too scared to install the full version). The first beta refused to work with TypeMock, so I decided that it's a sign and I should try RhinoMocks.

Now I'm fighting through my first test.

Wednesday, 11 July 2007

Custom exceptions in custom components

Just published my first article at CodeProject.

At the end they suggested I wrote some conclusion, so I got into a philosophical mood and wrote:

I strongly believe that for every programming pattern out there -- be it GoF, Microsoft, or your own, there should be a corresponding pattern of exception handling and/or raising, or at least the exceptions should be made an essential part of the pattern. After all, exceptions are classes, so many existing patterns could be applied to them. Perhaps some day we'll hear about Exception factories, Exception Strategies, and Exception Observers?

This is interesting, I do read a lot about design practices and patterns lately, and exceptions seem totally out of sight. Why? It's almost like not mentioning the devil..

Thursday, 28 June 2007

Build your own CAB series

Just finished reading the series on WinForms programming patterns by Jeremy D. Miller. Although I've been reading a lot of stuff on "how-to-do-it" recently, these posts really stand out. First, they are a big fun to read. Sometimes Jeremy explains his ideas with the help of four musketeers applying these patterns while fighting with their enemies. But even back to the coding, Jeremy's language is clear and simple. And second, and here goes my respect, he doesn't make it a dogma. He nevers says "you should do it this way coz that's the right way doing this". He says, in these situations I usually do it this way, and it helps me because of this and that. But yes, you can also do it another way if you want, but beware of this and that.

Sunday, 17 June 2007

When good programming practices are just too good

Recently I saw an article on CodeProject (the article itself is very good, so I'd rather not post the link here) that had a great explanation of things like MVC, flexible design patterns, dependency injection, programming with interfaces etc.

What struck me in this article is the ridiculous amount of code required to implement simple things. The author explained everything using a WinForm application as an example. So, the dependency injection was implemented not just for the form, but for a button as well! The author designed an interface for the Click event, and an extra class that implemented this interface and forwarded the call to the underlying button.

Now, I see it as an excellent example of good programming practices -- everything is very clear. But at the same time, it is an example of what happens when a good practice it taken to an extreme. Take a look at this snippet:

Private Sub OkButton_Click(...) Handles OkButton.Click
DoIt()
End Sub

Now, a "good programmer" would design an interface that provides the Click event, then design a class (say, GoodButton) that implements this interface and use it as a button on your form. Every single method or property that you need with a button you should put into the interface and then implement in your control, propagating them to your underlying button. In a way, you have "triplication" of your code here, and you write a lot of code that actually doesn't do anything, except for making your program "well designed" and use "proven practices". Why? Because this way "you have no hardcoded dependency on your Button class". Doesn't it sound ridiculous? And think of it, you now has a class GoodButton that just got a hardcoded dependency, and you have your form that has a hardcoded dependency on your GoodButton. Unless, of course, you put the class name in your config and load it dynamically. This way you just have to change "Button" in your config without recompiling the application.

Sounds familiar?

Next, you, of course, is going to test this code. You want to test that each time the button is clicked, the method DoIt is called. In order to do it you, perhaps, will make it public (which is clearly against the best practices), have some mock tests etc. But think again -- do you really want to test this 1-liner? How many lines of test code do you want to write, how much time to dedicate to fix bugs in your test code? Do you really think you are going to refactor it at some time? If yes, just put a comment: "NOT TESTED: REFACTOR WITH CARE" next to it, and you'll feel fine. Use NUnitForms to test your GUI, use TypeMock to test your hardcoded dependencies, and use interface-first design where you feel you need flexibility. In other words, use "thought driven design".

We all love rules -- with them, you don't have to think much, you just apply what the smart guys think is the best. However, lately we younger generation begin rebel and declare these rules as "elitist crap". This happens in response to posts like this, when convenient tools are slammed down just because they don't force "good practices" on us. You can guess that the least thing I want is that something enforces me to do that somebody else thinks is "good". As a result, after I spend too much time on implementing some practice that I don't need, next time I prefer to forget about it. Same thing with testing -- after spending too much time on 100% testable projects, I discovered that I tend to skip testing totally, which proved to be very wrong decision.

So, my idea is -- follow the big guys' ideas, but don't take them as rules, rather as advices. Implement them in say 90% of your application, and carefully weight all benefits each time. This is especially true in places where the code is autogenerated (it's already flexible enough: it takes little effort to replace a dependency, and you can trust it and use without testing). Your business layer is a good place for these practices. Hell, you might even want to change your ORM tool at some point!

To summarise the whole Thought-Driven-Development-versus-Big-Guys issue:
- Always think about the benefits of the proposed pattern. Don't get content with words like "flexible" and "eliminates dependency", even more with "proven". Think about what "flexible" means in your case, and what is "inflexible" if you do it straight, and why it is bad to be "inflexible" here. Estimate how much do you have to change if you need that flexibility later.
- Always think about the costs. How much to you need to code just to make it flexible or testable. Note that you don't have to make it testable with TypeMock, for example, but that's considered a "bad" tool among purists, because it doesn't "enforce" us (meaning it liberates us a lot, actually).
- If you discover that thу proposed practice makes your design unnecessarily complicated, unclear, adds too much nonfunctional code, or in any other way distracts you from your main purpose, write a blog entry about it, or comment somebody's article on good patterns, in order to provoke thinking and stop people from blindly following the rules.