0

伙计们,我是 JUnit 测试的新手并试图很好地掌握它,现在我正在为构造函数(用于创建有向图的 Digraph 类)编写 JUnit 测试,当它读取负 int 值并创建图形时抛出 IllegalArgumentException如果一切正常(节点值的数量)大于零。

有向图类:

 In in = new In();
 public Digraph(In in) {
  try {
    this.nodes = in.readInt();
    System.out.println("Total nodes in graph: "+ nodes);
    if (nodes < 0) throw new IllegalArgumentException("Number of vertices must be > 0);
    int E = in.readInt();
    if (E < 0) throw new IllegalArgumentException("Number of edges must be >0);
  }catch (NoSuchElementException e) {
     throw new InputMismatchException("Invalid input format in Digraph constructor");
  }

以下是我正在尝试编写的测试:

@Rule
  public ExpectedException exception = ExpectedException.none();  

@Test(expected = IllegalArgumentException.class)
public void DigraphIn() {

    Digraph G = new Digraph(in.readInt());

    exception.expect(IllegalArgumentException.class);
    exception.expectMessage("Vertices can't be nagative");
    exception.expectMessage("Invalid input format in Digraph constructor");
    exception.expectMessage("Number of edges in a Digraph must be nonnegative");
try{
}catch (AssertionError e){
    }
}

我应该如何使用一个(或两个)测试用例来测试这两种情况?如果“in”没有检测到 -ve 值,我会得到 java.lang.AssertionError 否则测试通过。提前致谢

4

2 回答 2

2

你应该有很多测试用例。每个例外都有一个很好。

您执行的每个测试都是不同的,应该区别对待。

一个很好的参考是:Junit Cookbook

实际上我在你的代码中看到了一个错误。在您的测试用例中,您可以模拟如下协作者。我做了一个模拟,使用“mockito”模拟库在每次调用时返回不同的值。

你基本上需要类似的东西:

@Test(expected = IllegalArgumentException.class)
public void DigraphInThatThrowsExceptionForVertices() {
    In in = Mockito.mock(In.class);
    when(in.readInt()).thenReturn(-1);
    Digraph G = new Digraph(in);
    fail();
}

@Test(expected = IllegalArgumentException.class)
public void DigraphInThatThrowsExceptionForEdges() {
    In in = Mockito.mock(In.class);
    when(in.readInt()).thenReturn(10).thenReturn(-1);
    Digraph G = new Digraph(in);
    fail();
}

@Test
public void DigraphInThatDoesNotThrowException() {
    In in = Mockito.mock(In.class);
    when(in.readInt()).thenReturn(10).thenReturn(15);
    Digraph G = new Digraph(in.readInt());
}

这种方式测试代码更干净,更易于阅读。

于 2013-09-23T20:17:02.627 回答
1

当您测试一个方法时,您实际上调用了它,这将使它执行。该测试每次测试只能验证一个异常,因为该异常将取消该方法的其余处理。因此,您需要对可能引发异常的每个地方进行一次测试。

在使用单元测试验证是否引发异常时,基本上有 3 种方法可以做到:

一个 try-catch 块:

@Test
public void myTest() {
    try {
        myClass.myMethod(42);
        fail();
    } catch(final IllegalArgumentException e) {
        assertEquals("something went wrong", e.getMessage());
    }
}

expected-annotation 的 -属性@Test

@Test(expected=IllegalArgumentException.class)
public void myTest() {
    myClass.myMethod(42);
}

ExpectedException

@Rule
public ExpectedException expectedException = ExpectedException.none();

@Test
public void myTest() {
    expectedException.expect(IllegalArgument.class);
    expectedException.expectMessage("something went wrong");

    myClass.myMethod(42);
}

在您的示例中,您尝试使用所有三个。

如果我们比较测试异常的方法,实际上只有第一种和第三种能够对抛出的异常进行验证,这使得它们更适合于可以从多个地方抛出相同类型的异常的测试方法,那么你可以使用异常消息来验证异常是从您将其发送到的位置引发的。

第二个是迄今为止最易读的,但不允许您区分被测方法抛出的异常,在大多数情况下,它提供的价值不如其他两个。

在第一个和第三个中,第三个是迄今为止最易读的,也是我个人最喜欢的。

由于被测方法每次执行只能抛出一个异常,因此对于每个可能抛出异常的地方,它应该有一个测试方法:

public class DiagraphTest {

    @Rule
    public ExpectedException expectedException = ExpectedException.none();

    private Diagraph diagraph;
    private In in;

    @Before
    public void setup() {
        in = mock(In.class); 
    }

    @Test
    public void constructorShouldThrowExceptionWhenNumberOfVerticesIsLessThanOne() {
         expectedException.expect(IllegalArgumentException.class);
         expectedException.expectMessage("vertices must be > 0"); //expectMessage only needs a substring of the exception-message

         doReturn(-1).when(in).readInt();

         new Diagraph(in);
    }

    @Test
    public void constructorShouldThrowExceptionWhenNumberOfEdgesIsLessThanOne() {
         expectedException.expect(IllegalArgumentException.class);
         expectedException.expectMessage("edges must be > 0"); 

         when(in.readInt()).thenReturn(42, -1);

         new Diagraph(in);
    }

    //as to the last exception, I really can't see that it will ever be thrown in that try-block, but here's a test for that as well..
    @Test
    public void constructorShouldThrowInputMismatchExceptionIfReceivedNoSuchElementException() {
         expectedException.expect(InputMismatchException.class);
         expectedException.expectMessage("Invalid input format); 

         doThrow(new NoSuchElementException("phail")).when(in).readInt();

         new Diagraph(in);
    }

}

于 2013-09-23T21:20:21.873 回答