仅仅为了测试而使用额外的构造函数有多好?

时间:2016-08-12 14:41:56

标签: java spring oop spring-boot

目前我需要写SecondClass并决定使用这种解决方案进行编写,否则它将无法测试。

@Component
public class FirstClass {

    public void doStuff() {
        System.out.println("First Class stuff!");
    }

}

@Component
public class SecondClass {

    private final Random random;
    private final FirstClass firstClass;

    @Autowired
    public SecondClass(FirstClass firstClass) {
        this(new Random(), firstClass);
    }

    public SecondClass(Random random, FirstClass firstClass) {
        this.random = random;
        this.firstClass = firstClass;
    }

    public void doOtherStuff() {
        firstClass.doStuff();
        System.out.println("Second Class stuff " + random.nextInt(10));
    }

}

我的同事们并不喜欢我解决问题的方法,并喜欢SecondClass这样的实施:

@Component
public class SecondClass {

    private Random random;
    private final FirstClass firstClass;

    @Autowired
    public SecondClass(FirstClass firstClass) {
        this.random = new Random();
        this.firstClass = firstClass;
    }

    public void doOtherStuff() {
        firstClass.doStuff();
        System.out.println("Second Class stuff " + random.nextInt(10));
    }

    public void setRandom(Random random) {
        this.random = random;
    }

}

我不同意这种解决方案,因为我认为Random是这个类的必要部分,它不会在运行时更改,并且setter仅用于测试目的和那个为什么我更喜欢两个构造函数的解决方案

我们也提出了这种构造函数:

@Autowired(required = false)
public SecondClass(FirstClasss firstClass, Random random) {
    this.random = (random == null ? new Random() : random)
    ...
}

但实际上构造函数中注入了更多的组件,因此如果需要所有组件,那将更为可取。

我很感兴趣,如果有人在此之前有这种经历吗?您如何看待这种情况,以及是否有更好的方法来解决这个问题?

3 个答案:

答案 0 :(得分:2)

你的问题源于最后的领域。跟随鲍勃叔叔,随意让他们受到保护,这样你就可以在测试课上给他们写信。您不是在编写界面,而是在编写代码,因此访问修饰符并不重要,因为它允许包访问,所以受保护很好。

或者您可以通过硬连接将它们自己设置在测试用例中,比如ReflectionUtils.setField

答案 1 :(得分:1)

如果在SecondClass中声明了两个构造函数,并且仅在单元测试中使用其中一个,则意味着单元测试的构造函数可以替换为单元测试的更相关技巧,例如用于设置random字段值的反射 如果可以避免,则不应仅为单元测试打开API 当您无法选择时,您应该打开您的API以进行单元测试。

如果random字段是只读的,并且只要SecondClass被实例化,就会知道一个已知值,那么SecondClass应该有一个构造函数。 random字段应该是最终的,并在SecondClass的构造函数中进行评估。

答案 2 :(得分:1)

你是对的,我的朋友。您的方法是正确的,因为根据面向对象的设计,必须在执行任何构造函数后将对象初始化为有效状态。因此,如果random字段是必需的,必须在所有构造函数中初始化。此外,如果以后不再更改,必须是最终(并且没有设置者)。所以第二种解决方案不正确,因为它违反了这一原则。

我不喜欢第三种解决方案,因为它对客户端代码来说可能是愚蠢的:虽然客户端假设null被设置为输入值,但实际上另一个(未受控制的)值是反而用了。

因此,如果您需要SecondClass的构造函数可以测试,请转到并添加它。因为如果一个类不可测试,它也不可执行。测试是确保类可以使用的正确方法。