xUnit and TDD: Why and How in Enterprise Software, August 2012

download xUnit and TDD: Why and How in Enterprise Software, August 2012

If you can't read please download the document

Transcript of xUnit and TDD: Why and How in Enterprise Software, August 2012

Slide 1

Justin GordonEnterprise TDD EvangelistSenior Software EngineerIBM, Master Data Management Collaboration [email protected]

[email protected]

http://www.tddtips.com

https://github.com/justin808/dof

JUnit and Test Driven Development:
Experiences in MDM CS 2003 to 2012

Author background

BA Harvard Applied Mathematics, MBA UC Berkeley

Writing Java Enterprise Software since 1996

Engineer at Trigo, acquired by IBM

Product now called InfoSphere Master Data Management Collaboration Server. Led rewrite of storage layer doing using TDD

Founder and author of Open Source Project Dependent Object Framework

Outside of programming, interests include my kids, surfing, standup paddling, my dogs, cycling, and home improvement. Check out the house I designed and built: http://www.sugarranchmaui.com.

Speaker SD West 2008, Architecture and Design World 2008, and SD Best Practices 2008. See my blog for notes! (http://www.tddtips.com)

Please tell me about your challenges and maybe I can help

Success Factors

Nature of the projectExisting, legacy, and not designed for testing?

New codeHow many dependencies and how containable?

Motivation of the teamTDD is hard work!

LearningThere's a ton to learn about writing good tests. Lots of patterns. Lots of pitfalls.

Recommended: xUnit Test Patterns, but it's huge, and Kent Becks original book Test Driven Development by Example

Measurement is keyContinuous Integration including Code Coverage Reports

BenefitsQuality!

Sanity for the overall team, including developers, QA, and managers

Test First CodeReally affects the overall structure of the code, as code is written to be testable.

Most important overall is to have automation tests!

Summary of MDM CS JUnit

Check out my blog articles, especially this one:

JUnit and Test Driven Development for MDM CS -- Fixtures and FactoriesSerialization development was perfect for jUnit

High algorithmic complexity, limited outside dependencies

Rest of the product for jUnitChallenging due to very complex database interactions

Led to the open source project Dependent Object Framework which works on the problem of test fixture setup

Big problem was that developers confused the usage of the framework in terms of how to use the true fixtures versus setting up scratch objects.

Good article on the subject of fixtures and factories for ruby testing: Fixture vs. Factories - Can't we all just get along.

Presentation given at SD West, 2009

JUnit and Test Driven Development:
Why and How in Enterprise Software

Roadmap

Conventional versus Agile

JUnit Tests

Test Driven Development

Tools

TDD Architecture

Database Techniques: DOF

Getting Started

Conventional versus Agile

CONVENTIONALArchitects High Level Design (HLD) Detailed Technical Design (DTD) Coding QA & Bug Fixing Regressions More QA & Bug Fixing Major Release Bug Fixing Regressions Bug Fixing Minor Release

Painful mess! Unhappy developers, unhappy customers, unhappy managers!

AGILEStories and Requirements Simple Specs Write JUnit (automated) tests in Conjunction with Source Ensure code coverage Refactoring to make code better QA Limited bug fixing with JUnit tests for each bug fixed Almost no regressions! Performance tuning with confidence Release Very few bugs Happy developers, happy customers, happy managers!

Premise

A comprehensive suite of JUnit tests is one of the most import aspects of a software project because it reduces bugs, facilitates adding new developers, and enables refactoring and performance tuning with confidence. Test-driven development (TDD) is the best way to build a suite of tests. And the Dependent Object Framework is the best way to test against database objects.Justin Gordon

Justin

Roadmap

A tale of two development groups

JUnit Tests

Test Driven Development

Tools

TDD Architecture

Database Techniques: DOF

Getting Started

JUnit Tests: What?

JUnit test: a method, written in Java, that verifies the behavior of an individual unit of code, or occasionally of a larger subsystem, and reports errors in an automated fashion.

public void testAddReturnsSum() {int sum = Calculator.add(2, 3);assertEquals(5, sum);}

JUnit Tests: Why?

If JUnit tests pass and code coverage is high Nearly Bug-Free Code!

When JUnit tests cover requirements and tests pass Code is Complete!

JUnit tests facilitate automatic test running to detect regressions instantly during bug fix cycles. Cant do that with manual QA! Cant do that with QA Automation tools!

Enables Courage and Creativity

Developers (experienced and new) can change the code with confidence, enabling

Refactoring

Performance Tuning

JUnit tests serve to document and demonstrate the API

Large team sizes, offshoring, complexity

Wikipedia: http://en.wikipedia.org/wiki/RefactoringIn software engineering, the term refactoring means modifying source code without changing its external behavior, and is sometimes informally referred to as "cleaning it up". In extreme programming and other agile methodologies refactoring is an integral part of the software development cycle: developers alternate between adding new tests and functionality and refactoring the code to improve its internal consistency and clarity. Automated unit testing ensures that refactoring does not make the code stop working.Refactoring does not fix bugs or add new functionality. Rather it is designed to improve the understandability of the code or change its structure and design, and remove dead code, to make it easier for human maintenance in the future. In particular, adding new behavior to a program might be difficult with the program's given structure, so a developer might refactor it first to make it easy, and then add the new behavior.An example of a trivial refactoring is to change a variable name into something more meaningful, such as from a single letter 'i' to 'interestRate'. A more complex refactoring is to turn the code within an if block into a subroutine. An even more complex refactoring is to replace an if conditional with polymorphism. While "cleaning up" code has happened for decades, the key insight in refactoring is to intentionally "clean up" code separately from adding new functionality, using a known catalogue of common useful refactoring methods, and then separately testing the code (knowing that any behavioral changes indicate a bug). The new aspect is explicitly wanting to improve an existing design without altering its intent or behavior.

Catch 22: Why not write JUnit tests?

Normal development cycle inhibits JUnit test creation

Catch-22: existing quality is low, so developers are too busy fixing problems found in the field to write tests.

(Bad) Attitude: Its QAs job to find my bugs. I dont have time to write tests.

Skills: tough to learn how to write JUnit tests for new code. Many new patterns!

Even tougher for old code!

Unless existing code is designed for testability, implementing JUnit tests is very difficult.

Really Tough!

Normal development cycle inhibits JUnit test creationDevelopers rewarded for getting something working ASAPCode like mad to meet a deadline (DCut?); testing is an afterthoughtThrow it over the fence to QA and fix bugs as theyre foundMotivation to write tests for already-working code is low!

Inertia: hard to change ingrained working habits

Roadmap

A tale of two development groups

JUnit Tests

Test Driven Development

Tools

TDD Architecture

Database Techniques: DOF

Getting Started

What is Test Driven Development (TDD)?

Programming practice in which all production code is written in response to a failing test.

Read Kent Becks: Test Driven Development By Example

List Requirements

Write One Test

Run Test toMake Sure It Fails

Add or modify just enough code to make new test pass and all previous tests pass

Refactor to eliminate code smells (e.g., duplicated code)

Ask about purity of TDD in practice.

What Is Not Test Driven Development?

Any time you write code that is not fixing a failing test.

I.e., Writing code, then writing tests or intending to eventually write tests.

Relying on QA to automate their manual tests.

Be honest when trying this.

Conventional Big Up Front Design is not TDD!

Any time you write code that is not fixing a failing test.

I.e., Writing code, then writing tests or intending to eventually write tests.No matter how good they are!

Relying on QA to automate their manual tests.Especially using SilkTest or similar.

Be honest when trying this.If it isnt TDD, you wont get the benefits

What about the conventional approach of have the architects design the APIs, the developers code to the specs, and QA tests? Is that TDD?

Why TDD Code Coverage & Better Code

Guarantees existence of JUnit tests covering most, if not all of your code!

Guarantees code will be written to be testable.

Reverse is also true: if you write your code first, and then your tests, you may have difficulty writing tests for the new code, and then you may not write the tests at all! More natural to write untestable code unless tests written at the same time.

Solves the motivation problem. Test writing becomes part of the coding process, not a tedious afterthought.

Produces better code: more decoupled, with clearer, tighter contracts.

Tests are the canary in the coal mine! Bad designs show up as hard to test!

Shooting hoops? Practice Makes Perfect

Developers improve skills because of the immediate feedback from the tests, rather than months later from QA!

Academic study confirms higher quality code: An Initial Investigation of Test Driven Development in Industry by Boby George and Laurie Williams, 2003, http://collaboration.csc.ncsu.edu/laurie/Papers/TDDpaperv8.pdf

TDD developers took more time (16%), but non-TDD developers did not write adequate automated test cases even though instructed to do so!

Write Test

Write Code

Does Test First Matter?

Goal is Automated Unit Test Coverage

Your choice how you get there

Sometimes, you already have lots of code! So too late for pure TDD!

So

Consider a broader definition of TDD

Not just Test First Programming

But delivering code with unit tests

You want automated test coverage as code is delivered!

Roadmap

A tale of two development groups

JUnit Tests

Test Driven Development

Tools

TDD Architecture

Database Techniques: DOF

Getting Started

Making it Happen: Tools for Success

IDEs: Eclipse, RSA, RAD, and IntelliJ offer these essentials:

Ease of use to run a single new test

Refactoring tools.

Code Coverage

Now built into Eclipse (EclEmma) and IntelliJ

Clover or Emma are the most popular.

How do you know how good your tests are?

Measure progress for morale (and management reports).

Continuous Automation

Jenkins, Cruise Control, or BuildForge

Automated system for building code, running JUnit tests and reporting on code coverage.

Alerts team of issues (build or JUnit) within minutes of checkins

Mock Objects: Mockito

Dependent Object Framework

Ease of writing JUnit tests in the context of persistent objects helps setup database fixtures

You cannot do TDD without the right tools

Emacs or VIM??? These do not support single test running and refactoring,

Code Coverage Inside Eclipse

Code Coverage in Eclipse provided by EclEmma: http://www.eclemma.org/index.html

Green lines indicate coverage by unit tests.

Pink lines indicate code not covered by unit tests

Exercise 1: Simple TDD: Compute Change Coins

Problem: Compute Change Coins

Coins include: pennies, nickels, dimes, quarters

Machine may be out of any coin except pennies

Give least number of coins

Example:All coins available:

37 cents: 1 quarter, 1 dime, 2 pennies

80 cents: 3 quarters, 1 nickel

No nickels:

80 cents: 2 quarters, 3 dimes

No dimes:

85 cents: 3 quarters, 2 nickels

public class Change{ public int pennies, nickels, dimes, quarters;public Change(int pennies, int nickels, int dimes, int quarters) {this.pennies = pennies; this.nickels = nickels;this.dimes = dimes;this.quarters = quarters; }

public class VendingMachine{// the number of coins leftpublic int pennies, nickels, dimes, quarters;public Change getChange(int cents){ }}

public class VendingMachineTest extends TestCase {public void testGetChangeReturnsNoChangeIfNoCents){ }}

Exercise 1 Tips

Only add code after adding a test that fails!

Do bare minimum to make tests pass

Patterns: Naming Test Methods

test{MethodName}Returns{Value}When{Condition}

testGetChangeReturnsZeroWhenNoCents

testGetChangeReturnsThreeQuartersOneNickelWhenEightyCents

test{MethodName} {DoesSomething}When{Condition}

testGetChangeThrowsWhenCentsLessThanZero

Why such long method names?

Method names print out in test failures

Programmers never call these methods

Directory Structure

src: Where production code goes

junit:Where junit tests go, use parallel package structure to test package and protected methods.

Implement equals() so that we can compare Change objects.

Implement toString() so that error messages are clear

Exercise 1: Steps for Initial example

Run VendingMachineTestSuite to see all tests run

Run the test suite with code coverage turned on to see code coverage

Uncomment Change.toString() to see effect on the error message implement toString() for better messages

Uncomment Change.equals() to see VendingMachineTest tests pass.

Put @Ignore in front of failing test and run all tests to see that no failures and 2 ignored tests

Exercise 1b: Steps for Intermediate

Check out the triangulation method of verification.

Fix the test testGetChangeReturnsThreePenniesIfTwoCents()

Check out the use of the @Before setup of the variable VendingMachine

Complete the rest of the commented tests

Handle the tougher cases where the vending machine can run out of a certain kind of coin

Question: Can we exhaustively find all the solutions and check the code against these solutions? Possibly could write tests to use triangulation.

Roadmap

A tale of two development groups

JUnit Tests

Test Driven Development

Tools

TDD Architecture

Database Techniques: DOF

Getting Started

Architectural benefits of TDD

With TDD

Better abstractions and better decoupling of classes.

Loose coupling, otherwise impossible to test individual components.

When using Test Doubles (e.g., test stubs and mock objects), you want to mock out smaller APIs or else youll work too hard! Keep classes small and methods tight.

Loose Coupling: TDD!

A

B

D

C

e

cc

bb

dd

B

A

D

E

C

Tight Coupling!

interface

class

Without TDD, you often see tight coupling between classes and throughout to all dependencies, making the implementation of new tests prohibitively painful.

Test Doubles

Test Doubles: Fakes, Stubs, Mocks see xUnit Test Patterns by Meszaros

Reasons to use Test Doubles

Isolates code for testing!

Awkward to setup for JUnit test e.g. web services

Not available (or not yet written) new component in large project

Too slow (less so now with in-memory DBs like H2)

Reasons to not use Test Doubles

Extra code to maintain that is only used for testing

Difficult to refactor code to use test doubles

You still need to test the delivery code!

Dont mock out the database!

Making it Happen: Isolating Code for Testing

Note Mock objects are a form of test double and the name is used interchangeably here

Successful TDD depends on dependency isolation you need to separate the code to be tested from the rest of the system.

This is THE main technical challenge, esp. for database and integration points

Must decouple classes with interface/implementation/mock object pattern

Use a combination of dependency location and/or dependency injection for dependencies (following slides)

Typically mocking out a dependency on a clearly defined component, such as an object that would call a web service.

Difficult to do TDD without using test doubles (or using the Dependent Object Framework)

Tip: minimize business logic in mock objects because you have to duplicate that logic in the real implementation. Refactor code to minimize business logic in mock classes.

Class To Test

Dependency(Runtime Object)

Dependency(Test Double, e.g. Mock Object)

Interface

Dependency Isolation: Static Location

Example: Class fetches data from the database or from a mock source. But how does your code get a handle to the correct Component?

Dependency location: your class looks up the dependency (test double or real instance) from a known location, typically a static method call

Test setup code:

// note, setTaxRateProvider takes interface TaxRateProviderGlobalContext.setTaxRateProvider(new MockTaxRateProvider());

Production setup code:

GlobalContext.setTaxRateProvider(new SoapTaxRateProvider());

Production Code sees interface does NOT know if real or test double!!

class OrderProcessor public float getTaxRateForState(String state) { TaxRateProvider taxRateProvider = GlobalContext.getTaxRateProvider(); return taxRateProvider.getSalesTax(state); }

See: Inversion of Control Containers and the Dependency Injection pattern: http://www.martinfowler.com/articles/injection.html

Static method returns interface!

Note this slide should be skipped in a short presentation. In a longer presentation, this material will be reviewed several times.

Dependency Isolation: Constructor/Setter Injection

Dependency Constructor/Setter Injection: Pass a reference to the dependency in the constructor (or a setter):

Test setup code:

// note interface TaxRateProviderTaxRateProvider mockTaxRateProvider = new MockTaxRateProvider();OrderProcessor orderProcessor = new OrderProcessor(mockTaxRateProvider)

Production code:

TaxRateProvider soapTaxRateProvider = new SoapTaxRateProvider());OrderProcessor orderProcessor = new OrderProcessor(soapTaxRateProvider)

InvoiceComponent class does NOT know if it has a real or mock persistence object

class OrderProcessor { TaxRateProvider taxRateProvider;// constructor injection OrderProcessor (TaxRateProvider taxRateProvider ) { this.taxRateProvider = taxRateProvider;}

public float getTaxRateForState(String state) { return taxRateProvider.getSalesTax(state); }

Member interface variable!

Note this slide should be skipped in a short presentation.

Dependency Injection versus Service Locator

Inversion of control is a common feature of frameworks, but it's something that comes at a price. It tends to be hard to understand and leads to problems when you are trying to debug. So on the whole I prefer to avoid it unless I need it. This isn't to say it's a bad thing, just that I think it needs to justify itself over the more straightforward alternative

The key difference is that with a Service Locator every user of a service has a dependency to the locator. The locator can hide dependencies to other implementations, but you do need to see the locator. So the decision between locator and injector depends on whether that dependency is a problem.

A lot of this depends on the nature of the user of the service. If you are building an application with various classes that use a service, then a dependency from the application classes to the locator isn't a big deal.The difference comes if the lister is a component that I'm providing to an application that other people are writing. In this case I don't know much about the APIs of the service locators that my customers are going to use. Each customer might have their own incompatible service locators. I can get around some of this by using the segregated interface. Each customer can write an adapter that matches my interface to their locator, but in any case I still need to see the first locator to lookup my specific interface. And once the adapter appears then the simplicity of the direct connection to a locator is beginning to slip.

Martin Fowler http://martinfowler.com/articles/injection.html#ServiceLocatorVsDependencyInjection

Mockito

Highly recommended! First, you have to isolate dependencies. Then mock: http://code.google.com/p/mockito/

Roadmap

A tale of two development groups

JUnit Tests

Test Driven Development

Tools

TDD Architecture

Database Techniques: DOF

Getting Started

Basic Phases of an xUnit Test

SetupPrepare Test Fixtures -- Get Application State Ready To Do Something

TeardownRestore application state for next test

Exercise Do Something With Fixtures Prefer Single Action

VerifyCheck Something Happened: assertSomething()

xUnit Test

DB Objects and the DOF

The setup and teardown boxes are grey to indicate that the reader of the test should be able to focus on the logic of what is done and what is verified (orange boxes)

Database Issues

Database is like a global variable that is slow to access!

Actually worse than a global variable because it lives between test runs!

Stickiness of data collides with xUnit philosophy of atomic/independent tests!

DB Fixture Setup without the DOF

Java code

SQL scripts

DB Backups

Issues: Difficult, Monolithic, fragile, frustrating, slow

Greater possibility of slow and erratically failing tests

Test fixtures often include objects that are persisted in the DB. I.e., in order to run a test, some values must exist in the DB, and a test may create some new records in the DB.

Why have xUnit tests hit the database?

Production code for end users will hit the DB, so you need to test it!

Catch 22: Refactor in order to isolate database dependencies to build tests, but without tests, how safely can we do the refactoring?!

Mocks and stubs add (much) more code to maintain (and to fix bugs!).

Reliance on mocks and stubs can mask errors with using the DB.

Annecdote: GuideWire Software used to depend heavily on stubs and dependency isolation techniques, but now focuses exclusively on tests against the database, using H2 in-memory DB to speed up tests.

Production code for end users will hit the DB, so you need to test it!

Conventional Wisdom: Real xUnit tests dont use a DB! But, developers are working on existing codebases that have countless intricate ties to the DB. Common advice is to refactor in order to isolate database dependencies in order to build tests that dont hit the database, but without tests, how safely can we do the refactoring? Catch 22!

Mocks and stubs add (much) more code to maintain (and to fix bugs!).

Reliance on mocks and stubs can mask errors with using the DB.

Annecdote: GuideWire Software used to depend heavily on stubs and dependency isolation techniques, but now focuses exclusively on tests against the database, using H2 in-memory DB to speed up tests.

DOF: What Problem Does it Solve?

Setup of the DB in a lazy and modular fashion as needed by each test

Only populate what is needed for a test

Test does not worry if DB already populated

Clarity of the DB setup required for a test

See clearly DB objects needed for a test

Establishes a clear pattern for teamwork and reusability behind the DB setup

Simple Example Logical Model

Invoice

Customer

Product

Manufacturer

Logical Object ModelDefines object relationships behind the DOF setup but the DOF setup is about the instances of these objects that have representations in the database

Data Relationships and Object Creation

Invoice 1001

Customer Jones

Product Orange Juice

ManufacturerOcean Spray

Product Grape Juice

Objects CreatedDOF takes care ensuring these DB backed objects are ready for your test

With DOF, test only specifies top level dependency!

This slide tells its story via animation. If youre reading this slide, then the blinds effect means check for this object and pinwheel means create the object

Object Dependency Processing: Java Reference Object

Call rbOrangeJuice.fetch() to check DB for primary key Orange Juice

Return Object Orange Juice

Call rbOrangeJuice.create()

ReferenceBuilder rbTropicana = new Manufacturer_Tropicana();Manufacturer tropicana = DOF.require(rbManufacturerTropicana);

Object pk = rbOrangeJuice.getPrimaryKey() Check cache for Orange Juice object

OJ Found in Database

OJ Foundin Cache

OJ Not Found

OJ Not Found

Check cache, maybe call rbTropicana.fetch()

Call rbTropicana.create()

You persist the Tropicana object

You persist the Orange Juice object with foreign key to Tropicana

Tropicana Found

ReferenceBuilder rbOrangeJuice = new Product_OrangeJuice();Product orangeJuice = (Product) DOF.require(productOJ);

Tropicana Not Found

Your ReferenceBuilder code

DOF plumbing

Your test code

Discuss this slide first with the DOF plumbing and then as if there was no DOF plumbing.

Principles of Test Automation for the DOF

From Meszaros, xUnit Test Patterns

Highly recommended!

Keep Tests Independent Any test can run on its own or with other tests in any order. Independent test failures easiest to reproduce and fix!

With the DOF, you can run test in any order and on a clean or existing DB schema

Absolute nightmare having giant DB setups for tests

Communicate Intent -- minimize code and avoid logic in tests (if statements)

DOF hides the messy setup of preparing objects

Test reader sees only objects involved directly in a test

Dont Modify the SUT -- prefer testing the production code

SUT = System Under Test your production code

DOF supports tests against production code which hits the database

Technique: Scratch Objects

Motivation: Avoid erratic tests that have database dependencies

Cause: Multiple tests depend on and modify the same objects in the database, and thus the tests sometimes fail because of uncertain database state

Solution: Always use a fresh primary key for objects created in a test

private Customer getNewUniqueCustomer() { Customer customer = new Customer(); // Example of pattern to create unique PKs customer.setName(System.currentTimeMillis() + ""); customerComponent.insert(customer); return customer; }

In-Memory DBs for Testing

Fixture setup for test class (not per test!)

Load frozen copy of DB

Load data into tables

Understand that test may interact with each other in the data for a given test class

Breaks the atomic model of JUnit tests

This solution is a little like using lock striping, or maybe the way that conferences might break up the registration lines by first letter of last name.

All tests are not atomic

But groups of tests together form one batch where they get the database to interact with.

Options

H2 supposedly the fastest

HSQLDB Hibernate examples are based on HSQLDB

See article on blog: http://justingordon.org

Dependent Object Framework

Open source project with EPL License: http://sourceforge.net/projects/dof

Problem: setup of required persistent (DB) objects for JUnit tests

Objectives:

Ease of test fixture setup: Simply list dependencies for a test.

Support both reference objects and scratch objects

Performance: Tests must run quicklyCache reference objects in memory

Tests are stable run any number of times in any order

Easily distinguish between reference (shared) objects and scratch (unique, non-shared) objects

Either use files and associated handlers or Java to define objects

Support deletion/garbage collection of created objects

Author: Justin Gordon based on technique used in product MDM Server for Product Information Management

Performance checksif the object with the path was already run, and if so, nothing is done.Then checks if given named object exists in the DB. The name is decoded from the path name. If the name exists, the script is not run.

Roadmap

A tale of two development groups

JUnit Tests

Test Driven Development

Tools

TDD Architecture

Database Techniques: DOF

Getting Started

Making it Happen: Dealing with Legacy Code

If only we could write everything from scratch again!

Expect islands of old, untested, and untestable code

UI code tends to be particularly problematic

Most legacy code will be essentially untestable

Try piecewise remodeling

Discard old modules one-by-one, replacing with TDD code

Try encrapsulation

Wall off legacy junk behind a faade interface (if possible; sometimes not)

Mock out legacy code when testing new modules

CRAP

CleanInterface

Pair Programming and TDD

Complementary: Pair Programming helps with TDD

Big aid in learning TDD

One person thinks strategically, encourages the coder to write tests before the implementation. Don't write a line of code that doesn't fix a failing test.

Pairing allows collaboration on solving dependency isolation issues, along with other issues in getting first tests to run.

Once there's a body of tests to use as examples, pair is less necessary.

Highly recommended when a team is learning TDD.

Making it Happen: Getting the Team Started

Allow extra time

for learning: there are many new skills and patterns to pick up.

for re-architecture: existing architecture probably doesnt support testing

Expect discomfort at first developers not used to working this way.

Start with a few respected early adopters and a trial run

Get Becks Test Driven Development, start up your IDE and do TDD! Or try to recreate my accounting example.

Solidify commitment at every level of the organization

TDD slower for first 6 months; net speedup afterwards.

Dont expect to hold the same schedule and just add testing!

Systematically discover and eliminate obstacles.

TDD takes discipline. Align incentives, communication, work environment everything

Consider using the Dependent Object Framework

Will avoid need to create too many mock objects

Start with a new project or pick a subproject

TDD can be done against existing code, but MUCH harder

Focus on lower levels of the system first

Your best bet!

WASSUP? How enterprise projects without automated tests can end up after 8 years!

http://www.youtube.com/watch?v=Qq8Uc5BFogE

Your Challenges?

What are some of the biggest hurdles your projects face in becoming Test Driven?

E-mail me with your challenges with TDD and maybe I can help.

Im not a formal consultant, but looking for material for my book in terms of real world enterprise problems and solutions in this area, and maybe you can benefit.

Try out the DOF and Id be happy to help.

Conclusion

How would you choose between a project with awesome JUnit tests and a project without JUnit, but lots of great architectural documents and other documentation? Id take the JUnit one hands down.

Documentation gets out of date quickly. Code without tests may be quite buggy, and even if its not buggy, would I trust myself to join a project and not introduce bugs without JUnit?

Having a comprehensive suite of JUnit tests is the most import piece of intellectual property in a software project.

Why? First you have very few bugs. Second, developers, new and old, can change the code with confidence because they know immediately if they break something. This enables the two most important activities in a software project.

Refactoring

Performance Tuning

And TDD is the best way to get that suite of tests (with the DOF for DB testing)!

Thank you for listening!

http://sourceforge.net/projects/dofhttp://[email protected]

Resources

Justin [email protected]://www.tddtips.comhttps://github.com/justin808/dof

Just Do It! You cannot just read books on it! Just Do It!

Use Eclipse or IntelliJ and try doing TDD on a simple example

Gerard Meszaros xUnit Test Patterns

Kent Beck Test Driven Development, Extreme Programming Explained

Lasse Koskela Test Driven: TDD and Acceptance TDD for Java Developers

Martin Fowler, especially Refactoring: Improving the Design of Existing Code

Michael Feathers Working Effectively with Legacy Code

Extras

Persistent Test Fixture Strategy

Purpose of the DOF is Persistent Test Fixture Setup

Keep the test clear by separating the fixture (setup of test) from the test

Shared Test Fixture

Shared among multiple tests

Addresses Performance Issues of creating objects for each test

Must be immutable or else erratic tests

Fresh Test Fixture (Scratch Objects)

Uses unique identifier so no collisions

Used for objects your test will modify

String pk = System.currentTimeMillis() + ;

ReferenceObjects

ScratchObjects

Reference Objects versus Scratch Objects

Reference Objects are

Shared Persistent Test Fixtures

Background data for a test

Loaded once and cached SUPER for PERFORMANCE

Must not be changed by tests immutable!

Reference and Scratch objects can refer to (have foreign keys) to either other reference objects or scratch objects

Scratch Objects

Persistent Fresh Fixtures

These are what the test modifies

Only visible to a single test for a single test run

Run test multiple times and new scratch objects are created

Must be given a unique keys (i.e., timestamp string)

Can refer to reference objects or scratch objects

Ensures that tests dont interfere with each other no erratic test runs!

ReferenceObjects

ScratchObjects

DOF Basic Concepts

One file (or Java class) per object definition.

DOF takes care of recursive object creation. Thus, object dependencies are ONLY listed in an objects definition file (or class), never in a test.

I.e., if object A depends on object B, you should NEVER need to specify both object B and object A in a test. Definition of object A specifies that it depends on object B.

Best practices for use of the DOF

Each developer has own database.

Simple script for populating a clean schema.

Critical to avoid changing reference objects, otherwise tests become erratic and unstable

You can choose Java or Text Files or any combination to define objects.

Deletion (garbage collection) of created objects is available, but discouraged (see next slide).

Java or Text Definitions for Objects?

Java Builder Pattern

Create one Java class per reference object of scratch object definition

Implement interface ReferenceBuilder or ScratchBuilder

Advantage: Full support of IDE for refactoring and navigation

More flexibility with Java code for each object

More explicit object creation

Disadvantage: Can be very verbose compared to a properties file or XML

Text Files and Associated Handlers

For each class (file type), write a handler class that implements interface DependentObjectHandler

Create one file per reference object or scratch object

DOF takes care of the plumbing

Advantage: More concise format

Disadvantage: Can be awkward if changes are needed

Use of Java and Text are not exclusive! Interoperable!

DOF FAQ

How is this different from DBUnit?

DBUnit focuses on DB rows. DOF focuses on object dependencies

Does the DOF only work with database dependencies?

No. Dependencies can be anywhere, such as with web services.

Should I use an in-memory DB?

Being able to switch your DB from Oracle or DB2 to an in-memory one for testing is highly recommended for speeding up test execution. Check out H2 and HSQLDB.

I work on a shared DB and I cant simply recreate the schema. Help!

No problem. Make sure that you run DOF.deleteAll(DOF.DeletionOption.all) when you tear down your tests. If you do that, you will avoid adding any records to the DB.