【问题标题】:Writing test case for Junit testing为 Junit 测试编写测试用例
【发布时间】:2010-08-03 16:19:19
【问题描述】:

作为一名开发人员,我是单元测试的新手,需要编写一个测试用例来对以下代码进行单元测试。有人可以在这里帮助我,也可以给我一些关于如何在 Eclipse 中编写单元测试的指示。

private void handle(final DocumentEvent e) { 
    Document doc = e.getDocument(); 
    try { 
       String text = e.getDocument().getText(0, doc.getLength()); 

       if (text.length() >= maxMessageSize) { 
            try { 
               component.getHighlighter()
                        .addHighlight(maxMessageSize, text.length() + 1, painter); 
            } catch (BadLocationException ex) { 
               System.out.println(ex.getMessage()); 
            } 
       } else { 
            component.getHighlighter().removeAllHighlights(); 
       } 
    } catch (BadLocationException e1) { 
       System.out.println(e1.getMessage()); 
    } 
} 

谢谢


更新

由于某种原因,当我运行测试用例时,我根本没有得到任何覆盖。我在这里做错了吗?进一步的研究表明我需要使用 test.perform() 方法来调用我想要测试的方法。对吗?你能提出一些建议吗?这是代码:

public class TestMaxLength {
  static final int maxMessageSize = 125;
  JTextPane textPane = new JTextPane();
  //***EasyMock varibles****
  private JTextComponent mockComponent;
  private MaxLength classUnderTest;
  private DocumentEvent mockEvent;
  private Document mockDocument;
  private Highlighter mockHighlighter;

  @Before public void setUp() {
    mockComponent = EasyMock.createMock(JTextComponent.class);
    mockEvent = EasyMock.createMock(DocumentEvent.class); 
    mockDocument = EasyMock.createMock(Document.class); 
    EasyMock.expect(mockEvent.getDocument()).andStubReturn(mockDocument);
    EasyMock.expect(mockDocument.getLength()).andReturn(256); 
    mockHighlighter = EasyMock.createMock(Highlighter.class); 
    EasyMock.expect(mockComponent.getHighlighter()).andReturn(mockHighlighter);
  }

  @Test public void testSetLength() { 
    MaxLength maxListener = new MaxLength(125); 
    maxListener.decorate(textPane);
  }

  @Test 
  public void testEmptyText() { 
    EasyMock.expect(mockDocument.getText(0, 1)).andStubReturn(""); 
    mockHighlighter.removeAllHighlights(); 
    EasyMock.replay(mockComponent, mockEvent, mockDocument, mockHighlighter);      
    classUnderTest.handle(mockEvent);      
    EasyMock.verify(mockComponent, mockEvent, mockDocument, mockHighlighter); 
  }     
}

decorate(JtextComponent jComponent) 方法存在于要测试的类 (MaxLength) 中,定义为:

public final void decorate(final JTextComponent c) {
  //TODO throw exception if already decorating
  this.component = c;
  component.getDocument().addDocumentListener(this);
}
#

更新:

