java - 有没有更好的方法来添加多个模拟交互?

标签 java groovy spock

我正在寻找一种更好的方法来在一个类中添加同一方法的多个交互。

给定一个对象列表,我想根据这些对象执行过滤器,并且我想为每个剩余对象添加 Spock 交互

我最好的工作实现(使用带有 for 循环的 Java8 流来添加交互):

SomeClassA classA = Mock() {

   def listOfDesiredObjects = listOfObjects
      .stream()
      .filter({i -> i != someObject})
      .map({i -> new DesiredObject(i)})
      .collect(Collectors.toList())

   for (int i = 0; i < listOfDesiredObjects.size(); i++) {
      methodIWantToMockMultipleTimes(_ as Type0, listOfDesiredObjects.get(i) as Type1) >> {
         return someMockedObject
      }
   }

   methodIWantToMockMultipleTimes(_ as Type0, someObject as Type1) >> {
      return someDifferentMockedObject
   }
}

我已经尝试了以下方法,但它们要么无法编译,要么就是乱七八糟(在我看来):

以下将返回一个 Groovyc: Interaction is missing a target 错误:

SomeClassA classA = Mock() {

   def listOfDesiredObjects = listOfObjects
      .stream()
      .filter({i -> i != someObject})
      .map({i -> new DesiredObject(i)})
      .forEach({i -> methodIWantToMockMultipleTimes(_ as Type0, listOfDesiredObjects.get(i) as Type1) >> {
         return someMockedObject
      }})

   methodIWantToMockMultipleTimes(_ as Type0, someObject as Type1) >> {
      return someDifferentMockedObject
   }
}

这很糟糕:

SomeClassA classA = Mock() {

   methodIWantToMockMultipleTimes(_ as Type0, listOfDesiredObjects.get(0) as Type1) >> {
      return someMockedObject
   }

   methodIWantToMockMultipleTimes(_ as Type0, listOfDesiredObjects.get(1) as Type1) >> {
      return someMockedObject
   }

   // {n} more interactions

   methodIWantToMockMultipleTimes(_ as Type0, someObject as Type1) >> {
      return someDifferentMockedObject
   }
}

供将来引用(我最终如何利用参数匹配器):

def classToMock = Mock() {
   methodToMock(_ as Type0, _ as Type1, _ as Type2) >> { Type0 a, Type1 objectToCompare, Type2 c ->
      listOfObjects
         .stream()
         .map({i -> someHelperMethod(i)})
         .filter({i -> i == objectToCompare})
         .map({i -> desiredObject })
         .findFirst()
         .orElse({i -> otherObject})
   }
}

最佳答案

用户 chrylis 是对的,如果您使用参数匹配,解决方案实际上非常简单。我根据您的(伪)代码(包括虚拟类)重新创建了您的情况,以便向您展示简化方法的不同方法:

以下是备选方案的关键部分:

  def "simplified test with two distinct cases"() {
    given:
    def someObject = new DesiredObject("C")
    SomeClassA classA = Mock() {
      methodIWantToMockMultipleTimes(_, !someObject) >> someMockedObject
      methodIWantToMockMultipleTimes(_, someObject) >> someDifferentMockedObject
    }
    // (...)
  }

  def "simplified test with special and default case"() {
    given:
    def someObject = new DesiredObject("C")
    SomeClassA classA = Mock() {
      // Attention, this only works if the special case is defined before the default one
      methodIWantToMockMultipleTimes(_, someObject) >> someDifferentMockedObject
      methodIWantToMockMultipleTimes(*_) >> someMockedObject
    }
    // (...)
  }

  def "simplified test with dynamic stub method"() {
    given:
    def someObject = new DesiredObject("C")
    SomeClassA classA = Mock() {
      methodIWantToMockMultipleTimes(*_) >> { a, b -> b == someObject ? someDifferentMockedObject : someMockedObject }
    }
    // (...)
  }

这是完整的代码(只需复制、粘贴并运行):

package de.scrum_master.stackoverflow.q57210075

import spock.lang.Specification

import java.util.stream.Collectors

class ConditionalMockCreationTest extends Specification {

  class Type0 {}

  class DesiredObject {
    String name

    DesiredObject(String name) {
      this.name = name
    }

    @Override
    String toString() {
      "DesiredObject('$name')"
    }

    boolean equals(o) {
      if (this.is(o)) return true
      if (getClass() != o.class) return false
      DesiredObject that = (DesiredObject) o
      if (name != that.name) return false
      return true
    }

    int hashCode() {
      return (name != null ? name.hashCode() : 0)
    }
  }

  class SomeClassA {
    DesiredObject methodIWantToMockMultipleTimes(Type0 type0, DesiredObject desiredObject) {
      return new DesiredObject("default")
    }
  }

  def someMockedObject = Mock(DesiredObject) {
    toString() >> "some mocked object"
  }
  def someDifferentMockedObject = Mock(DesiredObject) {
    toString() >> "some different mocked object"
  }

  def "original test"() {
    given:
    def listOfObjects = ["A", "B", "C", "D", "E"]
    def someObject = "C"
    SomeClassA classA = Mock() {
      def listOfDesiredObjects = listOfObjects
        .stream()
        .filter({ i -> i != someObject })
        .map({ i -> new DesiredObject(i) })
        .collect(Collectors.toList())

      for (int i = 0; i < listOfDesiredObjects.size(); i++) {
        methodIWantToMockMultipleTimes(_ as Type0, listOfDesiredObjects.get(i) as DesiredObject) >> {
          return someMockedObject
        }
      }

      methodIWantToMockMultipleTimes(_ as Type0, new DesiredObject(someObject)) >> {
        return someDifferentMockedObject
      }
    }

    expect: "normal object yields normal result"
    new SomeClassA().methodIWantToMockMultipleTimes(new Type0(), new DesiredObject("A")).toString() == "DesiredObject('default')"

    and: "mocked objects yield predefined mock behaviour"
    classA.methodIWantToMockMultipleTimes(new Type0(), new DesiredObject("A")).toString() == "some mocked object"
    classA.methodIWantToMockMultipleTimes(new Type0(), new DesiredObject("B")).toString() == "some mocked object"
    classA.methodIWantToMockMultipleTimes(new Type0(), new DesiredObject("C")).toString() == "some different mocked object"
    classA.methodIWantToMockMultipleTimes(new Type0(), new DesiredObject("D")).toString() == "some mocked object"
    classA.methodIWantToMockMultipleTimes(new Type0(), new DesiredObject("E")).toString() == "some mocked object"
    // Undefined case -> no stubbed method -> mock returns null
    classA.methodIWantToMockMultipleTimes(new Type0(), new DesiredObject("XXX")) == null
  }

  def "simplified test with two distinct cases"() {
    given:
    def someObject = new DesiredObject("C")
    SomeClassA classA = Mock() {
      methodIWantToMockMultipleTimes(_, !someObject) >> someMockedObject
      methodIWantToMockMultipleTimes(_, someObject) >> someDifferentMockedObject
    }

    expect: "normal object yields normal result"
    new SomeClassA().methodIWantToMockMultipleTimes(new Type0(), new DesiredObject("A")).toString() == "DesiredObject('default')"

    and: "mocked objects yield predefined mock behaviour"
    classA.methodIWantToMockMultipleTimes(new Type0(), new DesiredObject("A")).toString() == "some mocked object"
    classA.methodIWantToMockMultipleTimes(new Type0(), new DesiredObject("B")).toString() == "some mocked object"
    classA.methodIWantToMockMultipleTimes(new Type0(), new DesiredObject("C")).toString() == "some different mocked object"
    classA.methodIWantToMockMultipleTimes(new Type0(), new DesiredObject("D")).toString() == "some mocked object"
    classA.methodIWantToMockMultipleTimes(new Type0(), new DesiredObject("E")).toString() == "some mocked object"
    classA.methodIWantToMockMultipleTimes(new Type0(), new DesiredObject("XXX")).toString() == "some mocked object"
  }

  def "simplified test with special and default case"() {
    given:
    def someObject = new DesiredObject("C")
    SomeClassA classA = Mock() {
      // Attention, this only works if the special case is defined before the default one
      methodIWantToMockMultipleTimes(_, someObject) >> someDifferentMockedObject
      methodIWantToMockMultipleTimes(*_) >> someMockedObject
    }

    expect: "normal object yields normal result"
    new SomeClassA().methodIWantToMockMultipleTimes(new Type0(), new DesiredObject("A")).toString() == "DesiredObject('default')"

    and: "mocked objects yield predefined mock behaviour"
    classA.methodIWantToMockMultipleTimes(new Type0(), new DesiredObject("A")).toString() == "some mocked object"
    classA.methodIWantToMockMultipleTimes(new Type0(), new DesiredObject("B")).toString() == "some mocked object"
    classA.methodIWantToMockMultipleTimes(new Type0(), new DesiredObject("C")).toString() == "some different mocked object"
    classA.methodIWantToMockMultipleTimes(new Type0(), new DesiredObject("D")).toString() == "some mocked object"
    classA.methodIWantToMockMultipleTimes(new Type0(), new DesiredObject("E")).toString() == "some mocked object"
    classA.methodIWantToMockMultipleTimes(new Type0(), new DesiredObject("XXX")).toString() == "some mocked object"
  }

  def "simplified test with dynamic stub method"() {
    given:
    def someObject = new DesiredObject("C")
    SomeClassA classA = Mock() {
      methodIWantToMockMultipleTimes(*_) >> { a, b -> b == someObject ? someDifferentMockedObject : someMockedObject }
    }

    expect: "normal object yields normal result"
    new SomeClassA().methodIWantToMockMultipleTimes(new Type0(), new DesiredObject("A")).toString() == "DesiredObject('default')"

    and: "mocked objects yield predefined mock behaviour"
    classA.methodIWantToMockMultipleTimes(new Type0(), new DesiredObject("A")).toString() == "some mocked object"
    classA.methodIWantToMockMultipleTimes(new Type0(), new DesiredObject("B")).toString() == "some mocked object"
    classA.methodIWantToMockMultipleTimes(new Type0(), new DesiredObject("C")).toString() == "some different mocked object"
    classA.methodIWantToMockMultipleTimes(new Type0(), new DesiredObject("D")).toString() == "some mocked object"
    classA.methodIWantToMockMultipleTimes(new Type0(), new DesiredObject("E")).toString() == "some mocked object"
    classA.methodIWantToMockMultipleTimes(new Type0(), new DesiredObject("XXX")).toString() == "some mocked object"
  }
}

关于java - 有没有更好的方法来添加多个模拟交互?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57210075/

相关文章:

junit - 选择要在 gradle 中运行的特定测试

Java:观察同一类的许多对象

java - JBoss缓存 : setting uniqueid for proper JMX monitoring

java - 如何在 java 中使用 List<object> 与 Integer 和 Float ?

grails - 强制 @GrailsCompileStatic 检查请求为 AbstractMultipartHttpServletRequest

groovy - fork 的groovyc返回错误代码:-1073741819

grails - Groovy中此方法的返回类型是什么?

grails - getAll() 在单元测试中不起作用

java - Nexus 7 与 Eclipse 的问题

groovy - 如何获取 Geb 模块实例及其声明的类?