在已经测试的组件时测试复合对象 - >多余?

时间:2012-03-04 13:36:08

标签: java unit-testing tdd

我用一个具体的例子说明了一个普遍的问题。 当所有组件对象都已经过测试时,您建议测试复合对象多少?

作为具体示例,请考虑下面的NullTerminatedStringReader。它从bytebuffer读取一个以空字符结尾的字符串。为此,它使用Javas Charset解码器。

我当然想测试我的NullTerminatedStringReader。它应该能够读取各种字符串,如UTF8,UTF16,ASCII等。

想象一下,我编写了CharsetDecoder并测试它会解码各种可能的字符集中的字符。它真的经过了很好的测试和尝试,我毫不怀疑它的工作原理。现在我编写一个使用CharsetDecoder的NullTerminatedStringReader。理论上,我希望NullTerminatedStringReader能够处理CharsetDecoder可以解码的所有字符串字符串。如果我使用TDD,我会想要驱动我的设计,所以我会编写很多测试来测试NullTerminatedStringReader的每个字符集解码:

...
void testNullTerminatedStringReaderCanDecodeUTF8String()
void testNullTerminatedStringReaderCanDecodeASCIIString()
...

但这似乎是多余的,因为在CharsetDecoder测试中我有所有这些测试:

...
void testCharsetDecoderCanDecodeUTF8Char()
void testCharsetDecoderCanDecodeASCIIChar()
...

我不知道该怎么做,因为没有NullTerminatedStringReader的测试,我如何驱动它的设计来支持所有这些解码?我是否在NullTerminatedStringReader的错误级别上进行测试?如果我没有进行测试,它似乎也缺少了一些东西,因为理论上我知道NullTerminatedStringReader正在使用CharsetDecoder,我知道它所做的只是附加字符来形成一个字符串,所以这里没有太多可能出错的地方。如果CharsetDecoder工作,那么应该是NullTerminatedStringReader。但是如果它没有使用CharsetDecoder会怎么样 - 我认为更清楚的是假设不知道NullTerminatedStringReader的实现然后编写测试,但是这会导致看起来像多余的测试.. dillema?你敢打赌。

class NullTerminatedStringReader
{
    private Charset charset;

    public String read(ByteBuffer buffer)
    {
        StringBuilder sb = new StringBuilder();
        while (true)
        {
            char charVal = readChar(buffer, charset.newDecoder()); // unicode char, possibly span several bytes
            if (charVal == '\0')
                break;
            sb.append(charVal);
        }

        return sb.toString();
    }

    private char readChar(ByteBuffer buffer, CharsetDecoder decoder) {...}
}

2 个答案:

答案 0 :(得分:1)

测试复合对象时,我通常只测试两件事:

  • 它是否支持为其设计的复合端到端用例?
  • 复合对象添加的任何增量功能是否有效?

一般情况下,我不会尝试全面测试子对象,因为他们应该有自己的测试用例来做到这一点。即你应该假设你自己的子对象工作,就像你假设java.util.ArrayList工作一样。

因此,在你的NullTerminatedStringReader示例中,我可能只测试它是否与一个或两个备用字符集一起工作(即证明它正在调用正确的CharsetDecoder)并相信CharsetDecoder已经过充分测试,可以全部工作其他的charsets。

答案 1 :(得分:1)

我认为您可以将此情况视为某种集成测试:为了应对复杂性,您正在模块化。为此,你

  • 需要知道您重复使用的组件的 contract (您在readChar中使用的CharsetDecoder的前后条件),以便您的NullTerminatedStringReader可以使用其他一些履行合同的组件。但是您不需要知道您正在使用CharsetDecoder实现。
  • 无需重新测试您重复使用的组件的功能:只要满足前提条件,您就可以简单地假设后置条件。
  • 使用TDD的这些前提条件来驱动您的NullTerminatedStringReader设计,但您可以假设后置条件成立。并使用TDD的后置条件来推动您的某些实现的设计,例如CharsetDecoder,如果你仍然需要创建它。但为此,您可以假设将满足前提条件。