FlexTradeUKLtd / jfixture

JFixture is an open source library based on the popular .NET library, AutoFixture
MIT License
105 stars 22 forks source link

Static 'copy constructor' methods should not be used to create a specimen #30

Closed wellingtonsteve closed 8 years ago

wellingtonsteve commented 8 years ago

Some background first. We use another library, which given an interface such as the following:

public interface InterfaceType {
    String getSymbol();  
    int getSize();
}

generates an implementation which includes a static copy() method. Note the private constructor

public class TypeWithCopyFactoryMethod implements InterfaceType {

    private final String symbol;
    private final int size;

    private TypeWithCopyFactoryMethod(String symbol, int size) {
        this.symbol = symbol;
        this.size = size;
    }

    public static TypeWithCopyFactoryMethod copy(InterfaceType source) {
        return new TypeWithCopyFactoryMethod(source.getSymbol(), source.getSize());
    }

    public static TypeWithCopyFactoryMethod create(String symbol, int size) {
        return new TypeWithCopyFactoryMethod(symbol, size);
    }

    @Override
    public String getSymbol() {
        return symbol;
    }

    @Override
    public int getSize() {
        return size;
    }

}

If we try to use JFixture to generate an instance of TypeWithCopyFactoryMethod it fails, trying to use the copy() method instead of the create() method because the former has fewer parameters.

java.lang.UnsupportedOperationException: JFixture was unable to create an instance of testtypes.factorymethods.InterfaceType
Most likely because it has no public constructor, is an abstract or non-public type or has no static factory methods.
If this isn't the case it's likely that all constructors and factory methods on the type have thrown an exception.
To view any thrown exceptions just add the Tracing Customisation to the JFixture instance, e.g. fixture.customise(new TracingCustomisation(System.out));
    testtypes.factorymethods.TypeWithCopyFactoryMethod --> 
    testtypes.factorymethods.TypeWithCopyFactoryMethod copy(testtypes.factorymethods.InterfaceType) --> 
    testtypes.factorymethods.InterfaceType --> 
    testtypes.factorymethods.InterfaceType

This is only an issue with the interface; if you change the signature to public static TypeWithCopyFactoryMethod copy(TypeWithCopyFactoryMethod source) it works, presumably because the circular dependency is detected and ClassToFactoryMethodRelay automatically moves on to the next method.

Pull request to follow

richkeenan commented 8 years ago

This is great! Thanks @wellingtonsteve for the PR. I'll get that merged and push out a new version immediately

richkeenan commented 8 years ago

Thats's now been pushed to the Nexus OSS repo and will be available from Maven Central next time they sync (few hours usually). Version number is 2.6.2. Thanks again for your contribution

wellingtonsteve commented 8 years ago

Rich, I'm only seeing version 2.6.2 for jfixture-parent, but not the underlying modules. Should it have synced by now?

richkeenan commented 8 years ago

Yea I just spotted that - weird. I'll look into right now.

EDIT: Only the parent is in https://oss.sonatype.org so I think something went wrong with the release

richkeenan commented 8 years ago

OK I've pushed a new release, 2.6.3 (it needs to be a new version number even though nothing changed). Looks like it was due to a recent change in my deployment script. Sorry for the inconvenience.

I can see the latest version in the OSS repo for all jfixture projects, https://oss.sonatype.org/service/local/repositories/releases/content/com/flextrade/jfixture/jfixture/2.6.3/ so it's just a matter of waiting for the sync

wellingtonsteve commented 8 years ago

No worries, thanks for the quick response.