【问题标题】:how to unit test a class which creates new objects如何对创建新对象的类进行单元测试
【发布时间】:2012-10-05 16:15:36
【问题描述】:

我正在使用 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 方法的最佳方法是什么。

非常感谢……

【问题讨论】:

  • 名称中带有Test的两个类实际上并不是测试。在关于测试的讨论中,这有点让人头疼。
  • @SimonGibbs 是的。我发布的课程不是测试,这些是应该测试的课程。让我们想象这些类将被命名为 A 和 B :)
  • 我同意西蒙关于弯头的说法。顺便说一句,您尝试过捕获吗?
  • 我更改了类名。你是对的,他们一直在混淆。我的问题:我如何测试 A 类中的方法 getStatus,因为它使用 B 类的外部方法 getStatus。有没有模拟对象的方法?

标签: java unit-testing junit mockito


【解决方案1】:

我查看了您的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 方法,因此这些不是单独方法的测试。

【讨论】:

  • 谢谢public final void testGetBListIsEmpty() public final void testGetBListIsNotEmpty() public final void testGetStatus_StatusIsPassedWhenNoB() public final void testGetStatus_StatusIsPassedWhenEveryBPassed() public final void testGetStatus_StatusIsFailedWhenOneBIsNotPassed() public final void testGetStatus_StatusIsFailedWhenSomeBAreNotPassed()
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

    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() {
         // ...
      }
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-12-19
      • 1970-01-01
      • 2021-11-24
      • 1970-01-01
      • 1970-01-01
      • 2017-12-11
      • 2010-09-11
      • 2021-12-22
      相关资源
      最近更新 更多