7

我正在编写一个解析器,它为每个命令生成 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可以是任何东西,即可以给定任何变量。我该如何为此编写单元测试用例?我们可以避免对值进行硬编码吗?有没有办法让它通用?

4

7 回答 7

2

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

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

设置 lcl_var = 2

结果是:

0x10000010 0x01000002

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

您知道这些值的正确结果。不同的变量也是如此。

于 2009-01-14T10:19:36.730 回答
1
int[] opcodes = Parser.GetOpcodes("set lcl_var = 2");
Assert.AreEqual(2, opcodes.Length);
Assert.AreEqual(0x10000010, opcodes[0]);
Assert.AreEqual(0x01000002, opcodes[1]);
于 2009-01-14T15:25:58.220 回答
1

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

那么答案就是使用类似 RowTest NUnit 扩展的东西。我最近在我的博客上写了一篇快速启动帖子。这方面的一个例子是

[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());
        }
于 2009-02-04T09:20:28.263 回答
0

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

set lcl_var = 2

输出是:

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

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

set lcl_var2 = 2

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

于 2009-01-14T10:18:22.287 回答
0

您没有指定要使用哪种语言编写解析器,因此为了论证的目的,我将假设您使用的是面向对象的语言。

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

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

于 2009-01-13T15:59:05.063 回答
0

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

同时,只需编写一个简单的测试。明天或晚些时候,你会再次来到这个地方,因为有些东西坏了。那个时候,你会更了解你想做什么,设计一个测试可能会更简单。

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

于 2009-02-24T14:21:32.323 回答
0

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

就方法论而言,您可能不想进行广泛的单元测试。也许更好的方法是用您的领域特定语言编写一些程序,然后执行操作码以产生结果。然后测试程序会检查这个结果。这样你可以练习一堆代码,但最后只检查一个结果。从简单的开始清除明显的错误并转向更难的错误。而不是每次都检查生成的操作码。

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

设置 lcl_var = 2

设置 lcl_var = 3

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

虽然我没有使用过 cppunit,但我使用了一个非常类似于 cppunit 的内部工具。使用 cppunit 很容易实现单元测试。

于 2009-02-06T16:52:54.603 回答