首页 > 解决方案 > JUnit 我应该使用什么断言

问题描述

我想寻求帮助和建议在我的情况下什么是正确的方法(可能很容易,但我只是从 JUnit 开始)。这是我的代码的一部分

public boolean arrayListEmpty()
    {
        if(numericalSequence.isEmpty() == true)
            return true;
        else
            return false;
    }

这是模型中的一个公共方法,我想我应该测试它,如果我的数字序列为空,它只会返回 true,如您所见。我不能直接调用 numericSequence.isEmpty (在我需要它的控制器中)检查它,因为它是私有的。很明显的事情是检查

assertEquals(true, test.arrayListEmpty());

所以我的问题是关于建议我应该使用哪些其他断言/我应该预测哪些情况会出现。我应该在测试方法中用一些值填充 numericSequence 并断言它吗?(对我来说没有必要,因为插入到序列中的任何值 = 不为空,但可能并不那么容易)

标签: junitassertassertion

解决方案


首先,欢迎来到 Stack Overflow!

看看你的问题,听起来你是单元测试的新手(如果我错了,请纠正我)。所以,我将把我的答案分成两个部分;(1) 回答你的问题,(2) 给出如何编写好的测试和可测试类的大致方向。

1. 回答你的问题

您可以在这里想到更多用例:

  1. 如果numericalSequence是会发生什么null
  2. 如果numericalSequence有 1 个元素怎么办?
  3. 如果numericalSequence有一个空元素怎么办?

根据您的类的设置方式,上述某些情况可能无法实现,但这是一个值得进行的测试,因此违反这些测试用例之一的“先前约定的行为”的类的任何更改都会失败。

2. 编写好的测试

对于如何编写好的测试没有严格的指导方针,但是,如果您将代码构建为可测试的,您会发现编写好的测试变得更容易,并且不会那么繁琐。请允许我解释一下。

注意:这不是一个全面的指南,但旨在开始您的旅程,了解如何编写更好的代码和更好的测试

所以假设一个需要测试的类是这样写的:

class MyClass {
  // NOTE: This is not `final`
  private List<Integer> numericalSequence;

  public MyClass() {
    this.numericalSequence = new ArrayList<>();
  }

  public void doSomething(Integer x) {
    if (x < 0) {
      numericalSequence = new ArrayList<>();
    } else {
      numericalSequence.add(2 * x);
    }
  }

  public boolean arrayListEmpty() {
    // NOTE: Your sample code can be reduced to this one-liner
    return numericalSequence.isEmpty();
  }
}

以下是上述代码中的一些缺陷:

  1. doSomething方法允许为空(装箱整数)值,因此可以NullPointerException在第一if条语句中导致。
  2. numericalSequence成员不是最终的,因此将其设置为的代码在方法null内是有效的doSomething
  3. arrayListIsEmpty方法不检查是否numericalSequence为空
  4. 如果我想测试为 null 或具有 null 元素arrayListIsEmpty时的行为numericalSequence方式,则很难做到这一点,除非您知道如何让类进入该状态。

如果该类被重写为以下内容:

class MyClass {
  private final List<Integer> numericalSequence;

  public MyClass() {
    this(new ArrayList<>());
  }

  // Should be made public only if the classes that use this one needs to
  // initialize this instance with the sequence. Make this package-private
  // otherwise.
  public MyClass(List<Integer> sequence) {
    this.numericalSequence = sequence;
  }

  public void doSomething(int x) {
    if (x < 0) {
      // COMPILE ERROR: Cannot assign to a final member
      // numericalSequence = new ArrayList<>();
      numericalSequence.clear();
    } else {
      numericalSequence.add(2 * x);
    }
  }

  public boolean arrayListEmpty() {
    return numericalSequence == null || numericalSequence.isEmpty();
  }
}

关于上述结构需要注意的几点:

  1. 有两个构造函数;默认调用将整数列表作为序列的那个,以便它重用两者都需要共享的任何逻辑。此示例中没有逻辑,但希望您很快就会遇到。
  2. doSomething()方法不接受Integer值,但int值确保它x是 never null(Java 数字原始类型不能是null)。这也意味着,numericalSequence不能通过doSomething().
  3. 由于numericalSequence可以从此类外部初始化,因此该arrayListEmpty()方法确保检查numericalSequence是否null真的为空。

现在您可以像这样编写测试用例:

@Test
public void arrayListEmpty_WhenListIsNull() {
  MyClass test = MyClass(null);
  assertTrue(test.arrayListEmpty());
}

@Test
public void arrayListEmpty_WhenListIsEmpty() {
  MyClass test = MyClass();
  assertTrue(test.arrayListEmpty());
}

@Test
public void arrayListEmpty_WhenListHasOnlyOneNonNullElement() {
  List<Integer> sequence = new ArrayList<>();
  sequence.add(0);
  MyClass test = new MyClass(sequence);
  assertFalse(test.arrayListEmpty());
}

@Test
public void arrayListEmpty_WhenListHasOnlyOneNullElement() {
  List<Integer> sequence = new ArrayList<>();
  sequence.add(null);
  MyClass test = new MyClass(sequence);
  assertFalse(test.arrayListEmpty());
}

由于doSomething()添加/清除序列,因此在编写测试以doSomething()确保调用并通过检查返回值来验证类的状态arrayListEmpty()

例如:

@Test
public void doSomething_WhenInputLessThanZero() {
  List<Integer> sequence = new ArrayList<>();
  sequence.add(0);
  MyClass test = new MyClass(sequence);
  test.doSomething(-1);
  assertTrue(test.arrayListEmpty());
}

我的意图是展示几件事:

  1. 将您的测试用例组织得小而简洁
  2. 将您的课程设计为易于测试

希望这可以帮助。


推荐阅读