如何对创建新对象的类进行单元测试

6

我正在使用JUnit和Mockito来测试一些类。该类本身会从另一个类创建一个对象,一个称为testList的列表。这是我的代码:

public class A {
       private List<B> bList;

       //returns the bList
       public List<B> getBList() {
          return bList;
       }

       //checks the status by calling getStatus in class B
       public Status getStatus() {
          //status is an enum consists of PASSED and FAILED
          Status finalStatus = Status.PASSED;
          for (B be : this.getTestList()) {
             if (be.getStatus() != Status.PASSED) {
                finalStatus = Status.FAILED;
                break;
             }
          }
          return status;
       }
    }


    public Class B {
       private Status status = Status.FAILED;   

       public getStatus() {
          return status;
       }

       public void setStatus(Status status) {
          this.status = status;
       }
    }

如何最好地测试名为Test的类中的getStatus和getTestList方法?

非常感谢...


1
名称中带有"Test"的两个类实际上并不是测试。在关于测试的讨论中,这有点令人困惑。 - Simon Gibbs
@SimonGibbs 没错。我发的那些类并不是测试,而是应该被测试的类。让我们假设这些类被命名为A和B :) - Funky Monkey
我同意Simon关于头疼的问题。顺便问一下,你试过Capture吗? - bluesman
我已经更改了类名。你是对的,它们确实令人困惑。 我的问题是:如何测试A类中的getStatus方法,因为它使用B类中的外部getStatus方法。 是否有使用模拟对象的方法? - Funky Monkey
3个回答

4

我看了你的ClassA,想知道如何设置bList的值。目前,它除了null之外没有其他的值,这意味着每次调用getStatus时都会抛出空指针异常。

你的问题在于你在思考如何测试方法,而不是如何测试行为。一个原因是你的类必须以某种方式适应你的应用程序。为了确保它能够做到这一点,它需要确定的行为,而不是每个方法中的细节。因此,唯一有意义的测试是检查行为。

更加隐蔽的是,测试单个方法使你集中精力于你实际编写的代码。如果你在编写测试时查看你的代码,那么你的测试就成为了自我实现的预言。你可能错过了整个类所需提供的一整套行为;但是如果你只测试你的类确实提供的行为,你将永远不会知道。

所以,回到手头的问题。我看到你的类可能需要满足四个,也许五个行为。也许还有更多——如果你只展示代码而不展示规范,很难确定。你应该为每个行为编写一个测试。我坚信每个测试的名称应该描述行为,而不是反映测试使用的方法的名称。在这种情况下,我可能会选择以下名称。

public void statusIsPassedWhenEveryTestPassed()
public void statusIsFailedWhenEveryTestFailed()
public void statusIsFailedWhenSomeTestsPassedSomeFailed()
public void statusIsPassedWhenNoTests()
public void statusIsPassedWhenTestsNotSet() // this one currently fails

每个测试中,我会创建一个ClassA对象,然后执行必要的操作来设置对象内的bList。最后,我会调用getStatus并断言返回值是否为所需值。但重要的是,每个测试(除了最后一个)都使用了ClassA的多个方法,因此这些不是单个方法的测试。


公共最终void testGetBListIsEmpty() 公共最终void testGetBListIsNotEmpty() 公共最终void testGetStatus_StatusIsPassedWhenNoB() 公共最终void testGetStatus_StatusIsPassedWhenEveryBPassed() 公共最终void testGetStatus_StatusIsFailedWhenOneBIsNotPassed() 公共最终void testGetStatus_StatusIsFailedWhenSomeBAreNotPassed() - Funky Monkey

0

我想这取决于你如何在单元测试中填充testList。如果你有一个setter,那么你就不需要任何mocking框架。

class TestTest {

  Test test = new Test();

  @Test void should_return_failed_if_a_single_test_failed() {
     givenTestListWithOneFailedTest();
     assertThat(test.getStatus(), is(Status.FAILED))
  }

  void givenTestListWithOneFailedTest() {
     test.setTestList(createSomeTestListWithOnlyOneFailedTest());
  }

  @Test void should_return_passed_if_all_tests_passed() {
     // ...
  }

}

0

你可以为所涉及的对象提供受保护的setter(或构造函数注入),然后在测试用例中扩展该类以模拟这些对象,或者尝试使用类似于powermock的东西。您仍然需要提供一种设置这些对象的方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接