简体   繁体   English

我应该如何对长功能进行单元测试?

[英]How should I unit-test a long function?

If i have a long method of code which gathers data from 2 or 3 difference sources and returns a result. 如果我有一个很长的代码方法,它从2或3个差异源收集数据并返回结果。 How can I refactor it so that it is more unit-testable? 我怎样才能重构它以使它更可单元测试? This method is a webservice and I want to make one call from client code to gather all the data. 这个方法是一个web服务,我想从客户端代码调用一个来收集所有数据。

I can refactor some portions out into smaller methods which will be more testable. 我可以将一些部分重构成更小的方法,这些方法更容易测试。 But the current method will still be calling those 5 methods and will remain less testable. 但是目前的方法仍然会调用这5种方法,并且仍然不太可测试。 Assuming Java as programming language, is there a pattern for making such code testable? 假设Java是编程语言,是否有一种模式可以使这些代码可测试?

This is a very common testing problem, and the most common solution I come across for this is to separate the sourcing of data from the code which uses the data using dependency injection. 这是一个非常常见的测试问题,我遇到的最常见的解决方案是将数据源与使用依赖注入使用数据的代码分开。 This not only supports good testing, but is generally a good strategy when working with external data sources (good segregation of responsibilities, isolates the integration point, promotes code reuse being some reasons for this). 这不仅支持良好的测试,而且在处理外部数据源时通常是一个很好的策略(良好的职责分离,隔离集成点,促进代码重用是其中的一些原因)。

The changes you need to make go something like: 您需要做出的改变如下:

  • For each data source, create an interface to define how data from that source is accessed, and then factor out the code which returns the data into a separate class which implements this. 对于每个数据源,创建一个接口来定义如何访问来自该源的数据,然后将将数据返回到实现此目的的单独类中的代码分解出来。
  • Dependency inject the data source into the class containing your 'long' function. 依赖关系将数据源注入包含“long”函数的类中。
  • For unit testing, inject a mock implementation of each data source. 对于单元测试,请注入每个数据源的模拟实现。

Here is some code examples showing what this would look like - note that this code is merely illustrative of the pattern, you will need some more sensible names for things. 下面是一些代码示例,显示了它的外观 - 请注意,此代码仅仅是模式的说明,您需要一些更合理的名称。 It would be worth studying this pattern and learning more about dependency injection & mocking - two of the most powerful weapons in the unit testers armory. 值得研究这种模式并学习更多关于依赖注入和嘲弄的知识 - 单位测试者军械库中最强大的两种武器。

Data Sources 数据源

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;
    }
}

Class with Long Method 长方法的类

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;
    }
}

Unit Test 单元测试

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
        ...
    }
}

Please forgive (and correct) any syntactic mistakes, I don't write much java these days. 请原谅(并纠正)任何语法错误,这些天我写的不多。

The test for the "big" method will look like integration test where the smaller methods can be mocked. “大”方法的测试看起来像集成测试,其中较小的方法可以被模拟。

If you can separate the "big" method into five isolated methods, then the "big" method could be further partitioned into semantically-/contextually-meaningful groups of the isolated methods. 如果您可以将“大”方法分成五个独立的方法,那么“大”方法可以进一步划分为隔离方法的语义/上下文有意义的组。

Then you can mock the larger groupings of isolated methods for the "big" method. 然后你可以为“大”方法模拟更大的孤立方法分组。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM