Spock 存根未按预期工作。存根方法返回 null 而不是模拟响应

问题描述 投票:0回答:1

我正在尝试使用 spock 框架为服务类中的特定方法编写单元测试 但无法模拟方法的响应。

服务等级

@Service
@RequiredArgsConstructor
public class ServiceA  {
    private final RepositoryA repositoryA;
    private final ServiceB serviceB;

    public void methodUnderTest(String arg1,String arg2){
         ObjectA objectA = repositoryA.someMethod(arg1, arg2); // here objectA is null

        if (objectA != null) {
           // Do something
        }else{
          // Do something else
        }
    }
}

由于某种原因,我无法存根

repositoryA.someMethod(arg1, arg2) 
。 运行测试时返回 null。 我尝试按照另一个问题中的建议将模拟和存根交互放入 then 块中,但它对我不起作用。

测试课

class ServiceATest extends Specification {
    @Shared ServiceA serviceA;

    @Shared RepositoryA repositoryA =  Mock(RepositoryA)
    @Shared ServiceB serviceB = Mock(ServiceB)


    def "test"() {
        given:
        serviceA = new ServiceA(repositoryA, serviceB);
        
        ObjectA mockResponse = ObjectA.builder()
                .field1("demo")
                .field2("demo")
                .field3(Instant.now().plus(3, ChronoUnit.DAYS))
                .build();

        when:
        serviceA.methodUnderTest(arg1,arg2);

        then:
        1 * repositoryA.someMethod(arg1,arg2) >> mockResponse // this is not working as expected
    }
}

java unit-testing mocking spock
1个回答
0
投票

Spock 手册,章节交互范围

交互始终局限于特定的功能方法。因此,它们不能在静态方法、

setupSpec
方法或
cleanupSpec
方法中声明。同样,模拟对象不应存储在静态或
@Shared
字段中。

你为什么想分享一个模拟?共享状态是糟糕的测试设计,对于昂贵的对象应该尽可能少地使用。模拟很便宜。只需抵制反射即可使一切变得

@Shared
,尤其是嘲笑。这种过早的优化就等于搬起石头砸自己的脚。

作为参考,这里有一个 MCVE 再现了您的情况。你的工作就是提供一个:

package de.scrum_master.stackoverflow.q77973193

import spock.lang.Specification

import java.time.Instant
import java.time.temporal.ChronoUnit

class ServiceATest extends Specification {
  ServiceA serviceA
  RepositoryA repositoryA = Mock(RepositoryA)
  ServiceB serviceB = Mock(ServiceB)

  def "test"() {
    given:
    serviceA = new ServiceA(repositoryA, serviceB)
    String arg1 = "one"
    String arg2 = "two"

    ObjectA mockResponse = ObjectA.builder()
      .field1("demo")
      .field2("demo")
      .field3(Instant.now().plus(3, ChronoUnit.DAYS))
      .build()

    when:
    serviceA.methodUnderTest(arg1, arg2)

    then:
    1 * repositoryA.someMethod(arg1, arg2) >> mockResponse
  }
}
package de.scrum_master.stackoverflow.q77973193

class ServiceB {}
package de.scrum_master.stackoverflow.q77973193

class ServiceA {
  RepositoryA repositoryA
  ServiceB serviceB

  ServiceA(RepositoryA repositoryA, ServiceB serviceB) {
    this.repositoryA = repositoryA
    this.serviceB = serviceB
  }

  void methodUnderTest(String s1, String s2) {
    repositoryA.someMethod(s1, s2)
  }
}
package de.scrum_master.stackoverflow.q77973193

import java.time.Instant

class RepositoryA {
  ObjectA someMethod(String s1, String s2) {
    ObjectA.builder()
      .field1(s1)
      .field2(s2)
      .field3(Instant.now())
      .build()
  }
}
package de.scrum_master.stackoverflow.q77973193

import java.time.Instant

class ObjectA {
  String field1
  String field2
  Instant field3

  static Builder builder() {
    new Builder()
  }

  static class Builder {
    ObjectA instance = new ObjectA()

    Builder field1(String value) {
      instance.field1 = value
      this
    }

    Builder field2(String value) {
      instance.field2 = value
      this
    }

    Builder field3(Instant value) {
      instance.field3 = value
      this
    }

    ObjectA build() {
      instance
    }
  }
}

Groovy Web Console 中尝试一下。

© www.soinside.com 2019 - 2024. All rights reserved.