Spock Unroll似乎用boolean参数打印出一些奇怪的东西 [英] Spock Unroll seems to print something odd with boolean parameter

查看:120
本文介绍了Spock Unroll似乎用boolean参数打印出一些奇怪的东西的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我只是将这种测试方法放在一起:

I just put this test method together:

@Unroll
def 'super start edit should be called if cell is not empty'( boolean empty ){
    given:
    DueDateEditor editor = GroovySpy( DueDateEditor ){
        isEmpty() >> empty
    }

    when:
    editor.startEdit()

    then:
    if( empty){
        0 * editor.callSuperStartEdit()
    }
    else {
        1 * editor.callSuperStartEdit()
    }

    where:
    empty | _
    true  | _
    false | _
}

...就通过的两个测试而言,它工作正常……但是,当它失败时,这很奇怪:如果参数emptyfalse的输出为

... it works OK in terms of the two tests passing... but when you make it fail it's very odd: the output if the parameter empty is false is

super start edit should be called if cell is not empty[1]

...,如果参数emptytrue,则为0.这是一个错误吗?

... and it is 0 if the parameter empty is true. Is this a bug?

推荐答案

我正在写一个附加答案,因为

I am writing an additional answer because

  • Tim关于标题的解决方案中有一个小错误(但从技术上来说,他的回答仍然是绝对正确的!),
  • 这里不需要GroovySpy,一个简单的Spy就足够了,
  • 我想向您展示另一种测试方法,而无需添加isEmpty()
  • 我想向您展示如何使用三元表达式中的调用次数进行一次交互,而不是if-else(即使错误报告很丑陋),
  • 我想评论一下您的总体测试方式(请参阅本文结尾).
  • there is a small bug in Tim's solutions with regard to the title (but still his answer is technically absolutely correct!),
  • you don't need a GroovySpy here, a simple Spy absolutely suffices,
  • I want to show you an alternative way of testing without stubbing isEmpty(),
  • I want to show you how you can use just one interaction with the number of calls in a ternary expression instead of an if-else (even though error reporting is ugly then),
  • I want to comment on your way of testing in general (see the end of this post).
package de.scrum_master.stackoverflow.q61032514;

import java.time.LocalDate;

public class DueDateEditor {
  String text;

  public boolean isEmpty() {
    return text == null || text.trim() == "";
  }

  public void startEdit() {
    if (!isEmpty())
      callSuperStartEdit();
  }

  public void callSuperStartEdit() {}
}

package de.scrum_master.stackoverflow.q61032514

import spock.lang.Specification
import spock.lang.Unroll

class DueDateEditorTest extends Specification {
  @Unroll
  def 'super start edit #shouldMsg be called if the cell is #cellStateMsg'() {
    given:
    DueDateEditor editor = Spy() {
      isEmpty() >> empty
    }

    when:
    editor.startEdit()

    then:
    (empty ? 0 : 1) * editor.callSuperStartEdit()

    where:
    empty << [true, false]
    shouldMsg = empty ? 'should not' : 'should'
    cellStateMsg = empty ? 'empty' : 'not empty'
  }

  @Unroll
  def "super start edit #shouldMsg be called if cell text is '#text'"() {
    given:
    DueDateEditor editor = Spy()
    editor.text = text

    when:
    editor.startEdit()

    then:
    (editor.isEmpty() ? 0 : 1) * editor.callSuperStartEdit()
    // Or, if 'isEmpty()' has a side effect:
    // (text ? 1 : 0) * editor.callSuperStartEdit()

    where:
    text << ["foo", "", null, "line 1\nline 2"]
    shouldMsg = text ? 'should' : 'should not'
    cellStateMsg = text ? 'not empty' : 'empty'
  }
}

