测试使用System.console()的Groovy类

时间:2013-12-26 18:50:08

标签: groovy spock

我有一个groovy脚本,使用readline方法通过java.io.console对象向用户提出一些问题。另外,我用它来询问密码(用于设置HTTPS)。我如何使用Spock对这段代码进行单元测试?目前它抱怨该对象是Java最终对象,无法进行测试。显然,我不是第一个尝试这个的人,所以我想我会问。

代码草图如下所示:

class MyClass {

   def cons

   MyClass() {
     cons = System.console()
   }

   def getInput = { prompt, defValue ->
     def input = (cons.readLine(prompt).trim()?:defValue)?.toString()
     def inputTest = input?.toLowerCase()
     input
   }
}

我希望单元测试能够测试是否可以返回一些模拟响应,并且可以返回默认值。注意:这是简化的,所以我可以弄清楚如何进行单元测试,getInput方法中还有更多的代码需要进行测试,但是一旦我清除了这个障碍应该没问题。

akhikhl编辑的每回答 根据这个建议,我做了一个简单的界面:

interface TestConsole {
    String readLine(String fmt, Object ... args)
    String readLine()
    char[] readPassword(String fmt, Object ... args)
    char[] readPassword()
}

然后我尝试了这样的测试:

def "Verify get input method mocking works"() {

    def consoleMock = GroovyMock(TestConsole)
    1 * consoleMock.readLine(_) >> 'validResponse'

    inputMethods = new MyClass()
    inputMethods.cons = consoleMock

    when:
    def testResult = inputMethods.getInput('testPrompt', 'testDefaultValue')
    then:
    testResult == 'validResponse'
}

我选择不改变构造函数,因为我不想改变我的实际代码只是为了测试它。幸运的是,Groovy让我用一个'def'定义控制台,所以我做的工作正常。

问题是以上不起作用!!!我无法抗拒 - 这不是逻辑! Spock在某个地方的GroovyMockMetaClass中获得了“迷失”。如果我在代码中更改了一行,而在测试中更改了一行就可以了。

代码更改:

From:
    def input = (cons.readLine(prompt).trim()?:defValue)?.toString()
To: (add the null param)
    def input = (cons.readLine(prompt, null).trim()?:defValue)?.toString()

测试更改:

From:
    1 * consoleMock.readLine(_) >> 'validResponse'
To: (again, add a null param)
    1 * consoleMock.readLine(_, null) >> 'validResponse'

然后测试终于起作用了。这是Spock中的一个错误,还是我只是在左外野?我不介意需要做任何可能需要的测试工具,但必须修改代码才能使这项工作真的非常糟糕。

1 个答案:

答案 0 :(得分:3)

你是对的:因为Console类是final,所以无法扩展。因此,解决方案应该朝另一个方向发展:

  1. 创建新类MockConsole,不是从Console继承而是使用相同的方法。

  2. 以这种方式更改MyClass的构造函数:

    MyClass(cons = null){   this.cons = cons?:System.console() }

  3. 在spock测试中实例化MockConsole并将其传递给MyClass构造函数。

  4. <强>更新-201312272156

    我玩了一点spock。模拟“readLine(String fmt,Object ... args)”的问题似乎特定于varargs(或者最后arg是一个列表,这与groovy相同)。我设法将问题减少到以下情况:

    定义界面:

    interface IConsole {
      String readLine(String fmt, Object ... args)
    }
    

    定义测试:

    class TestInputMethods extends Specification {
    
      def 'test console input'() {
        setup:
        def consoleMock = GroovyMock(IConsole)
        1 * consoleMock.readLine(_) >> 'validResponse'
    
        when:
        // here we get exception "wrong number of arguments":
        def testResult = consoleMock.readLine('testPrompt')
    
        then:
        testResult == 'validResponse'
      }
    }
    

    此测试变体失败,异常“参数数量错误”。特别是,spock认为readLine接受2个参数而忽略了第二个参数是vararg的事实。证明:如果我们从IConsole.readLine中删除“Object ... args”,则测试成功完成。

    这是解决方法(希望是暂时的)问题:将readLine的调用更改为:

    def testResult = consoleMock.readLine('testPrompt', [] as Object[])
    

    然后测试成功完成。

    我也尝试过针对spock 1.0-groovy-2.0-SNAPSHOT的相同代码 - 问题是一样的。

    <强>更新-201312280030

    varargs的问题解决了!非常感谢@charlesg,他在Spock: mock a method with varargs

    回答了我的相关问题

    解决方案如下:用Mock替换GroovyMock,然后正确解释varargs。