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

llew8vvj  于 2023-03-11  发布在  Java
关注(0)|答案(1)|浏览(155)

Spock测试工作时遇到问题。我想知道如何计算两个不同模拟交互的次数。
我知道交互需要模拟。但是在我的例子中,我有两个模拟要处理。请看下面的示例代码。
我想确保当AccountService.findUser抛出异常时,ReportService.makeReport被调用一次。

@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(*_) >> _
  }
}
mspsb9vt

mspsb9vt1#

下面是一组可复制的示例类,它们在现有代码中做了一些修改,以便使伪代码示例能够首先编译和运行。顺便说一句,为了回答这个问题,我用显式的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()
    1 * helper.reportService.reportRequest(*_)
  }
}

请注意:

  • 如果您希望findUser方法正常运行,则AccountService不应是模拟的。请使用正常示例,或者如果您需要验证其上的交互,请使用Spy(此处不需要)。但是,如果您模拟它,则findUser将不执行任何操作,尤其是不调用restTemplate.postForEntity。***更新:*有关可以模拟此类的另一个解决方案,请参阅下文。
  • 它是“给予,当,然后”,而不是“给予,当,给予”。
  • thrown CustomInvalidExceptionA没有任何意义,因为该异常将在SomeHelper.request中捕获。
  • 在你的Spock规范中还有一些其他不正确的细节,我不打算详细说明。

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

**更新:**如果您想模拟AccountService,假设稍后实际使用Report report变量(如源代码注解所示),则一个替代方案(可能比以前更好)将是:

一个12b1x一个13b1x
你可以像这样测试它,模拟除了被测类之外的所有东西:

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()
    1 * helper.reportService.reportRequest(*_) >> _
  }
}

请注意,在本例中,最后一行末尾的>> _对于使reportRequest(*_)方法返回一个非null存根响应而不是null的模拟默认值是非常必要的。简单地使用Stub(ReportService)的想法在这里是行不通的,因为在存根上您无法验证诸如1 *之类的交互。也就是说,使用mock并使其返回null以外的内容是可行的方法。
Groovy Web Console中尝试一下。

相关问题