如何用渐进状态编写JUnit测试

时间:2015-05-23 15:57:22

标签: java testing junit

我是JUnit的新手,正在寻求建议。为了开始自己,我选择了一个简单的帮助对象,用于管理字符串表。这些测试的进展与我所阅读的指南中鼓励的粒度相反。

尝试在不重复测试的情况下保持粒度良好,我已经为相关方法创建了一些测试,例如AddAndSize或GetAndPut。但是,拥有在多个测试中设置测试的代码似乎很奇怪,我真的试图通过jUnit站稳脚跟并找到测试粒度的平衡。这是我的Target和TestCase -

  • 我是否已经开始了任何我应该避免的坏习惯?
  • 这是一个典型的"使用jUnit?
  • 在测试抽象类时,我应该放置" Stub"测试或主要源文件夹中的目标?

DataTable是一个待测试的类

public class DataTable {
    private ArrayList<String> columnNames = new ArrayList<String>();
    private ArrayList<ArrayList<String>> theData = new ArrayList<ArrayList<String>>();

    public DataTable() {
    }

    public int size() {
        return theData.size();
    }

    public int cols() {
        return columnNames.size();
    }

    public void addCol(String name) {
        this.columnNames.add(name);
    }

    public int getCol(String name) {
        return columnNames.indexOf(name);
    }

    public String getCol(int index) {
        if (index < 0 | index >= columnNames.size()) {return "";}
        return columnNames.get(index);
    }

    public String getValue(int row, String name) {
        return getValue(row,this.getCol(name)); 
    }

    public String getValue(int row, int col) {
        if (row < 0 | row >= theData.size()) {return "";}
        if (col < 0 | col >= theData.get(row).size()) {return "";}
        return theData.get(row).get(col);
    }

    public ArrayList<String> getNewRow() {
        ArrayList<String> newRow = new ArrayList<String>();
        this.theData.add(newRow);
        return newRow;
    }
}

以下是我写过的测试用例。

public class DataTableTest {

    /**
     * Test Constructor
     */
    @Test
    public void testDataTableConstruction() {
        DataTable table = new DataTable();
        assertNotNull(table);
    }

    /**
     * Test GetNewRow and Size
     */
    @Test
    public void testGetNewRowAndSize() {
        DataTable table = new DataTable();
        assertEquals(0, table.size());
        ArrayList<String> row = table.getNewRow();
        assertNotNull(row);
        assertEquals(1, table.size());
    }

    /**
     * 
     */
    @Test
    public void testColsAndAddCol() {
        DataTable table = new DataTable();
        assertEquals(0, table.cols());
        table.addCol("One");
        table.addCol("Two");
        table.addCol("Three");
        assertEquals(3, table.cols());
    }

    /**
     * 
     */
    @Test
    public void testGetColInt() {
        DataTable table = new DataTable();
        table.addCol("One");
        table.addCol("Two");
        table.addCol("Three");
        assertEquals("One", table.getCol(0));
        assertEquals("Two", table.getCol(1));
        assertEquals("Three", table.getCol(2));
    }

    /**
     * 
     */
    @Test
    public void testGetColString() {
        DataTable table = new DataTable();
        table.addCol("One");
        table.addCol("Two");
        table.addCol("Three");
        assertEquals(0, table.getCol("One"));
        assertEquals(1, table.getCol("Two"));
        assertEquals(2, table.getCol("Three"));
        assertEquals(-1, table.getCol("Four"));
    }

    /**
     * 
     */
    @Test
    public void testGetValueIntString() {
        DataTable table = new DataTable();
        table.addCol("One");
        table.addCol("Two");
        table.addCol("Three");
        ArrayList<String> row = table.getNewRow();
        row.add("R1C1");
        row.add("R1C2");
        row.add("R1C3");
        row = table.getNewRow();
        row.add("R2C1");
        row.add("R2C2");
        row.add("R2C3");

        assertEquals("R1C1", table.getValue(0, "One"));
        assertEquals("R1C3", table.getValue(0, "Three"));
        assertEquals("R2C2", table.getValue(1, "Two"));
        assertEquals("", table.getValue(2, "One"));
        assertEquals("", table.getValue(0, "Four"));
    }

    /**
     * 
     */
    @Test
    public void testGetValueIntInt() {
        DataTable table = new DataTable();
        table.addCol("One");
        table.addCol("Two");
        table.addCol("Three");
        ArrayList<String> row = table.getNewRow();
        row.add("R1C1");
        row.add("R1C2");
        row.add("R1C3");
        row = table.getNewRow();
        row.add("R2C1");
        row.add("R2C2");
        row.add("R2C3");

        assertEquals("R1C1", table.getValue(0, 0));
        assertEquals("R1C3", table.getValue(0, 2));
        assertEquals("R2C2", table.getValue(1, 1));
        assertEquals("", table.getValue(2, 0));
        assertEquals("", table.getValue(0, 3));
    }
}

3 个答案:

答案 0 :(得分:0)

我建议使用the Enclosed runner,并为要用于测试的每个初始状态设置不同的静态嵌套类:

@RunWith(Enclosed.class)
public class DataTableTest {

