如何模拟单例工厂的对象创建?
How to mock object creation for a singleton factory?
我尝试遵循的示例:
@PrepareForTest(X.class)
public class XTest extends PowerMockTestCase {
@Test
public void test() {
whenNew(MyClass.class).withNoArguments().thenThrow(new IOException("error message"));
X x = new X();
x.y(); // y is the method doing "new MyClass()"
..
}
}
工厂class我正在尝试单元测试:
public final class LoadableBeanFactory implements ILoadableBeanFactory {
private static final class Loader {
private static final LoadableBeanFactory INSTANCE = new LoadableBeanFactory();
}
private LoadableBeanFactory() { }
public static @Nonnull LoadableBeanFactory getInstance() {
return Loader.INSTANCE;
}
public final @Nonnull <BeanT extends ILoadableBean> BeanT create(final Class<BeanT> beanClass) {
final BeanT optionBean;
try {
final Constructor<BeanT> ctor = beanClass.getConstructor();
optionBean = ctor.newInstance();
return beanClass.cast(optionBean);
} catch(Exception e) {
throw new IllegalArgumentException("Could not instantiate an instance of " + beanClass);
}
}
}
我的测试如下。工厂没有 return 模拟。我在想这是因为工厂是一个实例化并加载了私有静态加载器 class 的单例。那么,有没有办法模拟这个对象创建场景,或者我应该放弃将其变成真正的单元测试?
@PrepareForTest(LoadableBeanFactory.class)
@Test(groups = {"FactoryTestGroup", "LoadableBeanFactoryTestGroup"})
public class LoadableBeanFactoryTest extends PowerMockTestCase {
@Mock LoadableBean mockBean;
public void setUp() {
MockitoAnnotations.initMocks(this);
}
@Test
public void shouldCreateBean() {
try {
PowerMockito.whenNew(LoadableBean.class).withNoArguments().thenReturn(mockBean);
LoadableBeanFactory.getInstance().create(LoadableBean.class);
assertEquals(LoadableBeanFactory.getInstance().create(LoadableBean.class), mockBean,
"LoadableBeanFactory should have return mocked bean, but did not: " + mockBean);
} catch(Exception e) {
fail("Failed to mock bean creation");
}
}
}
你为什么要这么做?
如果您将工厂包装在抽象中(一个单独的 class),那么您可以通过构造函数注入它并模拟其创建方法。
public class BeanFactory {
public <BeanT extends ILoadableBean> BeanT create(final Class<BeanT> beanClass) {
return LoadableBeanFactory.getInstance().create(beanClass);
}
}
现在是您想要合作的 class
public class SomeClass {
private final BeanFactory beanFactory;
public SomeClass(BeanFactory beanFactory) {
this.beanFactory= beanFactory;
}
public void doSth() {
beanFactory.create(...);
}
}
然后你根本不需要 PowerMock,你的设计真的很棒。
我尝试遵循的示例:
@PrepareForTest(X.class)
public class XTest extends PowerMockTestCase {
@Test
public void test() {
whenNew(MyClass.class).withNoArguments().thenThrow(new IOException("error message"));
X x = new X();
x.y(); // y is the method doing "new MyClass()"
..
}
}
工厂class我正在尝试单元测试:
public final class LoadableBeanFactory implements ILoadableBeanFactory {
private static final class Loader {
private static final LoadableBeanFactory INSTANCE = new LoadableBeanFactory();
}
private LoadableBeanFactory() { }
public static @Nonnull LoadableBeanFactory getInstance() {
return Loader.INSTANCE;
}
public final @Nonnull <BeanT extends ILoadableBean> BeanT create(final Class<BeanT> beanClass) {
final BeanT optionBean;
try {
final Constructor<BeanT> ctor = beanClass.getConstructor();
optionBean = ctor.newInstance();
return beanClass.cast(optionBean);
} catch(Exception e) {
throw new IllegalArgumentException("Could not instantiate an instance of " + beanClass);
}
}
}
我的测试如下。工厂没有 return 模拟。我在想这是因为工厂是一个实例化并加载了私有静态加载器 class 的单例。那么,有没有办法模拟这个对象创建场景,或者我应该放弃将其变成真正的单元测试?
@PrepareForTest(LoadableBeanFactory.class)
@Test(groups = {"FactoryTestGroup", "LoadableBeanFactoryTestGroup"})
public class LoadableBeanFactoryTest extends PowerMockTestCase {
@Mock LoadableBean mockBean;
public void setUp() {
MockitoAnnotations.initMocks(this);
}
@Test
public void shouldCreateBean() {
try {
PowerMockito.whenNew(LoadableBean.class).withNoArguments().thenReturn(mockBean);
LoadableBeanFactory.getInstance().create(LoadableBean.class);
assertEquals(LoadableBeanFactory.getInstance().create(LoadableBean.class), mockBean,
"LoadableBeanFactory should have return mocked bean, but did not: " + mockBean);
} catch(Exception e) {
fail("Failed to mock bean creation");
}
}
}
你为什么要这么做?
如果您将工厂包装在抽象中(一个单独的 class),那么您可以通过构造函数注入它并模拟其创建方法。
public class BeanFactory {
public <BeanT extends ILoadableBean> BeanT create(final Class<BeanT> beanClass) {
return LoadableBeanFactory.getInstance().create(beanClass);
}
}
现在是您想要合作的 class
public class SomeClass {
private final BeanFactory beanFactory;
public SomeClass(BeanFactory beanFactory) {
this.beanFactory= beanFactory;
}
public void doSth() {
beanFactory.create(...);
}
}
然后你根本不需要 PowerMock,你的设计真的很棒。