如何计算 Spock 与两个不同模拟的交互?

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

我无法让 Spock 测试正常工作。我想知道如何计算两个不同的模拟交互。

我明白互动需要嘲笑。但是就我而言,我有两个模拟要处理。请参阅下面的示例代码。

我想确保我的

ReportService.makeReport
AccountService.findUser
抛出异常时被调用一次。

@Getter
@Setter
@Service
// others...
public class AccontService {

  @Autowired
  private RestTemplate restTemplate;

  public Account findUer(Stirng acccountId) throws CustomInvalidExceptionA, CustomInvalidExceptionB {
    this.restTemplate.postEntity(/* some params */);
    // other logics
    throw new CustomInvalidExceptionB("Parent Account does not qualify");
  }
}
@Getter
@Setter
@Component
// others...
public class SomeHelper {
  @Autowired
  private InventoryService inventoryService;

  @Autowired
  private AccountService accountService;

  @Autowired
  private ReportSerice reportService;

  public ProcessRequest requst(String accountId, Item item) {
    try {
      Account acct = this.accountService.findUser(accountId);
      this.inventoryService.check(item);
    } catch (CustomInvalidExceptionA | CustomInvalidExceptionB ex) {
      Report report=this.reportService.reportRequest(accountid, item, ex.getClass());
      log.error("ERROR" + ex.getClass());
      //Other logics using report object
    }
  }
}
@Getter
@Setter
@Service
// others...
public class ReportService {
  public Report reportRequest(String accountId, Item item, String message) {
    // some logics to make a report request
    return // Report object
  }
}
class SomeHelperSpec extends Specification {
  SomeHelper helper;
  
  def setUp() {
    this.helper = new SomeHelper()
    this.helper.accountService = Mock(AccountService)    
    this.helper.reportService = Mock(ReportService)
  }  

  def "make sure if the accountService findUser failed, then reportService reportRequest is called once"() {
    given:
    this.helper.accountService.restTemplate = Mock(RestTemplate)
    this.helper.accountService.restTemplate.postForEntity(_ as URI, _, String) >> {
      throw new CustomInvalidExceptionA()
    }

    when:
    this.helper.accountService.findUser("123")

    given:
    thrown CustomInvalidExceptionA
    1 * this.helper.reportService.reportRequest(*_) >> _
  }
}
java spring groovy spock
1个回答
0
投票

这里是一组可重现的示例类,在现有代码中有几个修复,以便首先编译和运行您的伪代码示例。顺便说一句,出于这个答案的目的,我用明确的 setter 和 getter 替换了 Lombok:

package de.scrum_master.stackoverflow.q75679629;

public class Account { }
package de.scrum_master.stackoverflow.q75679629;

public class CustomInvalidExceptionA extends Exception { }
package de.scrum_master.stackoverflow.q75679629;

public class CustomInvalidExceptionB extends Exception {
  public CustomInvalidExceptionB(String message) {
    super(message);
  }
}
package de.scrum_master.stackoverflow.q75679629;

public class InventoryService {
  public void check(Item item) { }
}
package de.scrum_master.stackoverflow.q75679629;

public class Item { }
package de.scrum_master.stackoverflow.q75679629;

public class ProcessRequest { }
package de.scrum_master.stackoverflow.q75679629;

public class Report { }
package de.scrum_master.stackoverflow.q75679629;

import java.net.URI;

public class RestTemplate {
  public void postForEntity(URI uri, Object o, Class<?> clazz) throws CustomInvalidExceptionA { }
}
package de.scrum_master.stackoverflow.q75679629;

public class ReportService {
  public Report reportRequest(String accountId, Item item, Class<? extends Exception> message) {
    // some logics to make a report request
    return new Report();
  }
}
package de.scrum_master.stackoverflow.q75679629;

import java.net.URI;
import java.net.URISyntaxException;

public class AccountService {
  public RestTemplate getRestTemplate() {
    return restTemplate;
  }

  public void setRestTemplate(RestTemplate restTemplate) {
    this.restTemplate = restTemplate;
  }

  private RestTemplate restTemplate;

  public Account findUser(String acccountId) throws CustomInvalidExceptionA, CustomInvalidExceptionB {
    URI uri = null;
    try { uri = new URI("https://scrum-master.de"); }
    catch (URISyntaxException e) { throw new RuntimeException(e); }

    this.restTemplate.postForEntity(uri, "x", Integer.class);
    // other logics
    throw new CustomInvalidExceptionB("Parent Account does not qualify");
  }
}
package de.scrum_master.stackoverflow.q75679629;