  @RunWith(JUnit4.class)
  public static class WhenTableIsEmpty {
    private final DataTable table = new DataTable();

    @Test
    public void rowCountShouldReturnZero() {
      assertEquals(0, table.rowCount());
    }

    @Test
    public void addingRowsShouldSucceed() {
      table.addRow("row1");
      assertEquals(1, table.rowCount());
    }

    ...
  }

  @RunWith(JUnit4.class)
  public static class WhenTableHasOneRow {
    private final DataTable table = new DataTable();

    @Before
    public void addOneRow() {
      table.addRow("row1");
    }

    @Test
    public void rowCountShouldReturnOne() {
      assertEquals(1, table.rowCount());
    }

    ...
  }
}

请注意,嵌套类必须为static(我忽略了在此答案的初始版本中添加static关键字)

当您在IDE中运行测试时,测试用例将具有如下可读名称:

  • DataTableTest.WhenTableIsEmpty.rowCountShouldReturnZero
  • DataTableTest.WhenTableIsEmpty.addingRowsShouldSucceed

请注意,您不需要为嵌套类使用JUnit4运行器。您还可以使用ParameterizedTheories

答案 1 :(得分:0)

我认为只有少数事情值得测试:

  • getCol(int)
  • getValue(int, String)
  • getValue(int, int)
  • 可能 getNewRow()

我这样说的原因是:您依靠ArrayList来完成大部分功能,而且您不应该测试任何专门代表已知且经过测试的类的内容。

测试时你想做的主要事情是:

  • 确定您正在测试的事物的状态
  • 仅测试那件事

让我们进行getCol(int)的测试。为清楚起见,我会在此处重新发布代码。

public String getCol(int index) {
    if (index < 0 | index >= columnNames.size()) {return "";}
    return columnNames.get(index);
}

您可以测试四件事:

  • index < 0 && index >= columnNames.size()
  • index >= 0 && index >= columnNames.size()
  • index < 0 && index < columnNames.size()
  • index >= 0 && index < columnNames.size()

或者,如果您将|更改为||,则只需测试三件事:

  • index < 0
  • index >= columnNames.size()
  • index >= 0 && index < columnNames.size()

原因是|不会短路,并且会评估该条件的双方。

设置您的州必须逐个测试。通过这种方式,您可以清楚地了解您正在测试的内容,为什么会失败,并且可以更轻松地解决问题。我不会拼出你需要的每一个测试(因为有很多),但他们读了这样的东西:

@Test
public void testGetColWithIndexLessThanZero() {}

确定以填写每个测试的状态。如果您发现自己重复了状态生成,那么只有 那么您可以在测试中创建一个帮助方法来帮助解决这个问题。

  

在测试抽象类时,我应该放置&#34; Stub&#34;测试或主要源文件夹中的目标?

您无法直接实例化抽象类,因此您能够测试它的唯一方法是创建一个扩展它的类。您可以直接执行此操作(扩展抽象类的实际类),也可以在测试中创建(创建扩展抽象类的匿名类)。

答案 2 :(得分:-1)

我会像任何软件一样,将公共部分复制到一个地方等等。我的建议是:

public class MyTests {
  private DataTable table;

  @Before
  public void setup() {
    table = new DataTable();
    assertEquals(0, table.size());
    assertEquals(0, table.cols());
    table.addCol("One");
    table.addCol("Two");
    table.addCol("Three");
    assertEquals(3, table.cols());
  }

  /**
   * Test GetNewRow and Size
   */
  @Test
  public void testGetNewRowAndSize() {
    ArrayList<String> row = table.getNewRow();
    assertNotNull(row);
    assertEquals(1, table.size());
  }

  @Test
  public void testGetColInt() {
    assertEquals("One", table.getCol(0));
    assertEquals("Two", table.getCol(1));
    assertEquals("Three", table.getCol(2));
  }

  @Test
  public void testGetColString() {
    assertEquals(0, table.getCol("One"));
    assertEquals(1, table.getCol("Two"));
    assertEquals(2, table.getCol("Three"));
    assertEquals(-1, table.getCol("Four"));
  }

  private void addRows() {
    ArrayList<String> row = table.getNewRow();
    row.add("R1C1");
    row.add("R1C2");
    row.add("R1C3");
    row = table.getNewRow();
    row.add("R2C1");
    row.add("R2C2");
    row.add("R2C3");
  }

  @Test
  public void testGetValueIntString() {
    addRows();
    assertEquals("R1C1", table.getValue(0, "One"));
    assertEquals("R1C3", table.getValue(0, "Three"));
    assertEquals("R2C2", table.getValue(1, "Two"));
    assertEquals("", table.getValue(2, "One"));
    assertEquals("", table.getValue(0, "Four"));
  }

  @Test
  public void testGetValueIntInt() {
    addRows();
    assertEquals("R1C1", table.getValue(0, 0));
    assertEquals("R1C3", table.getValue(0, 2));
    assertEquals("R2C2", table.getValue(1, 1));
    assertEquals("", table.getValue(2, 0));
    assertEquals("", table.getValue(0, 3));
  }
}

不确定之前的断言是否有任何用途,如果那些不持有,任何测试都会失败。无论如何,在我看到的时候,写作测试没有特别的魔力。