如何为Parser编写单元测试用例?

时间:2009-01-13 15:51:01

标签: unit-testing tdd testcase test-suite

我正在编写一个解析器,为每个命令生成32位操作码。例如,对于以下语句:

set lcl_var = 2

我的解析器生成以下操作码:

// load immdshort 2 (loads the value 2)
0x10000010
// strlocal lclvar (lcl_var is converted to an index to identify the var)
0x01000002

请注意lcl_var可以是任何内容,即可以给出任何变量。如何为此编写单元测试用例?我们可以避免对值进行硬编码吗?有没有办法让它变得通用?

7 个答案:

答案 0 :(得分:2)

这取决于您如何构建解析器。单元测试测试单个UNIT。

因此,如果要将整个解析器作为一个单元进行测试,可以给它一个命令列表并验证它是否产生正确的操作码(您在编写测试时手动检查)。您可以为每个命令编写测试,并测试正常使用情况,边缘情况使用情况,以及超出边缘情况的使用情况。例如,测试:

设置lcl_var = 2

结果:

0x10000010 0x01000002

0,-1,MAX_INT-1,MAX_INT + 1,...

相同

您知道这些值的正确结果。同样适用于不同的变量。

答案 1 :(得分:1)

int[] opcodes = Parser.GetOpcodes("set lcl_var = 2");
Assert.AreEqual(2, opcodes.Length);
Assert.AreEqual(0x10000010, opcodes[0]);
Assert.AreEqual(0x01000002, opcodes[1]);

答案 2 :(得分:1)

如果您的问题是“如何在不为每个输入 - 输出组合编写一个xUnit测试的情况下,使用不同的输入和期望值运行相同的测试?”

然后答案就是使用类似RowTest NUnit扩展名的东西。我最近在我的博客上写了quick bootup post。 这方面的一个例子是

[TestFixture]
    public class TestExpression
    {
        [RowTest]
        [Row(" 2 + 3 ", "2 3 +")]
        [Row(" 2 + (30 + 50 ) ", "2 30 50 + +")]
        [Row("  ( (10+20) + 30 ) * 20-8/4 ", "10 20 + 30 + 20 * 8 4 / -")]
        [Row("0-12000-(16*4)-20", "0 12000 - 16 4 * - 20 -")]
        public void TestConvertInfixToPostfix(string sInfixExpr, string sExpectedPostfixExpr)
        {
            Expression converter = new Expression();
            List<object> postfixExpr = converter.ConvertInfixToPostfix(sInfixExpr);

            StringBuilder sb = new StringBuilder();
            foreach(object term in postfixExpr)
            {
                sb.AppendFormat("{0} ", term.ToString());
            }
            Assert.AreEqual(sExpectedPostfixExpr, sb.ToString().Trim());
        }

答案 3 :(得分:0)

你没有指定你正在编写解析器的语言,所以为了论证你将使用面向对象的语言。

如果是这种情况,那么依赖注入可以帮助你在这里。如果发出的操作码的目的地是类的实例(例如File),请尝试为您的发射器类提供一个构造函数,该构造函数将该类型的对象用作发出代码的目标。然后,从单元测试中,您可以传入一个模拟对象,该对象是目标类的子类的实例,捕获特定语句的发出操作码,并断言它们是正确的。

如果您的目标类不易扩展,您可能希望创建一个基于它的接口,目标类和模拟类都可以实现。

答案 4 :(得分:0)

据我了解,您首先要为您的特定示例编写一个测试,即解析器的输入是:

set lcl_var = 2

,输出为:

0x10000010 // load immdshort 2
0x01000002 // strlocal lclvar 

当您实现了生产代码以通过该测试并重构它时,如果您不满意它可以处理任何局部变量,使用不同的局部变量编写另一个测试并查看它是否通过。例如带输入的新测试:

set lcl_var2 = 2

编写新测试以期望您想要的不同输出。继续这样做,直到您对您的生产代码足够强大感到满意为止。

答案 5 :(得分:0)

目前尚不清楚您是否正在寻找用于测试的方法或特定技术。

就方法论而言,您可能不希望进行大量的单元测试。也许更好的方法是用域特定语言编写一些程序,然后执行操作码以产生结果。然后测试程序将检查此结果。这样你可以运用一堆代码,但最后只检查一个结果。从简单的问题开始,清除明显的错误,转移到更难的错误。而不是每次都检查生成的操作码。

另一种方法是使用特定于域的语言自动生成程序以及预期的操作码。这可以非常简单,比如编写一个生成一组程序的perl脚本,如:

设置lcl_var = 2

设置lcl_var = 3

一旦您拥有一套具有正确输出的语言测试程序,您就可以反过来生成检查每个操作码的单元测试。由于您已经拥有了操作码,因此需要检查解析器的输出是否正确;审查其代码。

虽然我没有使用cppunit,但我使用的内部工具非常像cppunit。使用cppunit很容易实现单元测试。

答案 6 :(得分:0)

你想测试什么?您想知道是否创建了正确的“存储”指令吗?是否拾取了正确的变量?下定决心你想知道的,测试将是显而易见的。只要你不知道自己想要达到什么目的,就不会知道如何测试未知数。

与此同时,只需编写一个简单的测试。明天或晚些时候,你会再次来到这个地方,因为事情已经破裂。那时,您将更多地了解您想要做什么,并且设计测试可能更简单。

今天,不要试图成为明天的人。