我应该如何对长函数进行单元测试?

Asa*_*bal 6 java unit-testing

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

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

rob*_*cox 5

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

您需要进行的更改如下:

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

下面是一些代码示例,展示了它的样子 - 请注意,此代码只是说明该模式,您将需要一些更合理的名称。研究这种模式并了解更多有关依赖注入和模拟的知识是值得的——这是单元测试人员军械库中最强大的两种武器。

数据源

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

具有长方法的类

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

单元测试

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
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

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