views:

141

answers:

5

Looking for some practical advice here and any experiences people have had in a similar situation.

We use a BDD/TDD sytle methodology for building our software (quite a large/complex application) The end result is.. behavioral specifications (Given/When/Then style) derived from business requirements, unit tests that reflect these and code that reflects the requirements of tests.

However, recently our test department has started running integration tests, and understandably they want to use our (already passing) business logic code to set up and tear down test state (rather than having to deal directly with a database) as they are mainly concerned with testing via the UI of the application and do not want to spend all day wrangling databases.

The problem is, some of the Entity Repositories do not have delete methods as there has been no business requirement expressed for these yet. Many have Archive/Reinstate/backup etc. (and may have delete pending on the backlog).

So now we have a test dept. requirement for deletion (but one which conflicts with business user stories)

So.... My question is... if I were to add methods specifically for the test department... what't the best way of handling these. I understand that this is generally considered bad practice in "TDD Utopia" but realistically, how have you dealt with this kind of conflict?

The first thoughts I have had are either use naming...

void TestOnly_Delete(Guid id){} 

...attributes...

[TestOnly]
void Delete(Guid id){}

... or compiler directives...

#if TESTBUILD
void Delete(Guid id){}
#endif

So at a minimum, developers can know not to call TestOnly methods and at a maximum, test methods are not deployed in production builds.

... or just cheat and add a user story to manage it that way ;-)

Any experience or advice gratefully appreciated?

Thanks in advance.

+1  A: 

In my code, I create a subclass: for example, if I have a DataLayer class which has all the public methods to access the data layer, then I might subclass it with a Test_DataLayer subclass, which inherits methods from DataLayer and which furthermore implements any additional methods that you might want like Delete, and whose implementation can access internal or protected methods of the base class.

ChrisW
+1  A: 

I usually end up with a "test-only" project/library holding whatever mock/helper classes that I need for my tests. Adding the necessary classes/methods in this mock library -- which isn't included by production code -- seems like a natural fit for this requirement. If you don't already have such a library, and don't want to create one, then I would mark -- if possible -- the methods internal and only expose them to the testing framework. You don't specify platform, but I know that this is possible with C#/.NET and I often use this ability to give my tests access to methods that would otherwise be unavailable outside the library.

On another note, I would say that the testers are as much a part of your requirements analysis as the actual customers. Just like we have some requirements that are purely for our development needs, we have some requirements for testing as well. The trick, as you're discovering, is to develop the code in a way that all of the stakeholders needs are satisfied as much as possible. When the needs conflict, we try to use the best compromise possible. IMO, making it possible to delete from test-only libraries is a reasonable compromise with the customer's need for data security (no delete).

tvanfosson
+2  A: 

Your first concern should be does adding this functionality enchance or deteriorate the current API? A typical scenario in TDD is that a class member is (initially) only required for testability reason, yet it conforms to all normal API design guidelines, so it does no harm (and often subsequently turn out to be a valuable addition in production code as well).

When this is true, then by all means just add it if you can do so easily.

Sometimes, however, the reverse is the case. In this case, you must resist the urge to add the member. However, often you can follow the Open/Closed Principle and open up your API so that others would be able to add the desired funtionality. Testability is really just another word for the Open/Closed Principle.

In your case, the simplest solution might simply be to ensure that the classes in question are unsealed and then ask the Test Department to derive from those classes and implement the functionality themselves. If they don't have this capability, then you can do it for them, while still keeping the sub-classes test-only.

Mark Seemann
A: 

This is an interesting question. I have some thought but not sure if it will be an answer to your problem.

I will personally create an explict set of interfaces inheriting from what you already have for the entity such as IIntTest_Customer: ICustomer and define the delete method there. If possible, trying to put all these interface in separate project so that way the developer dont even have reference to it from the usual project and avoid the accidental use of them. The test department will then use this specific internal test interfaces for their testing purpose.

To achieve this you perhaps have to refactor your current code and change the entity to implement these internal test interfaces instead and with the inheritance hierarchy all your existing code should still work that refer to the base interface.

Fadrian Sudaman
A: 

I will never add delete or other cleanup methods to my code only to support automated testing.

There are so many alternatives, ranging from intelligent transaction management to automatic restore of databases.

Gerrie Schenck