抛出异常后验证 Spock 模拟异步交互

Verify Spock mock asynchonous interaction after an exception is thrown

我正在尝试使用阻塞变量在协作者的方法失败后验证异步交互。 这是我得到的:

public class ServiceAImpl {
    private final ServiceB serviceB;
    private final ServiceC serviceC;

    @Override
    public void createAccount(String userId) {
        try {
            serviceB.createUserAccount(userId);
        } catch (Exception e) {
            asyncSaveCreationError(e);
            throw e;
        }
    }

    private void asyncSaveCreationError(Exception e) {
        runAsync(() -> saveCreationError(e));
    }

    private void saveCreationError(Exception e) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        e.printStackTrace(pw);

        serviceC.save(pw.toString());
    }
}

class ServiceAImplTest extends Specification {
    def serviceB = Mock(ServiceB)
    def serviceC = Mock(ServiceC)

    @Subject
    def subject = new ServiceAImpl(serviceB, serviceC)

    def "createAccount - should throw Exception and save error"() {
        given:
        def result = new BlockingVariable<Boolean>(10)

        when:
        subject.createAccount("userId")

        then:
        1 * serviceB.createUserAccount(_) >> { throw new Exception() }
        thrown(Exception)
        1 * serviceC.save(_) >> { result.set(true) }
        result.get()
        0 * _
    }
}

抛出异常,但从未设置阻塞变量,因此出现以下错误:

BlockingVariable.get() timed out after 10.00 seconds
    at spock.util.concurrent.BlockingVariable.get(BlockingVariable.java:113)*

我尝试使用普通的 Thread.sleep 但总是无法与 serviceC.save

交互

破坏你代码的是,模拟交互是每个 then 块中首先评估的东西,即使你首先列出其他东西。因此,要使其正常工作,您需要使用第二个 then 块,如文档中 invocation order 章中所述。由于首先对模拟进行评估,因此执行甚至没有达到 result.get() 会等待足够长的时间以触发保存交互。

这应该可以解决您的问题:

class ServiceAImplTest extends Specification {

    ServiceB serviceB = Mock()
    ServiceC serviceC = Mock()

    @Subject
    def subject = new ServiceAImpl(serviceB, serviceC)

    def "createAccount - should throw Exception and save error"() {
        given:
        def result = new BlockingVariable<Boolean>(10.0)
        
        when:
        subject.createAccount("userId")
        

        then: 'first catch the exception and wait for the result'
        thrown(Exception)
        result.get()
        
        then: 'verify mock interactions'
        1 * serviceB.createUserAccount(_) >> { throw new Exception() }
        1 * serviceC.save(_) >> { result.set(true) }
        0 * _
    }
}

您可以在 Groovy Web Console

上测试 MCVE