views:

226

answers:

4

Hi all,

I've recently been studying TDD, attended a conference and have dabbled in few tests and already I'm 100% sold, I absolutely love it TDD.

As a result I've raised this with my seniors and they are prepared to give it a chance, so they have tasked me with coming up with a way to implement TDD in the development of our enterprise product.

The problem is our system has evolved since the days of VB6 to .NET and implements a lot of legacy technology and some far from best practice development techniques i.e. a lot of business logic in the ASP.NET code behind and client script. The largest problem however is how our classes are tightly coupled with database access; properties, methods, constructors - usually has some database access in some form or another.

We use an in-house data access code generator tool that creates sqlDataAdapters that gives us all the database access we could ever want, which helps us develop extremely quickly, however, classes in our business layer are very tightly coupled to this data layer - we aren't even close to implementing some form of repository design. This and the issues above have created me all sorts of problems.

I have tried to develop some unit tests for some existing classes I've already written but the tests take A LOT longer to run since db access is required, not to mention since we use the MS Enterprise Caching framework I am forced to fake a httpcontext for my tests to run successfully which isn't practical. Also, I can't see how to use TDD to drive the design of any new classes I write since they have to be so tightly coupled to the database ... help!

Because of the architecture of the system it appears I can't implement TDD without some real hack which in my eyes just defeats the aim of TDD and the huge benefits that come with.

Does anyone have any suggestions how I could implement TDD with the constraints I'm bound to? Or do I need to push the repository design pattern down my seniors throats and tell them we either change our architecture/development methodology or forget about TDD altogether? :)

Thanks

+1  A: 

I think the mere fact that you are using ASP.NET Web Forms your road to TDD will be challenged. It's just a nightmare to mock the Session/ViewState/HTTPContext in Web Forms so test-driven is almost a hindrance. There is of course the ASP.NET MVC alternative, but you're way down the road already it seems. Another promising option for the Web Forms paradigm is ASP.NET Web Forms MVP, but that project is really not fully mature yet. I'm moving everything I can to MVC; the other option for TDD with Web Forms, Selenium, isn't really TDD as it should be.

Nissan Fan
Cheers Nissan Fan for your feedback.I am heavily challenged yes :) as for implementing MVC or MVP I'm not aware of our architecture team looking into it, as far as I know our new development platform for our new product will be using WPF and Entity Framework - still tightly coupled. I fear unless I get heavy backing, the chances of implementing MVP, MVC or the repository design pattern to aid with TDD will be slim to none. I just want to make sure I do as much research as possible, trying new things and exploring ideas etc. I don't want to just give up and easily and disappoint.
Chris D
I disagree with webforms being a nightmare! Not having seperation of concerns is a nightmare! Using the model-view-presenter pattern correct makes everything very easily testable (I should know, I have it in production) and should make it that you never use the caching stores in your code that isn't behind an interface as all of the reading/writing of those should be done at the View layer which is an interface or at the model/service layer which is an interface.
Chris Marisic
WPF is a smart-client technology; not web. If that is in fact what you'll be working with, it lends itself beautifully to the Model-View-ViewModel pattern, which is highly de-coupled and testable. A new, greenfield application is the perfect opportunity to test-drive test-driven.
Jay
+6  A: 

Nitpick: you can't do Test-Driven Design on existing code, but I do realize that you wish to use TDD against new functionality you implement on the existing code base.

The best thing you can do is to gradually introduce seams into the code base, shielding you from the tightly coupled code already in existence.

The book Working Effectively with Legacy Code contains much good advice on how to turn a legacy application into a testable application.

Mark Seemann
+1 for recommending WELC. It is your only man.
APC
+8  A: 

Just to clarify, Test driven development and unit testing are not the same thing.

  • TDD = Write your tests before your code.
  • Unit Testing = Writing tests that confirm a small unit of code works.
  • Integration Testing = Writing tests that confirm blocks of code work together.

TDD, by definition, can't be done on existing code. You've already developed the code, so you aren't going to develop it again. TDD means you first write a failing test, then you write just enough code to pass the test. You've already written the code, so you can't do TDD.

You can write unit tests for existing code but this isn't the same as doing TDD.

The tests you have described (accessing the database etc) are technically integration tests. Integration tests do usually take ages to run. A true unit test would purely test your DA layer code without actually accessing the database. True unit testing requires interfaces to test against so you can isolate units from the surrounding units.

It's very hard to properly unit test existing code, unless it's been well designed with interfaces and abstraction in mind.

I know I'm being slightly picky with terminology here, but it's important when it comes to what approach you can take. My advice would be that with existing code that isn't well abstracted you should gradually build up a suite of automated integration tests. When you come to write something new (Which may not be a whole project, it may just be a new part to the existing app), consider approaching it in a TDD style. To do this you will find that you need to write some interfaces and abstractions to allow you to do TDD on your new code without triggering too much of the existing code. You will then be able to write some more integration tests that test your new code working with the old code.

To cut it short - don't try and change methodology for existing code. Just do new code better.

Simon P Stevens
Thanks for the response, some excellent feedback.I won't get a chance to adapt old legacy code and create integration tests, we push to develop new features soo much that existing stuff gets left behind unless there's a problem or a new feature is to be added. It's such a competitive environment our time cannot be justified working on anything other than some new product that will generate revenue. As for new products going forward, with the way we develop (which I have v.little influence over) then all I can do are integration tests? does this defeat the purpose of TDD?
Chris D
Just to throw in my 2 cents while I understand the notion that database tests are "integration" tests, I do definitely feel they're important to do as a standard unit test as I really want to know my DAL will return me real objects and save data and not throw errors that I didn't expect from schema/permissions issues etc. I believe it's a myth that you could mock your entire database layer's physical execution and have meaningful tests. However generally I separate true persistence test project in their own tests vs nonpersistant tests.
Chris Marisic
@Chris D in response to your comment it sounds like the true issue is that your workplace hasn't adopted the SOLID design principles which allows code to be developed that is testable. http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod
Chris Marisic
Absolutely yes!, spot on Chris. Whilst I am trying to develop this way, alot of the existing code are classes with nothing but static methods that can't be re-used, and the code in these methods are often duplicated throughout the entire system, often in the code behind of the ASPX page. The design is to be desired but getting better, but even still we develop so tightly coupled to our data access layer. I'm really after some way that I could implement TDD and not just integration tests ... or should I just accept I can only implement integration tests in this environment? cheers for link :)
Chris D
@Chris M: yes I totally agree, if I didn't explain it well in my answer, there is definitely plenty of value to doing good integration testing too, particularly for data storage. @Chris D: Unless you can design in a way that supports TDD you are kind of left with just integration testing, but automated testing in any form is good, so don't be too disheartened. do TDD when you can, and unit/integration tests when you can't.
Simon P Stevens
Thanks Simon, it's all starting to make sense. I believe you're right and that I'm going to be stuck doing integration tests so I need to ensure my seniors understand this. I guess I was hoping for a bit of magic and someone to give me the perfect solution but hey, I can always dream of TDD :)
Chris D
There is 1 possible source of magic that could let you do TDD against your existing code base would be to consider TypeMock, it's a moderately expensive tool for mocking (especially when there is great free ones with RhinoMocks and Moq etc) but I believe it's supposed to be able to mock ANYTHING which even includes tightly coupled code.
Chris Marisic
A: 

For adding tests to your existing code, you might check out Working Effectively With Legacy Code. "Legacy code" is defined as code that does not have tests in it.

pc1oad1etter