jwcolby
jwcolby at colbyconsulting.com
Tue Apr 12 11:03:54 CDT 2011
My question is, why do regression testing if we never regress? I write these functions and they are done. They work or they don't. We are currently testing them manually. Take the flag class, dead simple. It reads / writes to SQL server or it doesn't. We test the flag by actually using it and watching that the flag sets. It works, it is used forever. That is a simple example but the concept extends. It is not that we do not test, we do but once these functions work we are not modifying them. Actually in fact, we do paradigm changes (code functions custom written to read / write the flags in SQL Server to a flag class), but even here, the new flag class entirely replaces a couple of dozen functions, each of which wrote one flag. I would like to stress that it is not testing that I question, it is not unit testing that I question, it is when and where it might be applied, and when and where it is more trouble than it is worth. John W. Colby www.ColbyConsulting.com On 4/12/2011 10:09 AM, Jim Dettman wrote: > John, > > If your doing any type of n-tier design, unit testing is a must and you > write the tests as you develop the objects. > > As to your point about bugs and lines of code, unit tests are very simple > and limited by nature. You only test one very specific thing with each. It's > not one all encompassing test against your entire app, but rather a series > of test (possibly thousands). > > For example, I develop a customer class, which handles CRUD operations for > customers. > > As result, I would develop four separate basic unit tests for that object: > > 1. Adding a record. > 2. Deleting a record. > 3. Reading a record. > 4. Updating a record. > > Then I would develop a unit test for each of the business rules, say on > credit limit, can't delete a customer with open items, etc. > > So just for the customer object alone, I might have dozens of unit tests. > > Jim. > > -----Original Message----- > From: accessd-bounces at databaseadvisors.com > [mailto:accessd-bounces at databaseadvisors.com] On Behalf Of jwcolby > Sent: Tuesday, April 12, 2011 9:03 AM > To: Access Developers discussion and problem solving > Subject: Re: [AccessD] AccessD Digest, Vol 98, Issue 7 > > I guess I just don't "get it". OTOH there are a lot of things I don't get. > > When I write a class, I look at the functions that class needs to perform > it's job. If it needs a > function that is used elsewhere (in Access) I go to a lib to execute that > function. To a smaller > extent I do the same in C# (static class methods). > > But in general the class functions are only used in the class. A function > can be made to accept > args and return a value and never modify anything external to itself. It > would make programming > some functionality much more complicated however. Let's take an example. > > I have records in SQL server where the record itself represents an object. > A "supervisor" > represents a database which needs a specific process applied (address > validation). Due to > limitations of the third party address validation program, the sets of tens > of millions of addresses > have to be broken down into 2 million record "chunks". The process table is > child to the supervisor > and each process record represents a chunk of up to 2 million records. > > Address validation of a table of addresses is an extremely complex task > requiring dozens of steps. > The Supervisor (parent) and process (child) tables contain flags to store > state, "Process X has > completed". It takes an entire SQL statement to write that flag back to the > appropriate table > (parent or child) / field. So I have a "flag class" where I initialize the > class with the PKID of > the record that contains this data (flag), the field name, and the table > name. Now the flag class > can accept a data and write that data to its specific table / record / > field. > > So (to get back to the subject at hand) there is a process that creates a > temporary database and a > table to hold the tens of millions of records needing processing. The > process builds that. No flag > is used, we just ask SQL server whether the objects exist and create them if > not. When we *fill* > that table, a piece of SQL code executed in a function. That function takes > database / src view > information (which it does not modify) and returns a boolean true (SQL > Server says it did the > operation) or false (SQL Server threw an error). > > However the function also logs to NLog (modifies information outside of the > function) with logging > type of stuff such as the database name, table, number of records affected > etc. *IF* the table > filled, the function also directly calls the class property to set the flag > (remember the flag > class?) saying that it successfully filled the table in the temp database. > The pro[erty actually > calls the flag class and the flag class writes the data back to SQL server > right then and there. > > The function's reason to exist is to fill a table in a temp database with > data from a view in a > "live" database. The function itself does not modify the parameters passed > in. It returns a true / > false which makes the control logic a simple if (the table filled) then > else. > > However it also writes to the NLog the results for status debug and it > writes the flag saying that > it succeeded, which is immediately written back to SQL Server. There are > threads in other processes > polling SQL Server every N seconds asking whether there are any processes > where flag XYZ has been > set, IOW it is ready to move to the next stage of processing. > > Could I break this down into umpteen other functions that (in the end) every > one only does one > thing? Of course, but I ain't gonna! > > I like that the function logs its state in NLog and that the function logs > its state in the property > and I like that the property immediately writes the information back to SQL > Server. I went to a > great deal of effort to get all of this stuff working this way. I want a > system where every step of > the process immediately logs its completion and if I stop the big picture > for any reason (power loss > or simply shutting down the server) I can pick right up where I left off. > > Each such flag is written to (initialized) from the code that loads the > class instance from SQL > Server and then modified in the function that actually performs that step. > These process step > functions are only used in one place, precisely and only in the class that > performs that step. They > will never be called from anywhere else (in fact they are private to the > class) because no other > code anywhere in the world performs that step of address validation > processing. > > As for testing... an interesting read. > > http://en.wikipedia.org/wiki/Unit_testing > > particularly "Unit testing limitations". I am not here to get in a peeing > match about whether or > not... But where is the unit test of the unit test code... This article > claims that the unit test > requires 2-3 lines of code for every line tested, and we all know that there > is (statistically) 1 > bug in every 20 lines of code... > > Since unit testing code is code, and since it introduces 2-3 lines of test > code for every line > tested and since there are going to be bugs in the unit test code, then we > need unit test code for > the unit test code for the unit test code for the unit test code for... > > Kind of like looking in a mirror at a reflection in a mirror behind you. > > Sounds like the stuff sci-fi novels are made of. ;) > > At any rate, as a sole proprietor I have to pick a tool which can implement > the systems that I > design. I am not sitting at a desk collecting a paycheck regardless of what > I produce. I do not > have a test department, I am the test department. I am actually fascinated > with the unit testing > concept but I barely have the time to write the code itself, never mind code > to test the code which > tests the code... > > Whats a guy to do? > > John W. Colby > www.ColbyConsulting.com > > On 4/11/2011 4:09 PM, Kenneth Ismert wrote: >> John, >> >> I understand the reasoning and all however... because the code is no > longer >>> contained within the object that needs it, you now open yourself up to > the >>> old "I need to modify this method... oops... that change breaks something >>> else that uses the code". > > ...