Archive for the ‘Unit Testing’ Category

Test Driven Development – Patterns & Practices

I have posted the different sections of the term paper Test Driven Development – Patterns & Practices that I have submitted for Software Engineering course of MS-CS. The published section of the paper are as under.

Introduction: Test Driven Development & Units Testing
TDD/Unit Testing Practices
TDD Patterns
TDD Anti-Patterns
References.

Feel free to share your feelings/comments. Try to use Firefox 3.0+ while reading the post. IE7.0 sucks .


Test Driven Development – Patterns & Practices: References

[Beck02] Addison Wesley. Test-Driven Development by Example: Kent Beck 2002.

[Astels03] Prentice Hall. Test Driven Development: A Practical Guide by David Astels 2003.

[Lesse07] Manning. Test Driven TDD and Acceptance TDD for Java Developers by Lasse Koskela. 2007.

[Mar06] The impact of Test-Driven Development on Design Quality: Maria Sinaalto.

[Beck04] Addison Wesley. Extreme Programming Explained, Second Edition: Embrace Change.

[Gera07] Addison Wesley xUnit Test Patterns – Refactoring Test Code: Gerard Meszaros. May 2007

[Beedle05] Kent Beck, M. Beedle, et al., Manifesto for Agile Software Development, 28.12.2005,

http://www.agilemanifesto.org.

[Janzen] Software Architecture Improvement through Test-Driven Development: David S. Janzen.

[Martin06] Prentice Hall – Agile Principles, Patterns, and Practices in C# By Martin C. Robert, Martin Micah 2006.

[Prag03] Pragmatic Programmer – Pragmatic Unit Testing 2003.

[TDD1] Introduction to Test Driven Design (TDD) http://www.agiledata.org/essays/tdd.html

[TDD2] Stepping Through the Looking Glass: Test-Driven Game Development www.gamesfromwithin.com/articles/0502/000073.html

[TDD3] The Three Rules of TDD. http://butunclebob.com

[TDD4] Test-driven development. http://en.wikipedia.org/wiki/Test-driven_development

[Pancur] Pancur, M., Ciglaric, M., M., T., and Vidmar, T. (2003). Towards empirical evaluation of testdriven development in a university environment. In EUROCON 2003, Computer as a Tool, The IEEE Region 8, volume 2, pages 83–86.

[Deursen] van Deursen, A. (2001). Program comprehension risks and opportunities in extreme programming. In Proceedings of the Eight Working Conference on Reverse Engineering (WCRE’01), pages 176–185.

[Constantine] Constantine, L. (2001). Methodological agility. Software Development, pages 67–69.

[George04] George, B. and Williams, L. (2004). A structured experiment of testdriven development. Information and Software Technology, 46(5):337–342.

[refa01] The Process of Database Refactoring.

http://www.agiledata.org/essays/databaseRefactoring.html

[agil01] Agile Database Best Practices

http://www.agiledata.org/essays/agileDataModeling.html

[FOW01] Continuous Integration – Martin Fowler http://martinfowler.com/articles/continuousIntegration.html

[Shun01] Test Driven Development – Self Shunt Pattern

http://www.objectmentor.com/resources/articles/SelfShunPtrn.pdf

[Fow07] Mocks Aren’t Stubs – Martin Fowler

http://martinfowler.com/articles/mocksArentStubs.html

[Wor01] JUnit best practices

http://www.javaworld.com/jw-12-2000/jw-1221-junit.html

[Wor02] Best practices for test-driven development

http://www.javaworld.com/javaworld/jw-05-2004/jw-0510-tdd.html

[Vin04] Manning – JUnit in Action, Vincent Massol, Ted Husted 2004

[Ant01] JUnit Antipatterns

http://www-128.ibm.com/developerworks/opensource/library/os-junit/

[Ant02] TDD Anti-Patterns by James Carr

http://blog.james-carr.org/2006/11/03/tdd-anti-patterns/

[surv01] Agile Adoption survey

http://www.ambysoft.com/downloads/surveys/AgileAdoption2007.ppt

[surv02] TDD Survey

http://www.stelligent.com/content/view/159/61/

[TDD3] Yahoo TDD group

http://tech.groups.yahoo.com/group/testdrivendevelopment/

Test Driven Development – Patterns & Practices: Anti-Patterns

The problem while finding the TDD Anti-Patterns was that there is not a single definitive source of information for anti-patterns; instead you have to rely on group/forums/blogs to get the information from [Ant02, TDD3]. Following is a list of common anti-patterns.

· The Giant: A unit test that is testing too many targets and contains too many test cases in there. Just like any other class, a unit test should have only one responsibility and test cases inside it should help it achieving that bit of functionality. This kind of a test cast show very low cohesion as many of its test methods are unrelated.

· The Mockery: Mocking should be used when a unit test require a resource that is too expansive (in term of time and space); it’s a need base object. If a developer try to mock out thing that is not being tested then unit test is actually testing the mocks not objects.

