【问题标题】:Using constants from the class within JUnit test class在 JUnit 测试类中使用类中的常量
【发布时间】:2018-11-02 13:58:20
【问题描述】:

目前我在 Java 方面有点平庸,对 JUnit 也很陌生,但我无法在其他地方找到这个问题的答案。

我想使用在我正在测试的类中定义的常量变量,在 Junit 测试用例本身中,但由于它是一个静态 final,它不可用。

我正在测试的类如下:

public class MyClass {
    private static final int HSIZE = 7;
    private static final int NUM_LOCS = 3;
    .
    .
    .

    void generateRandomLocations() {
        Random rand = new Random();
        String[] hPos = new String[NUM_LOCS]; 

        for (int i=0; i<NUM_LOCS; i++) {
            hPos[i] = Integer.toString(rand.nextInt(HSIZE)); 
        } 
        setLocations(hPos);
    }
}

足够简单,而且似乎工作正常,但我想在 JUnit 中添加一个测试用例 - 如下所示:

@Test
void testGenerateRandomLocations() {
    MyClass mc = new MyClass();

    mc.generateRandomLocations();

    String[] check = mc.getLocations();
    assertEquals(sadc.NUM_LOCS, check.length);

    }

(getter 已定义,为简洁起见,这里没有包含它)

但是(当然)sadc.NUM_LOCS 不可用,因为它只在 MyClass 中可见。

如何从 JUnit (5) 测试用例中访问它?

【问题讨论】:

  • 您可以将常量的可见性更改为公开而不是私有
  • 当它们只在课堂上使用时,这不被认为是糟糕的编码标准吗?还是认为这样做可以以这种方式定义测试?

标签: java junit junit5


【解决方案1】:

我使用了@VisibleForTesting 注释,它注释了为什么通常私有变量和方法受到保护。它们不是完全可见的,但可以出于测试目的访问。我已经将它与 private static final 变量一起使用。

我看到/使用过的示例使用了一个名为 VisibleForTesting.java 的文件,其中包含:

package your.pkg.here;

import java.lang.annotation.Retention;

import static java.lang.annotation.RetentionPolicy.SOURCE;
/**
 * Indicates that the class, method or field has greater
 * visibility than otherwise needed to enhance testability.
 */
@Retention(SOURCE)
public @interface VisibleForTesting {
}

然后在要测试的类中将方法或变量设置为受保护,并使用@VisibleForTesting 注释:

public class MyClass {
    private static final int HSIZE = 7;

    @VisibleForTesting
    protected static final int NUM_LOCS = 3;

    void generateRandomLocations() {
        Random rand = new Random();
        String[] hPos = new String[NUM_LOCS]; 

        for (int i=0; i<NUM_LOCS; i++) {
            hPos[i] = Integer.toString(rand.nextInt(HSIZE)); 
        } 
        setLocations(hPos);
    }
}

这允许测试看到它,但该变量对公众不可用,并且有一个解释为什么它不是私有的。 @VisibleForTesting 也可以在 Google 的 Guava 库中找到,因此您可以包含它而不是添加 VisibleForTesting.java,但我没有使用过。

【讨论】:

  • 我们可以在不改变访问修饰符的情况下做同样的事情吗?
  • 不,您仍然需要将访问修饰符设置为受保护。这只是解释了为什么它没有设置为私有。
【解决方案2】:

您是否尝试过使用 Powermockito。 Whitebox 有 getInternalState。

看看有没有帮助

https://github.com/powermock/powermock/wiki/Bypass-Encapsulation

【讨论】:

    【解决方案3】:

    简单回答

    将变量 NUM_LOCS 公开。然而,这是一个糟糕的设计。

    更好的答案

    通过构造函数传递NUM_LOCS而不是静态变量:

    public class MyClass {
    
        private final int numLocs;
    
        public MyClass(int numLocs) {
            this.numLocs = numLocs;
        }
    
        //default ctor with default numLocs value
        public MyClass() {
            this.numLocs = 3;
        }
    
        //methods
    
    }
    

    现在,您的测试如下所示:

    @Test
    void testGenerateRandomLocations() {
        int expectedNumLocs = 7;
        MyClass mc = new MyClass(7);
        mc.generateRandomLocations();
        String[] check = mc.getLocations();
        assertEquals(expectedNumLocs, check.length);
    }
    

    您也可以进行测试以检查默认值是否为 3:

    @Test
    void testGenerateRandomLocations() {
        int expectedNumLocs = 3;
        MyClass mc = new MyClass(); //no args
        mc.generateRandomLocations();
        String[] check = mc.getLocations();
        assertEquals(expectedNumLocs, check.length);
    }
    

    【讨论】:

    • 如果属性是固定且必不可少的,那么增加类的可见性可能不是一个糟糕的设计。如果要修复大小,则在构造函数中定义它会更糟糕。
    • 如果需要属性来进行测试并且它是私有的,那么毫无疑问这是一个糟糕的设计。如果它是要修复的,那么需要它来编写测试用例是没有意义的。 assertEquals(3, check.length); 就够了。
    • 该属性是固定的,其目的只是为了以后能够在不更改对“3”的每个引用的情况下对其进行扩展。
    • 如果字段只是在该类中使用,将其公开不是一个好习惯......也许,最好考虑一下该测试用例是否有用并提供价值。
    猜你喜欢
    • 2020-08-07
    • 1970-01-01
    • 1970-01-01
    • 2021-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多