【问题标题】:How to mock static method of 3rd party api call using spock?如何使用 spock 模拟 3rd 方 api 调用的静态方法?
【发布时间】:2021-01-05 10:44:28
【问题描述】:

我正在使用 micronaut 框架和 spock 来编写 API 测试用例。 我正在尝试创建我的 API 的测试用例,它在内部调用第三方 api 的 PaymentIntent.retrieve() 静态方法。 我想模拟这个第 3 方 url 调用并返回一个 PaymentIntent 的 fakeObject。

这是我创建的一个示例测试用例,它正在执行实际的 3rd 方 api 静态方法:

@Inject Service myService;

@Unroll
void "method returns nothing"() {
  given:
  PaymentIntent paymentIntent = new PaymentIntent()
  Mock(PaymentIntent)
  PaymentIntent.retrieve("pi_123", requestOptions) >> paymentIntent
  
  when:
  def result = myService.getPayment("", "pi_123", obj)

  then:
  result.amount == paymentIntent.amount
}

有人可以指导我如何限制实际 API 的执行吗?

我已经参考了这些已经提出的问题,但在我的情况下它不起作用。 Mock static method with GroovyMock or similar in Spock

【问题讨论】:

  • 欢迎来到 SO。请注意了解MCVE 是什么以及它如何帮助您获得所需的答案。一个测试 sn-p,甚至不是一个完整的课程,再加上你没有提供被测课程的事实,这使得你很难回答你的问题,这就是为什么 Jeff 在他的回答中不得不推测。这个假设你的测试类是用 Groovy 编写的,这可能是对的,也可能是错的,这取决于他的答案是否对你有用。

标签: unit-testing groovy stripe-payments spock micronaut


【解决方案1】:

解决方案将取决于调用PaymentIntent.retrieve 的类是用什么语言编写的。以下假设是Groovy,因为问题是用groovy 标记的。

import com.stripe.exception.StripeException
import com.stripe.model.PaymentIntent
import com.stripe.net.RequestOptions

import javax.inject.Singleton

@Singleton
class MyService {
    PaymentIntent getPayment(String s1, String s2, RequestOptions options) throws StripeException {
        PaymentIntent.retrieve s1, options
    }
}
import com.stripe.model.PaymentIntent
import io.micronaut.test.extensions.spock.annotation.MicronautTest
import spock.lang.Specification

import javax.inject.Inject

@MicronautTest
class MyServiceSpec extends Specification {
    @Inject
    MyService myService;

    void "method returns nothing"() {
        when:
        PaymentIntent paymentIntent = new PaymentIntent()
        paymentIntent.amount = 42
        GroovySpy(PaymentIntent, global: true)
        1 * PaymentIntent.retrieve("pi_123", _) >> paymentIntent

        def result = myService.getPayment("pi_123", null, null)

        then:
        result.amount == 42
    }
}

【讨论】:

    【解决方案2】:

    模拟静态的东西并不是一件小事。我建议将第 3 方调用提取到一个单独的方法中,该方法可以在单元测试中被覆盖。

    假设您的 Service.getPayment() 看起来像

      class MyService {
        PaymentIntent getPayment(String s1, String s2, RequestOptions options) throws StripeException {
            // some logic here
            // ...
    
            PaymentIntent.retrieve s1, options
        }
      }
    

    让我们重构它

      class MyService {
        PaymentIntent getPayment(String s1, String s2, RequestOptions options) throws StripeException {
            // some logic here
            // ...
    
            doRetrieveCall s1, options
        }
    
        PaymentIntent doRetrieveCall(String s1, RequestOptions options) throws StripeException {
            PaymentIntent.retrieve s1, options
        }
      }
    

    你的单元测试

      given:
        def myService = new Service() {
          // overriden
          PaymentIntent doRetrieveCall(String s1, RequestOptions options) throws StripeException {
            // todo validate of the s1 for "pi_123"
            // todo validate of the options
    
            // return a fake instance
            new PaymentIntent()
          }
        }
      
      when:
        def result = myService.getPayment("", "pi_123", obj)
    
      then:
        result.amount == paymentIntent.amount
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-08-20
      • 1970-01-01
      • 1970-01-01
      • 2017-10-08
      • 1970-01-01
      • 2016-09-13
      • 2016-03-04
      相关资源
      最近更新 更多