· The Inspector: Agile way of development promotes just enough design philosophy, but it does not mean that design principles (encapsulation, low coupling, and high cohesion) are not applicable. Code that does not have these attributes will be very difficult to refactor later on; infect it will break some code in an attempt to do so. A test case that breaks the encapsulation principle in an attempt to get the required visibility of the target is just doing that. Very difficult to maintain and refactor that kind of code.

· The Local Hero: A test case/unit test that has a dependency on something specific to a particular environment (may be in IDE or platform) will show a green bar only when it is tested under those condition and will show a red else where.

· The Nitpicker: A unit test case should focus on the part of the target that is required; if it is testing some part of it that is not important for the overall goals of the systems. This kind of a test case will required that target should be kept in state that is unimportant for the system.

· The Secret Catcher: A test case that appears to be doing nothing as it is not asserting something e.g. if a test case is relying on an exception to be thrown. This kind of a test case should make it explicit by naming the exception variable expected and add a true assertion to make its intention absolutely clear to the reviewer of code.

· The Loudmouth: A unit test the write too many messages/logging to the console/log files. If the test is passing, a test case should keep its mouth shut. See Tangled Vision Pattern.

· Excessive Setup: A unit test that requires a lot of time/code to setup and does not make it’s intention entirely clear to every one or require a lot of time to do so. This kind of a test case normally tests too many things, try refactoring to make it manageable.

· Hidden Dependency: A test relies on some external data to be available before it gets executed; test case will pass when data is available and fail when it will not be there.

· The Greedy Catcher: A test case that relies on silent exceptions to pass its test cases or log down lesser informative messages to console/log file.

· The Liar: A unit test that pretends to test to target but upon inspection it’s found that it was not able to do so. This kind of a test uses all your system resources & time but does not do any provide any useful functionality.

· The Enumerator: The unit test that does make its intentions entirely clear as the names of the test cases is not following the Legible Naming best practice. The name of the test case uses a sequence as test1, test2 etc. The only way to find the intention is to look at the test case code.

· The Slow Poke: A test case that run very slow, it may require an expansive resource. Try creating mock to simulate the resource behavior.

· The Dodo: A functionality which was probably outdated and not required by the application now. As the functionality was built using the test first, it should have a test case for it which should be deleted.

· The Stranger/The Distant Relative: A unit test is designed to test a particular target object, if a test case insides it is not testing the intended target object and test a another object (most likely an object used by the target object). This kind of a test case create unit test with low cohesion.

· The Operating System Evangelist: A test case that has a dependency on operating system specific features. This will affect the portability of the test case severely.

· Success against All Odds: A unit test should be designed in a way that if one of the test case/part of the test is failed the entire unit test is failed; infect when ever a assertion failed in a test case, unit test should halt the processing of the test case with a red bar. The opposite of this rule is anti-pattern.

· The Free Ride: A test for a functionality/feature was written inside an existing test case; instead of writing a new test case for the functionality. Make the test case less cohesive.

· Silver Bullet: A test case that tests entire set of functionality provide by an object or group of objects. It has only one test case method which tests. A common indicator is that the test method is often the same as the unit test name, and contains multiple lines of setup and assertions.

· Love Birds: A test case which is so closely coupled with a particular class under test that it distracts from the need to test important interaction between classes. Commonly found in a test package structure created for Homing Pigeons where there is no obvious place to put tests for class interaction

· The Web Surfer: A unit test that requires a connection to internet with access to the outside world in order to run.

Test Driven Development – Patterns & Practices: Practices

Following are TDD and Unit testing best practices.

· Legible Naming: A method name should suggest it purpose, test cases are just like methods we have in our application code. So, same rules apply to them as well. A good practice while naming a test case is start its name with ‘test’ should be the name of the method under test ‘testNameOftheTestee’. If we have more that one test cases for the same method, instead of numbering them add the purpose of the test case at the end as ‘testNameOftheTesteePurpose’.

· Exceptional Legibility: A test case that is written to check an exception should make this exception clear by naming the exception variable ‘expected’. Name of the variable should give indication of its purpose and that what it is doing here.

· Failed due to: Each failure should have a description attached to it. So, just by looking at the console, a programmer can find out what’s wrong with the code and quickly go there are fix. A description is normally attached to the assertion in xUnit. This practice resulted in fast red-green-refactor cycle.

· Same Namespace yet Disparate: How to structure you test classes along with the application classes? This practice gives us the answer to this question. A general practice is to place test in the same namespace where you have your application code being tested. You can create a child namespace for the tests but parent namespace should remain the same. That will normally be required for protected and package access in some programming languages.

· Single Point of Focus: A fine grained test is always desired, the more fine grained our test are the greater the level of coverage we can achieve. In Object Oriented realm a fine grained test case will focus on single object at a time. As we are focusing on a single object at a time, the things are in our control and not much can go wrong. A test case that tries to cover too many things will eventually become a maintenance hazard.

