【问题标题】:if local variables cant be tested then what other ways can the variable values be checked [duplicate]如果无法测试局部变量,那么还有什么其他方法可以检查变量值[重复]
【发布时间】:2017-12-01 12:33:05
【问题描述】:

这将是一个很长的问题,由想知道某些东西是如何与传统方法相悖的渴望驱动的。

我遇到了一个非常有趣的应用程序codeacademy,它实际上是在一个 main 方法中测试局部变量。 这是页面的屏幕截图,这让我开始思考这怎么可能。 在stackoverflow中发现了一些类似的问题。

Is it possible to obtain variables in main method from junit test?

How to use BCEL or ASM to get the value of a local declared variable in JAVA?

我不满意知道它不能完成,我想知道的是,有没有一种方法,比如 java 编译器 api 或其他一些方法,可以让我知道这样的应用程序是如何制作的可能的。

【问题讨论】:

  • 局部变量的值在测试中无所谓,因为一旦方法退出它们就不存在了。您应该只关心在方法生命周期之外可观察到的事物,例如返回值、异常、成员变量、依赖项上的方法调用。话虽如此,您在调试时可能会关心它们的值;这就是调试器的用途。
  • 请避免使用屏幕截图。您可以将代码作为 text 放入您的问题中;您也可以简单地将指令引用为代码。图片不适用于屏幕阅读器 - 您的问题中没有任何内容需要使用图片!
  • 可能是使用调试界面。例如在调试时Eclipse,您还可以看到局部变量。并不意味着我会支持对方法内部进行单元测试的想法——一点也不!
  • 那么在codeacademy网站上会发生什么,如果你做myNumber = 21 + 21;,它会接受吗? myNumber = (myNumber = 21) << 1; 怎么样?还有myNumber = 21; myNumber <<= 1;? ...它们都将myNumber 设置为42,但如果课程检查是基于字符串的,它肯定不会涵盖所有更棘手的变体。
  • 然后,它解析文本而不是编译后的代码,因为myNumber = 21 + 21 编译为与myNumber = 42 完全相同的字节码。

标签: java unit-testing junit java-bytecode-asm bcel


【解决方案1】:

在实现验证(如单元测试或 QA 自动化测试)中测试局部变量通常是不好的做法。

局部变量取决于特定的实现,并且特定的实现应该隐藏在合理抽象的 API 后面,以允许开发人员在未来更换实现 - 如果他们有更好的想法 - 而不会影响结果的消费者(只要,因为 API 非常好,所以不需要任何更改)。

对于一些非常复杂的实现/算法,开发人员确实可能有兴趣验证复杂算法的特定中间/内部结果,以简化实现本身的开发。在这一点上,创建提供合理抽象的中间结果的内部内部 API 是有意义的,即使它们被硬绑定到特定算法,并在单元测试中测试该内部 API。但在算法替换的情况下,您必须接受内部 API 的更改以及所有内部单元测试。应该还有不受内部变化影响的合理的高层抽象API。

需要在局部变量级别进行测试应该表明代码库存在一些更深层次的问题。


您的 Java 教程的特定用例与真正的 Java 代码开发完全无关,因为该讲座的目的完全不同。

正如简单的myNumber = 21 + 21; 测试所示,讲座的验证纯粹是基于比较文本的,可能对学生输入的源代码使用了一些正则表达式。甚至不检查生成的字节码,因为该字节码与myNumber = 42; 相同。

如果您正在开发一些讲座系统,使用某种自定义虚拟机和调试界面可能会起作用,但如果是简单的讲座,甚至文本比较解决方案可能就足够了。

通常当学生足够先进以解决某些任务时,您可以开始使用来自/到 stdin/stdout 的输入/输出来创建自动化测试,以使用一组已知的输入/输出测试来验证学生解决方案,例如一些网站,例如https://www.hackerrank.com/do,或者各种编程竞赛。此时您不需要访问任何东西,也不需要局部变量,也不需要 API 单元测试,您只需将标准输入重定向到具有所需输入的馈送解决方案,并捕获标准输出以将其与设计输出进行比较。


但讲座验证与单元测试完全无关。

而对预期结果过于严格的测试甚至可能在讲课过程中适得其反!

假设你要求学生编写代码,输出从 1 到 N 的平方和,而你只会接受:

    int sum = 0;
    for (int i = 1; i <= N; ++i) {
        sum += i * i;
    }

(在字节码级别验证,变量名称和 ++ 后缀/前缀无关紧要)

现在有些学生会尝试提交这个:

    int sum = 0, square = 0, sq_delta = -1;
    for (int i = 1; i <= N; ++i) {
        sum += (square += (sq_delta += 2));
    }

它会失败......(即使他的解决方案在 1985 年左右绝对优于实际乘法的变体)......悲伤的故事。 :)

【讨论】:

    【解决方案2】:

    我们可能想多了。

    当我收到您的问题时,您想知道这里使用的在线工具如何知道某些main() 方法中的变量的内容

    问题是:这不一定是 Java 功能。

    记住:这是他们的网络应用程序。他们可以做任何他们在那里实施的事情。换句话说:当然,您可以使用 Java 编译器生成 any 段 Java 代码的 AST(抽象语法树)表示。当然,该 AST 包含相应源代码中存在的所有信息。

    【讨论】:

    • 对于这个简单的代码,甚至可以使用启用调试信息的传统 Java 编译器(javacecj)进行编译,并按照代码流预测变量的最终值。它不包含静态代码分析无法预测的任何内容。当然,这也是这段代码不值得进行单元测试的原因。单元测试开始,这种可预测性结束……
    • 我不完全同意。单元测试有几个功能:A)在进行 TDD 时,它们从您的需求的可运行规范开始 B)它们应该帮助发现错误/回归 C)大多数代码应该是 可预测的 ...或者做你编写的代码你不知道它会做什么?从这个意义上说:在您确切知道被测代码在做什么的情况下编写单元测试很有意义。但是您想要对知识进行自动化测试。 知道它将做什么完全没有帮助。
    • 也许你误会了我。在这个特定示例中,可以预测变量的值,而无需传入或假设任何运行时输入。将始终评估为相同结果的代码可以替换为常量结果,并且常量毫无疑问始终评估为指定值。对于任何其他重要代码,使用假设输入对其进行分析以预测其结果与执行代码没有什么不同。
    猜你喜欢
    • 2018-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-18
    • 2018-11-04
    • 2013-01-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多