I can't put into my head how to design an app in order to perform unit tests. In particular when you have objects that depends on database queries, or other external factors (network/serial communication, etc...) Do you have any reading suggestion ?
If you are talking to a database you should be able to mock the database connection. The code which then performs the query can be tested, and the results can be controlled through the mock and tested.
You might have to write an extra layer which can be interfaced and mocked depending on the external factor you are dealing with. Sometimes this is a good idea as it abstracts you further from a specific implementation or vendor. But it adds what can appear to be extra code for no gain.
I know I'll get a shower of "Boo"s for this and I am aware it's not "by the book" and I understand my case may be peculiar :) but I just create a new datamodule, copy and paste the db components and use that :) I know it's probably not how it's supposed to work :) but still works for me :)
IMO, isolation is key. If your code has layers where you can strip away dependencies, you increase the success rate. Test separately The inner core logic which process data elements The outer core logic which process data sets The persistence logic which retrieve and store the data sets The process logic which handle retrieving, processing and storing data sets The event control logic which decides when or why you process data ... and so forth
If you have GUI app, it helps immensely if your biz.logic is strictly decoupled from your UX - but - unfortunately this is very often not the case. States are so often kept in visual controls, which makes it really hard to automate testing of the stuff that the UX is supposed to just be a thin, glossy layer above.
Thank you Linas Naginionis ! I began to read "head first design pattern", but as Nick Hodges decided to do the Delphi translation work, I'll concentrate on your books ;)
In depends on what you want to test. If I need to test an object, which data and behavior depends on data in database, I run scripts putting the necessary data to the database in Setup method of TTestCase, test my object methods, and dispose data in Teardown method.
Since you asked for reading suggestions I'm going to recommend "Working Effectively with Legacy Code" by Michael Feathers. It is a treasure trove of advice on turning untestable code into testable code. Examples are in Java and C++ but are clear and to the point. Worth every penny.
http://en.wikipedia.org/wiki/Mock_object
ReplyDeleteIf you are talking to a database you should be able to mock the database connection. The code which then performs the query can be tested, and the results can be controlled through the mock and tested.
ReplyDeleteYou might have to write an extra layer which can be interfaced and mocked depending on the external factor you are dealing with. Sometimes this is a good idea as it abstracts you further from a specific implementation or vendor. But it adds what can appear to be extra code for no gain.
Some resources that I found readworthy. Other than that, Google search is your friend for finding more stuff to read.
ReplyDeletehttp://www.methodsandtools.com/archive/archive.php?id=103
http://programmers.stackexchange.com/questions/153410/what-are-the-design-principles-that-promote-testable-code-designing-testable-c
http://misko.hevery.com/code-reviewers-guide/
I know I'll get a shower of "Boo"s for this and I am aware it's not "by the book" and I understand my case may be peculiar :) but I just create a new datamodule, copy and paste the db components and use that :) I know it's probably not how it's supposed to work :) but still works for me :)
ReplyDeleteIMO, isolation is key. If your code has layers where you can strip away dependencies, you increase the success rate.
ReplyDeleteTest separately
The inner core logic which process data elements
The outer core logic which process data sets
The persistence logic which retrieve and store the data sets
The process logic which handle retrieving, processing and storing data sets
The event control logic which decides when or why you process data
... and so forth
If you have GUI app, it helps immensely if your biz.logic is strictly decoupled from your UX - but - unfortunately this is very often not the case. States are so often kept in visual controls, which makes it really hard to automate testing of the stuff that the UX is supposed to just be a thin, glossy layer above.
Mocking implies necessarily interface usage ? You must define interface for each object to mock ?
ReplyDeleteI can recommend a few books, they should help you to get on the right track: http://www.amazon.com/Test-Driven-Development-Kent-Beck/dp/0321146530 and http://www.amazon.com/Art-Unit-Testing-Examples-Net/dp/1933988274
ReplyDeleteThank you Linas Naginionis ! I began to read "head first design pattern", but as Nick Hodges decided to do the Delphi translation work, I'll concentrate on your books ;)
ReplyDeleteIn depends on what you want to test. If I need to test an object, which data and behavior depends on data in database, I run scripts putting the necessary data to the database in Setup method of TTestCase, test my object methods, and dispose data in Teardown method.
ReplyDeleteI will never be able to explain that to my colleagues who are sticked to Delphi 4 ... ;)
ReplyDeleteSince you asked for reading suggestions I'm going to recommend "Working Effectively with Legacy Code" by Michael Feathers. It is a treasure trove of advice on turning untestable code into testable code. Examples are in Java and C++ but are clear and to the point. Worth every penny.
ReplyDelete