@Peter:设法发现问题不是 Component 类,而是我需要 asm (http://forge.ow2.org/projects/asm)。我还更改了代码以将 2 种方法合并为 1 种方法:

public void testEmptyText() 
{ 
maxSizeListener.decorate(mockComponent);
//mockHighlighter.removeAllHighlights(); 
EasyMock.replay(mockComponent, mockEvent, mockDocument, mockHighlighter); 
maxSizeListener.handle(mockEvent); 
EasyMock.verify(mockComponent, mockEvent, mockDocument, mockHighlighter); 
} 

但现在我在验证时遇到了另一个错误:

java.lang.AssertionError: 
Expectation failure on verify:
getHighlighter(): expected: 1, actual: 0
at org.easymock.internal.MocksControl.verify(MocksControl.java:184)
at org.easymock.EasyMock.verify(EasyMock.java:2038)
at net.TestMaxLength.testEmptyText(TestMaxLength.java:98)

这是在 mockComponent 上执行 EasyMock.verify() 语句时引起的。

【问题讨论】:

  • 能否格式化代码以使其可读(并删除行号)?
  • 这一定是个技巧问题,因为你不应该对私有方法进行单元测试。

标签: java eclipse unit-testing junit


【解决方案1】:

我建议使用 模拟框架,例如 EasyMock。 Mocks 允许您为测试配置具有所需行为的依赖项。在您的情况下,您需要一个模拟 DocumentEvent,理想情况下是另一个模拟 component,我猜它是一个班级成员。

单元测试的两个方面

  • 如何进行测试,即在正确状态下组装正确对象集以使测试正常运行的技术细节(又名 _test 夹具),以及
  • 要测试的内容,即要验证的场景。

如何测试

Eclipse 支持开箱即用的 JUnit,因此您可以快速生成新的 JUnit 测试用例(在 Project Explorer 上下文菜单中:New -> (Other ->) JUnit -> JUnit Test Case),然后通过单击 Run 运行它按钮。

在您的案例中设置测试夹具看起来像这样,使用 EasyMock(并假设您可以将组件作为构造函数参数传递给您的测试类):

private Component mockComponent;
private ClassUnderTest classUnderTest;
private DocumentEvent mockEvent;
private Document mockDocument;
private Highlighter mockHighlighter;

@Before
public void setUp() {
    mockComponent = createMock(Component.class);
    classUnderTest = new ClassUnderTest(mockComponent);
    mockEvent = createMock(DocumentEvent.class);
    mockDocument = createMock(Document.class);
    expect(mockEvent.getDocument()).andStubReturn(mockDocument);
    expect(mockDocument.getLength()).andReturn(1);
    mockHighlighter = createMock(Highlighter.class);
    expect(mockComponent.getHighlighter()).andReturn(mockHighlighter);
}

@Test
public void testEmptyText() {
    expect(mockDocument.getText(0, 1)).andStubReturn("");
    mockHighlighter.removeAllHighlights();
    replay(mockComponent, mockEvent, mockDocument, mockHighlighter);

    classUnderTest.handle(mockEvent);

    verify(mockComponent, mockEvent, mockDocument, mockHighlighter);
}

此测试假定 maxMessageSize 默认至少为 1 - 为测试设置 maxMessageSize 作为练习留给您作为练习,因为您发布的代码 sn-p 对此没有任何线索。

测试什么

您展示的方法从与事件关联的文档中获取文本,然后根据其长度,它执行不同的操作。我至少会为此编写以下单元测试:

  • 带有maxMessageSize == 0 的空文档文本
  • 带有maxMessageSize > 0 的空文档文本
  • 带有maxMessageSize == text.length() 的非空文档文本
  • 带有maxMessageSize > text.length() 的非空文档文本
  • 带有maxMessageSize < text.length()addHighlight()的非空文档文本抛出BadLocationException

注意事项

  1. 感应BadLocationException 有点棘手,因为它所产生的只是输出到标准输出;幸运的是,您可以通过System.setOut 轻松重新分配标准输出。但是,您可能需要考虑改进异常处理,至少通过使用日志框架而不是打印到标准输出。
  2. 从代码看来,其他方法(例如removeAllHighlights() 和/或getText())也可能会抛出BadLocationException,但是try-catch 块组织得不好。我会考虑在这些方法抛出的地方添加更多单元测试,然后重构异常处理代码。

更新

我认为我做错了什么...请您提供修改/更正的代码吗???

您的 testSetLength 方法并没有真正测试任何东西 - 您需要断言语句(和/或 EasyMock 验证)才能让您的单元测试真正验证某些行为。但是,它为设置测试类提供了缺失的线索。因此,我尝试统一您的两种测试方法,以创建一种希望可以正常工作的方法(我是从内存中编写的,所以我不能保证它在第一次尝试时会全部编译并完美运行):

  @Test 
  public void testEmptyText() { 
    // set up the test class with a specific max length
    classUnderTest = new MaxLength(125); 
    // this shall be called from inside decorate()
    mockDocument.addDocumentListener(classUnderTest); 
    // the mock document shall always return an empty text
    EasyMock.expect(mockDocument.getText(0, 1)).andStubReturn(""); 
    // we expect this to be called from inside handle()
    mockHighlighter.removeAllHighlights();
    // start replay mode
    EasyMock.replay(mockComponent, mockEvent, mockDocument, mockHighlighter); 
    // inject mock component into tested object
    maxListener.decorate(mockComponent); 

    // call the tested method
    classUnderTest.handle(mockEvent); 

    // verify that all expected calls to the mocks have been made    
    EasyMock.verify(mockComponent, mockEvent, mockDocument, mockHighlighter); 
  }

【讨论】:

  • 嗨,Péter Török,您能否给我一些手动安装 EasyMock 的说明。我尝试按照 Easymock 文档页面的说明在 POM 文件中添加此依赖项,然后尝试运行 mvn instal 但它没有用。我在那里做错了什么吗?在这种情况下,您能否让我知道如何从 maven repo 获取此依赖项。另外,我已经下载了 jar 文件,想知道如何以这种方式安装 Easymock。谢谢。
  • @Global,奇怪。对我们来说,它奏效了。 “mvn instal没用”到底是什么意思?
  • 我在顶层的项目 POM 文件中添加了以下代码:org.easymockeasymock3.0test 没有关于 EasyMock 文档的进一步说明,因此我尝试通过运行此命令从 Maven 存储库下载依赖项。我几周前才开始使用 Maven ,所以不知道我还需要做什么。谢谢。
  • @Global,“顶级”是指它不在<dependencies> 部分内吗?如果是这样,请将其移到那里并重试mvn install
  • @Péter 不,我确实在 部分包含了easymock。我们如何检查这种依赖关系是否确实存在并且可供我们下载?
【解决方案2】:

当您编写单元测试时,您会尝试测试(在这种情况下)该方法是否完成了它应该做的事情。您应该查看实现并据此编写测试。相反,您应该考虑该方法应该能够处理哪些输入,以及调用该方法后的结果(返回值和/或副作用)应该是什么。

然后您应该编写一个或多个测试,使用有效和无效输入调用该方法,并让测试确认结果与您认为会发生的情况相符。

这是一个简短且不完整的描述,请在 Wikipediajunit.org 阅读更多内容。

这是JUnit in Eclipse 的旧(2005 年)但工作指南。

【讨论】:

  • 谢谢。我正在使用 Cobertura 工具来计算行和分支覆盖率,行号是未测试代码的指示。此外,该方法的私有访问器/可见性是错误的,现在已更改为公共。有人可以给我这段代码的示例测试用例吗?将不胜感激。谢谢
  • 你的意思是一些可以填写细节的骨架代码吗?因为,正如我所说,如果您不知道应该测试什么行为,就无法编写测试。
  • 我强烈反对。您描述了黑盒测试,而单元测试是白盒测试——您确实看到要测试的代码,因为它(理想情况下)是您自己的代码!您应该考虑该方法应该履行什么合同,并编写测试用例来验证这一点,这是另一回事。
  • 让我补充一点:如果测试并没有真正测试您的代码,请不要编写测试只是来增加您的覆盖率。那确实是错误的测试覆盖率。
  • @Péter Török:如果您自己编写测试,那当然是正确的。但我很确定我们在 SO 没有编写上面的代码,因此我们只能编写测试来确认代码是否正常工作,如果我们没有得到它的用途的迹象。并考虑以单元测试为核心的测试驱动开发。当你写测试时,你甚至没有代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多