我们在使用TDD的时候, 会不断添加测试, 这是因为我们需要通过这些测试去驱动完成我们的功能。
但是并不意味着我们获得的测试就是一个良好的测试,我们也需要对这些测试用例进行进一步的重构, 比如我们观察下面的测试代码。
@Test
public void should_throw_exception_if_dependency_not_found() {
setup();
config.bind(Component.class, ComponentWithInjectConstructor.class);
DependencyNotFoundException exception = assertThrows(DependencyNotFoundException.class, () -> {
config.getContext();
});
assertEquals(Dependency.class, exception.getDependency());
assertEquals(Component.class, exception.getComponent());
}
@Test
public void should_throw_exception_if_transitive_dependency_not_found() {
setup();
config.bind(Component.class, ComponentWithInjectConstructor.class);
config.bind(Dependency.class, DependencyWithInjectConstructor.class);
DependencyNotFoundException exception = assertThrows(DependencyNotFoundException.class, ()-> {
config.getContext();
});
assertEquals(String.class, exception.getDependency());
}
无论你是间接传递的, 还是直接传递, 这两个测试代码都是一样的,也就说这个是一个设计重复, 这两个东西就是我们在设计决策中产生的遗留。
因此需要重构来消除我们的测试构造中的留下来的不一样印记, 比如
通过重构测试去获得更好的组织测试, 才能够保证我们TDD进行之后, 我们得到了结构优秀的代码, 同时在我们的测试中, 真实反映了我们代码的那个意图。
首先我们删除should_throw_exception_if_transitive_dependency_not_found
测试函数, 然后创建一个dependencycheck类,
然后将dependency相关的处理的测试函数, 移到该类中。
然后我们处理关于constructor相关的测试用例, 我们发现constructor的测试用例是依赖于 ConstructorInjectionProvider实例的
我们希望将它重构new ConstructorInjectionProvider<>(implementation)
//提取方法
@Test
public void should_throw_exception_if_multi_inject_constructors_provide() {
setup();
assertThrows(IllegalComponentException.class, () -> {
getBind();
});
}
private void getBind() {
config.bind(Component.class, ComponentWithMultiInjectConstructor.class);
}
//然后引入一个参数:
private void getBind(Class<? extends Component> implementation) {
config.bind(Component.class, implementation);
}
@Test
public void should_throw_exception_if_have_no_inject_constructors_provide() {
setup();
assertThrows(IllegalComponentException.class, () -> {
getBind(ComponentWithNoInjectConstructorNorDefaultConstructor.class);
});
}
然后我们将 getBind() 中的内容转换成new ConstructorInjectionProvider<>(implementation);
然后inline回去。
下一步我们就要将constructorInjectTest的结构和其它的两个测试methodInjectTest, FieldInjectTest测试保持一致。在后两个测试中我们针对ConstructorProvider进行了测试, 而在构造constructorInjectTest的时候并没有构造它, 为了保持一致, 我们也需要在constructor中针对ConstructorProvider进行测试。
@Test
public void should_include_dependency_from_inject_constructor() {
ConstructorInjectionProvider<ComponentWithInjectConstructor> provider = new ConstructorInjectionProvider<>(ComponentWithInjectConstructor.class);
assertArrayEquals(new Class<?>[]{Dependency.class}, provider.getDependencies().toArray(Class<?>[]::new));
}
然后我们将这三者包含在一起,命名为一个新的类叫做InjectTest, 然后移动出去。
class InjectorTest {
class ConstructorTest {}
class MethodInjectTest{}
class FieldInjectTest{}
}
这样我的测试上下文就重构完成了, 所以写过的测试case并不是一成不变的, 它是会随着我们架构的改变而改变的。