Skip to content

devill/ObjectFactory.java

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ObjectFactory

Lightweight dependency injection for legacy code. Replace new with testable factories without major refactoring.

Video Introduction

Making Legacy Code Testable with ObjectFactory

Watch: Making Legacy Code Testable with ObjectFactory - Learn the problem ObjectFactory solves and see a simple example in action.

Overview

ObjectFactory provides a simple way to make untestable legacy code testable by replacing direct instantiation (new) with factory calls. This enables test double injection without changing constructors or adding complex dependency injection frameworks.

Key Features

  • Drop-in replacement for new: Minimal code changes required
  • Type-safe factory pattern: Supports both concrete types and interface/implementation pairs
  • Test double injection: Easy to inject mocks, stubs, or fakes in tests
  • Constructor parameter tracking: Automatically logs constructor arguments for test verification
  • Zero runtime overhead: Only active when tests configure it

Installation

Add to your pom.xml:

<dependency>
    <groupId>link.specrec.objectfactory</groupId>
    <artifactId>objectfactory</artifactId>
    <version>1.0.0</version>
</dependency>

Basic Usage

1. Replace new with create()

Before:

EmailService emailService = new EmailService();

After:

import static link.specrec.objectfactory.GlobalObjectFactory.*;

EmailService emailService = create(EmailService.class).with();

2. With constructor arguments

Before:

EmailService emailService = new EmailService("smtp.example.com", 587);

After:

EmailService emailService = create(EmailService.class).with("smtp.example.com", 587);

3. Interface and implementation

IEmailService emailService = create(IEmailService.class, EmailService.class).with();

Testing

Setting up test doubles

@Test
void testEmailSending() {
    ObjectFactory factory = ObjectFactory.getInstance();

    // Create a mock email service
    MockEmailService mockEmail = new MockEmailService();

    // Configure factory to return mock for one call
    factory.setOne(IEmailService.class, mockEmail);

    // Your code under test will receive the mock
    YourClass instance = new YourClass(); // internally calls create()
    instance.doSomething();

    // Verify the mock was called as expected
    assertTrue(mockEmail.wasCalled());

    // Clean up
    factory.clearAll();
}

Using setAlways for multiple calls

@Test
void testMultipleInstantiations() {
    ObjectFactory factory = ObjectFactory.getInstance();
    MockEmailService mockEmail = new MockEmailService();

    // Always return this mock
    factory.setAlways(IEmailService.class, mockEmail);

    // All calls will receive the same mock
    YourClass instance1 = new YourClass();
    YourClass instance2 = new YourClass();

    factory.clearAll();
}

Advanced Features

Constructor Parameter Tracking

Implement IConstructorCalledWith to receive constructor parameter information:

class FakeEmailService implements IEmailService, IConstructorCalledWith {
    private ConstructorParameterInfo[] parameters;

    @Override
    public void constructorCalledWith(ConstructorParameterInfo[] parameters) {
        this.parameters = parameters;
        // Log or validate parameters
    }
}

This is particularly useful for recording and replaying interactions in approval testing scenarios.

API Reference

ObjectFactory

  • create(Class<T> type) - Create builder for concrete type
  • create(Class<I> interface, Class<T> implementation) - Create builder for interface/implementation pair
  • setOne(Class<T> type, T instance) - Queue instance for next creation
  • setAlways(Class<T> type, T instance) - Always return this instance
  • clear(Class<T> type) - Clear settings for type
  • clearAll() - Clear all settings

GlobalObjectFactory

Static methods for convenience (recommended usage):

  • create(Class<T> type) - Uses singleton ObjectFactory instance
  • create(Class<I> interface, Class<T> implementation) - Uses singleton ObjectFactory instance

Best Practices

  1. Use static imports: Import GlobalObjectFactory.* for cleaner code
  2. Clear in teardown: Always call ObjectFactory.getInstance().clearAll() in test cleanup
  3. Prefer interfaces: Use interface/implementation pairs when possible for better testability
  4. Start simple: Begin with setOne() and move to setAlways() only when needed

License

PolyForm Noncommercial License 1.0.0

Related Projects

ObjectFactory is extracted from SpecRec, a comprehensive legacy testing library that includes recording, replay, and approval testing features.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •  

Languages