如何模拟或测试readPassword?

时间:2016-03-06 21:56:07

标签: java scala unit-testing mocking mockito

我正在开发一个简化控制台应用程序创建的框架。我的框架是用Scala编写的,我使用ScalaTest和Mockito进行单元测试。

我需要能够模仿java.io.Console,但它已被宣布为最终版。我试图实现100%的单元测试覆盖率目前这是阻止我的唯一因素 - 功能和单元测试。

到目前为止,我还没有能够解决任何问题,我只是无法想到这样做的方法。它没有实现我可以嘲笑的界面,这个方法在其他任何地方都不可用,显然我无法扩展它。我想也许有一个解决方案可能涉及调用readLinereadPassword等方法的某种动态方法,但我没有足够的经验来到任何地方还有那种思路!

1 个答案:

答案 0 :(得分:1)

您应该创建自己的界面来包装与java.io.Console的所有互动,例如

public interface ConsoleService {
    ...
}

只要您只通过ConsoleService的实例与控制台进行交互,您就可以像往常一样模拟ConsoleService并测试99%的代码。 ConsoleService接口成为应用程序的边界,用于整个应用程序的功能测试和直接与之交互的类的单元测试。

现在我们已将问题的范围缩小为“我如何测试ConsoleService实施”,我们需要获得一些创意。例如,您可以将控制台输出重定向到文件并检查文件的内容。您可能甚至不想测试Scala中的ConsoleService;您可以使用ConsoleService编写骨架应用程序,然后使用您选择的脚本语言在您喜欢的操作系统上启动真实的控制台,与您的骨架应用程序交互并以这种方式测试ConsoleService。你可以在这里获得创意(和hacky)因为:

  1. 它只影响少数测试;和
  2. 您的应用程序可能已经成熟到ConsoleService实现不需要进行太多改变的程度,即您的古怪测试解决方案对未来的开发人员来说不会是一个很大的负担。
  3. 由于这些原因,显而易见的是保持ConsoleService包装非常薄是个好主意,因为其中的任何逻辑都将通过奇怪的ConsoleService进行测试测试,不是很好的友好Scala测试。通常直接委派给java.io.Console方法已经足够了,但是您应该允许应用程序的功能测试驱逐ConsoleService接口而不是做出任何假设(您的功能测试断言可能依赖于与模拟ConsoleService,或者可能是存根的状态,测试ConsoleService的实现,你可以在测试中控制它。)

    最后,您可能认为ConsoleService包装器所以很薄,其实现确实需要任何单元/功能测试。 ConsoleService的实施可能对您的应用程序至关重要,因为任何缺陷都将通过集成测试或UAT中的应用程序的手动检查来公开。

    你最终会得到类似的东西(道歉,我不会说Scala所以它是Java):

    public class RealConsoleService implements ConsoleService {
        private final java.io.Console delegate;
        public RealConsoleService(java.io.Console delegate) {
            this.delegate = delegate;
        }
    
        @Override
        public String readLine() throws IOError {
            return delegate.readLine();
        }
    }
    

    两个有趣的观点:

    • 这是测试驱动开发有助于编写灵活代码的一个很好的例子。如果您想使用另一种输入和输出方法重写框架,您只需将ConsoleService重命名为更抽象的ApplicationInputOutputService并插入不同的实现。
    • 可以使用相同的概念来测试使用其他难以测试的API的应用程序。 Java的许多有用的文件IO方法都是静态方法,因此在测试中难以控制。通过在上面的界面中打包,您的应用程序功能变得易于测试。