Lightweight dependency injection for legacy code. Replace new with testable factories without major refactoring.
Watch: Making Legacy Code Testable with ObjectFactory - Learn the problem ObjectFactory solves and see a simple example in action.
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.
- 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
Add to your pom.xml:
<dependency>
<groupId>link.specrec.objectfactory</groupId>
<artifactId>objectfactory</artifactId>
<version>1.0.0</version>
</dependency>Before:
EmailService emailService = new EmailService();After:
import static link.specrec.objectfactory.GlobalObjectFactory.*;
EmailService emailService = create(EmailService.class).with();Before:
EmailService emailService = new EmailService("smtp.example.com", 587);After:
EmailService emailService = create(EmailService.class).with("smtp.example.com", 587);IEmailService emailService = create(IEmailService.class, EmailService.class).with();@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();
}@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();
}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.
create(Class<T> type)- Create builder for concrete typecreate(Class<I> interface, Class<T> implementation)- Create builder for interface/implementation pairsetOne(Class<T> type, T instance)- Queue instance for next creationsetAlways(Class<T> type, T instance)- Always return this instanceclear(Class<T> type)- Clear settings for typeclearAll()- Clear all settings
Static methods for convenience (recommended usage):
create(Class<T> type)- Uses singleton ObjectFactory instancecreate(Class<I> interface, Class<T> implementation)- Uses singleton ObjectFactory instance
- Use static imports: Import
GlobalObjectFactory.*for cleaner code - Clear in teardown: Always call
ObjectFactory.getInstance().clearAll()in test cleanup - Prefer interfaces: Use interface/implementation pairs when possible for better testability
- Start simple: Begin with
setOne()and move tosetAlways()only when needed
PolyForm Noncommercial License 1.0.0
ObjectFactory is extracted from SpecRec, a comprehensive legacy testing library that includes recording, replay, and approval testing features.
