I am trying to use a Blocking Variable to verify an asynchonous interaction after a collaborator's method fails. This is what I got:
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 * _
}
}
Exception is thrown, but the blocking variable is never set so I'm getting the following error:
BlockingVariable.get() timed out after 10.00 seconds
at spock.util.concurrent.BlockingVariable.get(BlockingVariable.java:113)*
I tried to use a plain Thread.sleep but always failed the interaction of serviceC.save
CodePudding user response:
The thing that breaks you code, is that mock interactions are the first thing that is evaluated in every then
block, even if you list other things first. So for it to work, you need to use a second then
block as explained in the chapter invocation order in the docs. Since, the mocks are evaluated first, the execution doesn't even reach result.get()
that would wait long enough so that the save interaction could be triggered.
This should fix your problem:
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 * _
}
}
You can test the MCVE on the Groovy Web Console