一般说明:

  • 我不会通过交互来测试单个类的内部布线.该测试将很脆弱,如果您在内部重构该类而根本不更改API,则如果交互不再如预期那样,则测试可能会中断.我认为这过于规范,我只会对不同类之间或一个类的不同实例之间的关键交互使用交互测试,关键"意味着诸如Observer之类的设计模式的功能.
  • 如果整个测试仅确切知道这两种情况,则可以通过两种不同的交互模式来区分两种情况,这会使测试的可读性和复杂性降低,请参阅您自己的代码以及我的代码和Tim的代码.在这种情况下,我宁愿编写两个具有简单标题和简单功能但没有if-else或三元表达式,没有标题等辅助变量的特征方法.

P.S .:对不起,我必须组成一个正在测试DueDateEditor的示例类,以使我的测试能够按预期进行编译和运行.像往常一样,不幸的是,迈克没有提供 MCVE ,而只是提供了一部分.

P.S.: Sorry, I had to make up a sample class under test DueDateEditor in order to make my test compile and run as expected. As usual, Mike unfortunately didn't provide an MCVE but just a part of it.

更新:我们在评论中谈到了GroovySpy,正如我所说,如果您的类是Java类,并且您想存根一个最终方法,那将是行不通的,请参见 Spock手册.这是给您的证明:

Update: We talked about GroovySpy in our comments and, as I said, it will not work if your classes are Java classes and there is a final method in you want to stub, see the Spock manual. Here is proof for you:

package de.scrum_master.stackoverflow.q61032514;

public class TreeTableCell<A, B> {
  String text;

  public final boolean isEmpty() {
    return text == null || text.trim() == "";
  }
}

package de.scrum_master.stackoverflow.q61032514;

import java.time.LocalDate;

public class DueDateEditor extends TreeTableCell<String, LocalDate> {
  public void startEdit() {
    if (!isEmpty())
      callSuperStartEdit();
  }

  public void callSuperStartEdit() {}
}

package de.scrum_master.stackoverflow.q61032514

import spock.lang.Specification
import spock.lang.Unroll

class DueDateEditorTest extends Specification {
  @Unroll
  def 'super start edit #shouldMsg be called if the cell is #cellStateMsg'() {
    given:
    DueDateEditor editor = GroovySpy() {
      isEmpty() >> empty
    }

    when:
    editor.startEdit()

    then:
    (empty ? 0 : 1) * editor.callSuperStartEdit()

    where:
    empty << [true, false]
    shouldMsg = empty ? 'should not' : 'should'
    cellStateMsg = empty ? 'empty' : 'not empty'
  }
}

如果您的应用程序类仅是Groovy类,则该测试将起作用.但是,如果它们像我的示例一样是Java类,则测试将失败,如下所示:

The test would work if your application classes were Groovy classes only. But if they are Java classes like in my example, the test will fail like this:

Too few invocations for:

(empty ? 0 : 1) * editor.callSuperStartEdit()   (0 invocations)

Unmatched invocations (ordered by similarity):

1 * editor.startEdit()
methodName == "callSuperStartEdit"
|          |
startEdit  false
           10 differences (44% similarity)
           (s---------)tartEdit
           (callSuperS)tartEdit

因此,在这种情况下,您不能仅使用Groovy魔术来检查交互.但是正如我所说,您无论如何都不应该这样做.而是要确保startEdit()callSuperStartEdit()都做正确的事情.检查其结果,或者如果为void,请检查其对被测对象或其合作者的状态的副作用.

So in this case you cannot just use Groovy magic to check interactions. But as I said, you shouldn't do that anyway. Rather make sure that both startEdit() and callSuperStartEdit() do the right things. Check their results or, if they are void, check their side effects on the state of the subject under test or its collaborators.

更新2:关于您最初关于索引方法命名的问题,实际上 @tim_yates 给出了正确的答案.我只想添加相应的Spock手册链接,说明方法展开,如何使用where:块中的变量影响命名.

Update 2: Regarding your original question about indexed method naming, actually @tim_yates gave the correct answer. I just want to add the corresponding Spock manual link explaining method unrolling and how you can influence naming using variables from the where: block.

这篇关于Spock Unroll似乎用boolean参数打印出一些奇怪的东西的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