public class SomeHelper {
  private InventoryService inventoryService;
  private AccountService accountService;
  private ReportService reportService;

  public ProcessRequest request(String accountId, Item item) {
    try {
      Account acct = this.accountService.findUser(accountId);
      this.inventoryService.check(item);
    }
    catch (CustomInvalidExceptionA | CustomInvalidExceptionB ex) {
      Report report = this.reportService.reportRequest(accountId, item, ex.getClass());
      System.out.println("ERROR: " + ex.getClass());
      //Other logics using report object
    }
    return new ProcessRequest();
  }

  public InventoryService getInventoryService() {
    return inventoryService;
  }

  public void setInventoryService(InventoryService inventoryService) {
    this.inventoryService = inventoryService;
  }

  public AccountService getAccountService() {
    return accountService;
  }

  public void setAccountService(AccountService accountService) {
    this.accountService = accountService;
  }

  public ReportService getReportService() {
    return reportService;
  }

  public void setReportService(ReportService reportService) {
    this.reportService = reportService;
  }
}

现在我们有一些东西要编译和测试,我们可以这样做:

package de.scrum_master.stackoverflow.q75679629

import spock.lang.Specification

class SomeHelperTest extends Specification {
  SomeHelper helper

  def setup() {
    helper = new SomeHelper()
    helper.accountService = new AccountService()
    helper.reportService = Mock(ReportService)
    helper.inventoryService = Mock(InventoryService)
  }

  def "make sure if the accountService findUser failed, then reportService reportRequest is called once"() {
    given:
    helper.accountService.restTemplate = Mock(RestTemplate) {
      postForEntity(*_) >> {
        throw new CustomInvalidExceptionA()
      }
    }

    when:
    helper.request("123", Mock(Item))

    then:
    noExceptionThrown()

    then:
    1 * helper.reportService.reportRequest(*_)
  }
}

请注意:

  • AccountService
    不应该是 mock,如果你希望它的
    findUser
    方法正常运行。要么使用普通实例,要么,如果您需要验证其上的交互,则使用
    Spy
    (此处不需要)。然而,如果你嘲笑它,
    findUser
    将什么都不做,尤其是不调用
    restTemplate.postForEntity
    更新: 请参阅下面的另一个解决方案,您可以在其中模拟此类。

  • 是“given, when, then”,不是“given, when, given”。

  • thrown CustomInvalidExceptionA
    没有任何意义,因为该异常将在
    SomeHelper.request
    中被捕获。

  • 您的 Spock 规范中还有一些其他不正确的细节,我不打算详细说明。

Groovy Web 控制台 中尝试。运行规范时,您首先会看到错误日志打印到控制台。要查看实际测试结果,请单击“结果”选项卡:


更新: 如果您想模拟

AccountService
,假设稍后实际使用
Report report
变量,如源代码注释所暗示的那样,另一种甚至可能比以前更好的解决方案是:

public class Report {
  public void doSomething() { }
}
  public ProcessRequest request(String accountId, Item item) {
    try {
      Account acct = this.accountService.findUser(accountId);
      this.inventoryService.check(item);
    }
    catch (CustomInvalidExceptionA | CustomInvalidExceptionB ex) {
      Report report = this.reportService.reportRequest(accountId, item, ex.getClass());
      System.out.println("ERROR: " + ex.getClass());
      // Other logic using report object
      report.doSomething();
    }
    return new ProcessRequest();
  }

你可以这样测试它,模拟除被测类之外的所有内容:

class SomeHelperTest extends Specification {
  SomeHelper helper

  def setup() {
    helper = new SomeHelper()
    helper.accountService = Mock(AccountService)
    helper.reportService = Mock(ReportService)
    helper.inventoryService = Mock(InventoryService)
  }

  def "make sure if the accountService findUser failed, then reportService reportRequest is called once"() {
    given:
    helper.accountService.findUser(_) >> {
      throw new CustomInvalidExceptionA()
    }

    when:
    helper.request("123", Mock(Item))

    then:
    noExceptionThrown()

    then:
    1 * helper.reportService.reportRequest(*_) >> _
  }
}

请注意,在这种情况下,为了使

 >> _
方法返回非
reportRequest(*_)
存根响应而不是
null
的模拟默认值,最后一行末尾的
null
确实是必不可少的。简单地使用
Stub(ReportService)
的想法在这里行不通,因为在存根上您无法验证诸如
1 * 
之类的交互,即使用模拟并使其返回
null
以外的东西是可行的方法。

Groovy Web 控制台中尝试它。

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