5

如果我有一个很长的代码方法,它从 2 或 3 个不同的来源收集数据并返回结果。如何重构它以使其更具单元测试性?此方法是一种 Web 服务,我想从客户端代码调用一次以收集所有数据。

我可以将一些部分重构为更易于测试的更小的方法。但当前方法仍将调用这 5 个方法,并且可测试性较低。假设 Java 作为编程语言,是否有使此类代码可测试的模式?

4

2 回答 2

5

这是一个非常常见的测试问题,我遇到的最常见的解决方案是将数据的来源与使用依赖注入的数据的代码分开。这不仅支持良好的测试,而且在使用外部数据源时通常是一个很好的策略(良好的职责分离、隔离集成点、促进代码重用是其中的一些原因)。

您需要进行的更改类似于:

  • 对于每个数据源,创建一个接口来定义如何访问来自该源的数据,然后将返回数据的代码分解到一个单独的类中,该类实现了这一点。
  • 依赖项将数据源注入到包含“long”函数的类中。
  • 对于单元测试,注入每个数据源的模拟实现。

以下是一些代码示例,展示了它的外观 - 请注意,此代码仅用于说明模式,您需要一些更合理的名称。值得研究这种模式并了解更多关于依赖注入和模拟的信息——这是单元测试工具库中最强大的两种武器。

数据源

public interface DataSourceOne {
    public Data getData();
}

public class DataSourceOneImpl implements DataSourceOne {
    public Data getData() {
        ...
        return data;
    }
}

public interface DataSourceTwo {
    public Data getData();
}

public class DataSourceTwoImpl implements DataSourceTwo {
    public Data getData() {
        ...
        return data;
    }
}

长方法类

public class ClassWithLongMethod {
    private DataSourceOne dataSourceOne;
    private DataSourceTwo dataSourceTwo;

    public ClassWithLongMethod(DataSourceOne dataSourceOne,
                               DataSourceTwo dataSourceTwo) {
        this.dataSourceOne = dataSourceOne;
        this.dataSourceTwo = dataSourceTwo;
    }

    public Result longMethod() {
        someData = dataSourceOne.getData();
        someMoreData = dataSourceTwo.getData();
        ...
        return result;
    }
}

单元测试

import org.junit.Test;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class ClassWithLongMethodTest {

    @Test
    public void testLongMethod() {

        // Create mocked data sources which return the data required by your test
        DataSourceOne dataSourceOne = mock(DataSourceOne.class);
        when(dataSourceOne.getData()).thenReturn(...);
        DataSourceTwo dataSourceTwo = mock(DataSourceTwo.class);
        when(dataSourceTwo.getData()).thenReturn(...);

        // Create the object under test using the mocked data sources
        ClassWithLongMethod sut = new ClassWithLongMethod(dataSourceOne,
                                                          dataSourceTwo);

        // Now you can unit test the long method in isolation from it's dependencies
        Result result = sut.longMethod();

        // Assertions on result
        ...
    }
}

请原谅(并纠正)任何语法错误,这些天我写的Java不多。

于 2013-07-18T08:06:57.097 回答
0

“大”方法的测试看起来像集成测试,其中可以模拟较小的方法。

如果您可以将“大”方法分成五个孤立的方法,那么“大”方法可以进一步划分为孤立方法的语义/上下文意义组。

然后,您可以为“大”方法模拟更大的隔离方法分组。

于 2013-07-17T19:08:04.893 回答