为什么测试也需要重构?

本文讲述了在TDD过程中如何通过重构测试用例,消除设计重复,保持测试一致性,以及针对构造函数测试的优化,确保得到结构优良且反映代码意图的测试代码。
摘要由CSDN通过智能技术生成

我们在使用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());
}

无论你是间接传递的, 还是直接传递, 这两个测试代码都是一样的,也就说这个是一个设计重复, 这两个东西就是我们在设计决策中产生的遗留。

因此需要重构来消除我们的测试构造中的留下来的不一样印记, 比如

  1. 之前的架构选择
  2. 之前设计上的这样的一些决策。
  3. 一些隐藏的测试不一致

通过重构测试去获得更好的组织测试, 才能够保证我们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并不是一成不变的, 它是会随着我们架构的改变而改变的。

  • 26
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值