我有一个名为ConfigReferenceProcessor
的类,负责实例化给定类型的新对象并使用提供的配置信息对其进行配置。实例化的对象的类型必须从名为BaseConfigurable
的抽象基类派生(在此基类中定义了一些方法,我需要调用这些方法来配置新实例)。 前进的注释:BaseConfigurable
不受我的控制,因此我无法对其代码进行任何更改。
我正在尝试对方法processConfigReference
进行单元测试,该方法执行以下操作:
public <T extends BaseConfigurable> T processConfigReference(
Class<T> clazz, ConfigReferenceCfg configRef) {
// All this in a try/catch
Class<? extends T> objClass = Class.forName(configRef.getClassName())
.asSubclass(clazz);
Constructor<? extends T> constructor = objClass.getConstructor();
T obj = constructor.newInstance();
// Some methods to setup and configure the new instance, including:
obj.loadConfig(configRef.getConfigName());
return obj;
}
在我的单元测试中,我需要控制loadConfig
方法,因为我不想使用文件系统查找等来拖动整个配置行为。我的第一个想法是简单地创建我自己的模拟对象:
static class MockConfigurable extends BaseConfigurable {
@Override
public void loadConfig(String configName) {
// Do nothing.
}
// Mandatory methods
}
不幸的是,我无法覆盖loadConfig
,因为它在基类中声明为final
。
我发现有关使用PowerMockito方法whenNew
模拟对象创建的堆栈溢出的其他问题,并试图通过模拟最终方法来设置我的模拟,并在创建新实例时返回模拟对象:< / p>
@RunWith(PowerMockRunner.class)
@PrepareForTest({ TestConfigReferenceProcessor.MockConfigurable.class, ConfigReferenceProcessor.class })
public class TestConfigReferenceProcessor {
static class MockConfigurable extends BaseConfigurable {
// Mandatory methods
}
@Mock private MockConfigurable mockConfigurable;
@Before
public void initializeMockConfigurableInstance() throws Exception {
doNothing().when(mockConfigurable).loadConfig(any(String.class));
whenNew(MockConfigurable.class).withAnyArguments().thenReturn(mockConfigurable);
}
@Test
public void shouldProcessConfigRef() {
MockConfigurable result =
ConfigReferenceProcessor.forClass(MockConfigurable.class)
.processConfigReference(configRefCfg);
// Fails!
verify(mockConfigurable).loadConfig(any(String.class));
}
}
但这种做法似乎对我不起作用。我怀疑它不起作用,因为我实际上并没有使用new
创建对象。
还有其他方法可以解决这个问题吗?
答案 0 :(得分:0)
BaseConfigurable
无法控制,loadConfig
是最终版,但你可以使用ConfigReferenceProcessor
做你想做的事吗?那么,我认为一个好的方法就是将事物与另一个层次的间接分开。使用ConfigurableFactory
方法创建newConfigurable
类。然后,您可以模拟工厂并验证交互。因此,创建工厂类:
public class ConfigurableFactory {
public <T extends BaseConfigurable> T newConfigurable(Class<T> clazz, String className) throws Exception {
Class<? extends T> objClass = Class.forName(className)
.asSubclass(clazz);
Constructor<? extends T> constructor = objClass.getConstructor();
return constructor.newInstance();
}
}
然后重构原始方法:
public class ConfigReferenceProcessor {
private ConfigurableFactory configurableFactory =
new ConfigurableFactory(); // default instance
public void setConfigurableFactory(ConfigurableFactory configurableFactory) {
this.configurableFactory = configurableFactory;
}
public <T extends BaseConfigurable> T processConfigReference(
Class<T> clazz, ConfigReferenceCfg configRef) throws Exception {
T obj = configurableFactory.newConfigurable(clazz, configRef.getClassName());
// Some methods to setup and configure the new instance, including:
obj.loadConfig(configRef.getConfigName());
return obj;
}
}
这样的东西会测试你的方法:
@RunWith(PowerMockRunner.class)
@PrepareForTest(BaseConfigurable.class)
public class ConfigReferenceProcessorTest {
// public class so the constructor is callable
public static class MockConfigurable extends BaseConfigurable {
// Mandatory methods
}
@Mock
private ConfigReferenceCfg configRefCfg;
@Mock
private ConfigurableFactory configurableFactory;
@Mock
private MockConfigurable mockConfigurable;
@InjectMocks
private ConfigReferenceProcessor processorUT;
@Test
public void shouldProcessConfigRef() throws Exception {
final String className = MockConfigurable.class.getName();
given(configRefCfg.getClassName()).willReturn(className);
given(configRefCfg.getConfigName()).willReturn("testName");
given(configurableFactory.newConfigurable(MockConfigurable.class, className)).
willReturn(mockConfigurable);
MockConfigurable result =
processorUT.processConfigReference(MockConfigurable.class, configRefCfg);
assertThat(result, is(mockConfigurable));
verify(mockConfigurable).loadConfig("testName");
}
}
然后您可以编写工厂类的单独测试,但那些不需要任何模拟 - 只需传入值并获取和实例,并检查实例的类是否符合您的预期。