如何对在其 builder() 方法中使用静态单例工厂的构建器进行单元测试?
How to unit test a builder which uses a static singleton factory in its builder() method?
我正在尝试对 Selenium 页面对象 API 进行单元测试,并且我有一堆构建器 类 使用 static
单例工厂 class
来实例化想要的对象。我一辈子都想不出一种方法来对构建器 class
中的 build()
方法进行单元测试。如何为我的构建器模拟 'object creation'?
public class LoadableBuilder<LoadableT extends ILoadable, BeanT extends ILoadableBean>
implements ILoadableBuilder<LoadableT, BeanT> {
/**
* The ILoadableBean object that specifies all the necessary information to construct an instance of the
* ILoadable that this LoadableBuilder builds
*/
private final @Getter @Nonnull BeanT state;
/**
* The class that of the ILoadable that this LoadableBuilder builds
*/
private @Getter @Nullable Class<LoadableT> componentClass;
public AbstractLoadableBuilder(final BeanT state) {
this.state = state;
}
@Override
public final @Nonnull LoadableBuilder setComponentClass(final Class<LoadableT> componentClass) {
this.componentClass = componentClass;
return this;
}
@Override
public final @Nonnull LoadableBuilder setDriver(final WebDriver driver) {
getState().setDriver(driver);
return this;
}
@Override
public final @Nonnull LoadableBuilder setLoadTimeoutInSeconds(final @Nonnegative int loadTimeoutInSeconds) {
getState().setLoadTimeoutInSeconds(loadTimeoutInSeconds);
return this;
}
@Override
public @Nonnull LoadableT build() {
return LoadableFactory.getInstance().create(getState(), componentClass);
}
}
这里是工厂:
public class LoadableFactory {
private static final class Loader {
private static final LoadableFactory INSTANCE = new LoadableFactory();
}
private LoadableFactory() { }
public static LoadableFactory getInstance() {
return Loader.INSTANCE;
}
public final<BeanT extends ILoadableBean, LoadableT extends ILoadable> LoadableT create(final BeanT bean, final Class<LoadableT> componentClass) {
final LoadableT component;
try {
final Constructor<LoadableT> ctor = ConstructorUtils.getMatchingAccessibleConstructor(componentClass, bean.getClass());
component = ctor.newInstance(bean);
} catch (InstantiationException e) {
throw new IllegalArgumentException("Could not instantiate an instance of " + componentClass + because it is abstract or an interface or for some other reason.");
} catch (InvocationTargetException e) {
throw new IllegalStateException("Could not instantiate an instance of " + componentClass + " because the constructor threw an exception. Cause: " + e.getCause() +". " + e.getMessage());
} catch (IllegalAccessException e) {
throw log.throwing(new IllegalArgumentException("Could not instantiate an instance of " + componentClass + " LoadableFactory does not have access to its class definition");
}
}
}
我不太确定这是否是最佳实践,但我过去处理此类问题的方法是将 getInstance()
调用移至另一个受保护的方法然后在我的测试中覆盖它 class。你需要让你的 LoadableFactory 实现一个接口来模拟 create()
调用:
protected ILoadableFactory getFactory(){
return LoadableFactory.getInstance();
}
然后将您的 LoadableBuilder
class 改为使用此:
@Override
public @Nonnull LoadableT build() {
return getFactory().create(getState(), componentClass);
}
然后,在你的 LoadableBuilderTest
class:
public class LoadableBuilderTest extends LoadableBuilder {
Mockery context = new Mockery();
final LoadableFactory mockFactory = context.mock(ILoadableFactory.class); //This is the interface you'll need to make which LoadableFactory implements. It will need the create() method.
@Override
protected ILoadableFactory getFactory(){
return mockFactory;
}
@Test
public void shouldCallCreateProperly(){
context.checking(new Expectations(){{
oneOf(mockFactory).create(arg1,arg2);
}});
//Do your stuff to test that create() was called.
}
}
我正在尝试对 Selenium 页面对象 API 进行单元测试,并且我有一堆构建器 类 使用 static
单例工厂 class
来实例化想要的对象。我一辈子都想不出一种方法来对构建器 class
中的 build()
方法进行单元测试。如何为我的构建器模拟 'object creation'?
public class LoadableBuilder<LoadableT extends ILoadable, BeanT extends ILoadableBean>
implements ILoadableBuilder<LoadableT, BeanT> {
/**
* The ILoadableBean object that specifies all the necessary information to construct an instance of the
* ILoadable that this LoadableBuilder builds
*/
private final @Getter @Nonnull BeanT state;
/**
* The class that of the ILoadable that this LoadableBuilder builds
*/
private @Getter @Nullable Class<LoadableT> componentClass;
public AbstractLoadableBuilder(final BeanT state) {
this.state = state;
}
@Override
public final @Nonnull LoadableBuilder setComponentClass(final Class<LoadableT> componentClass) {
this.componentClass = componentClass;
return this;
}
@Override
public final @Nonnull LoadableBuilder setDriver(final WebDriver driver) {
getState().setDriver(driver);
return this;
}
@Override
public final @Nonnull LoadableBuilder setLoadTimeoutInSeconds(final @Nonnegative int loadTimeoutInSeconds) {
getState().setLoadTimeoutInSeconds(loadTimeoutInSeconds);
return this;
}
@Override
public @Nonnull LoadableT build() {
return LoadableFactory.getInstance().create(getState(), componentClass);
}
}
这里是工厂:
public class LoadableFactory {
private static final class Loader {
private static final LoadableFactory INSTANCE = new LoadableFactory();
}
private LoadableFactory() { }
public static LoadableFactory getInstance() {
return Loader.INSTANCE;
}
public final<BeanT extends ILoadableBean, LoadableT extends ILoadable> LoadableT create(final BeanT bean, final Class<LoadableT> componentClass) {
final LoadableT component;
try {
final Constructor<LoadableT> ctor = ConstructorUtils.getMatchingAccessibleConstructor(componentClass, bean.getClass());
component = ctor.newInstance(bean);
} catch (InstantiationException e) {
throw new IllegalArgumentException("Could not instantiate an instance of " + componentClass + because it is abstract or an interface or for some other reason.");
} catch (InvocationTargetException e) {
throw new IllegalStateException("Could not instantiate an instance of " + componentClass + " because the constructor threw an exception. Cause: " + e.getCause() +". " + e.getMessage());
} catch (IllegalAccessException e) {
throw log.throwing(new IllegalArgumentException("Could not instantiate an instance of " + componentClass + " LoadableFactory does not have access to its class definition");
}
}
}
我不太确定这是否是最佳实践,但我过去处理此类问题的方法是将 getInstance()
调用移至另一个受保护的方法然后在我的测试中覆盖它 class。你需要让你的 LoadableFactory 实现一个接口来模拟 create()
调用:
protected ILoadableFactory getFactory(){
return LoadableFactory.getInstance();
}
然后将您的 LoadableBuilder
class 改为使用此:
@Override
public @Nonnull LoadableT build() {
return getFactory().create(getState(), componentClass);
}
然后,在你的 LoadableBuilderTest
class:
public class LoadableBuilderTest extends LoadableBuilder {
Mockery context = new Mockery();
final LoadableFactory mockFactory = context.mock(ILoadableFactory.class); //This is the interface you'll need to make which LoadableFactory implements. It will need the create() method.
@Override
protected ILoadableFactory getFactory(){
return mockFactory;
}
@Test
public void shouldCallCreateProperly(){
context.checking(new Expectations(){{
oneOf(mockFactory).create(arg1,arg2);
}});
//Do your stuff to test that create() was called.
}
}