· Continuous Melioration: A unit test behave as a user of your code, it give you instant feed back in term of failure and successes. As the application is solely based on the test cases we have and test cases are based on user requirement; so, a test case is just like a user telling us what to do next. As maxim goes “user is always right”. This approach leads to robust and correct solutions.

Another point worth mentioning here is that unit test help us improve the code, normally while unit testing we try to all possible paths and the only way to know about the paths is know the number of branches it has. The more branches that you have the more difficult it will be to write unit test code for that particular code. It is good indication of a bad design, apply refactoring to minimize the number of branches that your code take and then try again.

· Refactor, as they say: This practice has several facet attached to it, let me discuss those one by one.

- Improve the Design: Agile and XP methodologies promoted the term of just enough design that will work. We design as we move along in an iterative manner; if a conflict comes in the picture, we refactor the code to resolve the conflict. Now refactoring is just a way to improve the design without making any functionality related changes. How can we make sure that we just did that and everything else stays intact? Just run the test cases.

- Improve the Test Code: If you are working with mock objects and mock object take has huge setup method then it’s an indication of wrong number of Mock Objects. Try merging the expanding the mock object using refactoring.

- Try to refactor the long setup and teardown methods and use extract method to extract the common code and creating utility classes to share the same code between test classes.

· Layer Supertype: Create a super class of all your test classes and place the common test code in that super class, this will help in increasing the reusability of the code and well as you have a place to put something that will be applicable to all the test classes e.g. You can write interceptor for you test classes.

· Regress as you go: One of benefits of approach like TDD is test automation. A change is made to the systems, may be a new feature is added or an existing feature is modified. Just run the test cases to confirm that everything stays where it was before the changes. It is kind of on the go regression mechanism for your application code. A new change can be made with confidence as you know if something goes wrong I’ll have red bar indication.

· One = One: A single unit test should correspond to a single test method in the test class, if you have two unit tests; create a separate test for each of those. It is generally a bad practice if you try to test more that one unit test in the same test method. In other words we can say that a single test method has a single responsibility of testing a whole/part of a unit test. And a single test class has a single responsibility of testing a single fixture. Same Object orientation principles are application here, just the context is different.

· Exceptional absentee: While working with mocks, you sometimes need a few methods for the test cases. A mock object might be mimicking an object with a lot methods e.g. Database Driver. Good IDE will create a mock object based on the interface of the actual object in seconds but you need only a few those. So, make your intension absolutely clear by throwing an exception from methods that are not useful for you purpose instead of returning null from those. Leave methods with no return type as it is.

· Restricted Mockery: Mock object are just dumb object, they are mimicking something. Mock objects are not the place to put your business test code into; instead if you have to test business method, try creating a stub for that purpose [Fow07].

· Keep an eye on: A general practice in unit testing is that that if a test should fail for a specific case then it should fail for it. In TDD we write test before the application code, so this happens automatically as we go along. A technique to check the validity of a test case is to test it against a skeletal class; if it is not failing for that class then something is wrong wit the test case. Try reviewing it.

· Just enough Design: The simplest thing that could possibly work is the principle of XP. This is opposite to the upfront design methodology. This principle works hand in hand with the test first style of development. In TDD we only require the bare minimum application code that will make a failing test pass and improve as we move along the way.

· Immutability is the key: While working with an approach like TDD, where test code drives your application classes; always strive for application (or ever test classes) that are immutable. Immutability will not only simplify the design of the application but will also have no side effects at all. Immutable class designs are generally more maintainable [Wor01].

· Pairing is sharing: Coming from a code first back into the realms of TDD, there is always a temptation in mind to switch back to code first approach (may be due to fatigue). There comes in the partner to keep you focused. In pair programming, one person is on the driving seat (at keyboard) and the other person look at the code can come up with a suggestion; this will help in improving the design of the application in continuous manner. Pair Programming is indispensable when we talk about TDD [Wor02].

· Explicit Ordering: Never rely on the sequence in which the xUnit family of tools may call your code. These tools rely on reflection (introspection) techniques to find the methods out of you test cases, the behavior of these mechanism may vary from platform to platform (or ever IDE to IDE). If you write you your test cases with this assumption then you might have portability issues with your code. If you want explicit sequence, add test cased in the order in which you like to the test suite; xUnit will call it in that particular sequence [Wor02].

· Say No to Side effects: Avoid writing test cases with side effects. A test case will have side effects if it interferes with execution of other test cases in the system. Side effect can cause the following problems.

- It will affect the data used by another test case

- We cannot repeat the test case without manual intervention.

Automation of the test case will be severely effected by the manual intervention and data corrupting problems are very hard to diagnose. Avoid test cases with side effects [Wor01].

· Tangled Vision: Avoid test cases that require a Visual inspection for a human being to verify the test case generated data; instead a test case should be complete in its working and should have the mechanism to inspect the data. Visual inspection requires patience and an ability to inspect a large quantity of data; they qualities are not common in normal human beings

Follow

Get every new post delivered to your Inbox.