Verify no exceptions were thrown in Spock - groovy

I am brand new to Spock and perused their online docs. I have a test case where I need to verify that my fixture's interaction with a non-mock collaborator does not produce an exception:
class FizzSpec extends Specification {
def "no exception thrown when we hail buzz"() {
given:
Fizz fixture = new Fizz()
Buzz buzz = new Buzz("YES", true, "Garble barb") // A non-mock!
when:
fixture.hail(buzz)
// TODO: How to verify the hail didn't produce an exception?
// then:
// thrown() == null
}
}
Any ideas as to how I can accomplish this?

Found it.
You can use
noExceptionThrown()
To assert nothing was thrown

Related

StepVerifier::expectError accepts any exception in Spock

I'm testing a class that uses Spring Boot's webflux library, and I've encountered strange behavior with StepVerifier::expectError. Specifically, I can pass any type (even String!) to the method and the test passes. My method under test should respond with an error Mono for this particular test, and the mono should contain a custom exception. My understanding from this SO question is that I have the StepVerifier operating in the correct block. What is going wrong here?
Class under test:
#Service
#RequiredArgsConstructor
public class PaymentsBO {
private final ContractClient contractClient;
public Mono<Void> updatePaymentInfo(Request record) {
return contractClient
.getContract(UUID.fromString(record.getContractUuid()))
.onErrorResume(throwable -> Mono.error(() -> new CustomException(
"Contract Service responded with a non-200 due to "
+ throwable.getCause())))
.flatMap(
// happy-path logic
);
}
}
Unit test:
def "Returns an error if the Contract Service returns a non-200"() {
given:
def testSubject = new PaymentsBO(contractServiceMock)
def contractServiceMock = Mock(ContractClient)
when:
def result = testSubject.updatePaymentInfo(record)
and:
StepVerifier.create(result)
.expectError(String.class)
then:
1 * contractServiceMock.getContract(CONTRACT_UUID) >> Mono.error(new ContractServiceException())
}
In StepVerifier docs we can read that verification must be triggered by calling one ot the verify methods
Trigger the verification of the resulting StepVerifier on its Publisher using either verify() or verify(Duration). (note some of the terminal expectations above have a "verify" prefixed alternative that both declare the expectation and trigger the verification).
https://projectreactor.io/docs/test/release/api/reactor/test/StepVerifier.html
Your code doesn't use a verify method.
Please consider these two cases:
#Test
void without_verify() {
Mono.error(new IllegalArgumentException(""))
.as(StepVerifier::create)
.expectError(NullPointerException.class);
}
#Test
void with_verify() {
Mono.error(new IllegalArgumentException(""))
.as(StepVerifier::create)
.expectError(NullPointerException.class)
.verify();
}
without_verify is passing because no verification has been triggered.
with_verify is failing because verification has been triggered.

Groovy Power Assert Null Check

I have a Groovy class which receives an argument to its constructor and checks it with a Power Assert statement like so:
public MyClass(Map args) {
assert args.firstArg
}
Normally the value of args.firstArg is an instance of another Groovy class within my project. When I use a real instance of this class, the assertion passes. However when I use the Spock Mocking framework to pass in a mocked instance of the method, the assertion fails:
Assertion failed:
assert args.firstArg
| |
| Mock for type 'OtherClass' named 'mockOtherClass'
['firstArg':Mock for type 'OtherClass' named 'mockOtherClass']
However, when I change the code to
assert args.firstArg != null
then my code works.
Up until now, I had naively thought that
assert args.firstArg
was just shorthand for doing a null check.
Based on what I have observed passing in mocked class instances this is not the case, and I was wondering if someone can help me to understand what the difference is.
#hayfreed: Actually, I do not like to speculate, so please next time (or this time, in case my guess is wrong) provide more than just a set of incoherent code snippets when asking questions on StackOverflow. Ideally, provide an MCVE (please read!).
Let us create an MCVE first, shall we?
package de.scrum_master.stackoverflow.q62543765
class OtherClass {}
package de.scrum_master.stackoverflow.q62543765
class MyClass {
MyClass(Map args) {
assert args.firstArg
}
}
package de.scrum_master.stackoverflow.q62543765
import spock.lang.Specification
class MyClassTest extends Specification {
def "do not use mock"() {
when:
new MyClass([firstArg: new OtherClass(), secondArg: "foo"])
then:
noExceptionThrown()
when:
new MyClass([firstArg: null, secondArg: "foo"])
then:
thrown AssertionError
when:
new MyClass([secondArg: "foo"])
then:
thrown AssertionError
}
def "use mock"() {
when:
new MyClass([firstArg: Mock(OtherClass), secondArg: "foo"])
then:
noExceptionThrown()
}
}
This test passes. But what if we change OtherClass to be a collection type instead?
package de.scrum_master.stackoverflow.q62543765
class OtherClass extends ArrayList<String> {}
The test fails in the feature method not using the mock:
Expected no exception to be thrown, but got 'org.codehaus.groovy.runtime.powerassert.PowerAssertionError'
at spock.lang.Specification.noExceptionThrown(Specification.java:118)
at de.scrum_master.stackoverflow.q62543765.MyClassTest.do not use mock(MyClassTest.groovy:10)
Caused by: Assertion failed:
assert args.firstArg
| |
| []
['firstArg':[], 'secondArg':'foo']
at de.scrum_master.stackoverflow.q62543765.MyClass.<init>(MyClass.groovy:5)
at de.scrum_master.stackoverflow.q62543765.MyClassTest.do not use mock(MyClassTest.groovy:8)
So you see that Groovy truth is more than a null check. E.g. collection types yield false for assertions also if the collection is empty.
But back to your problem. How could the test for the mock fail? My best guess, not having seen the code for your OtherClass, is that the class implements an asBoolean method, as also described in the manual section about Groovy truth:
package de.scrum_master.stackoverflow.q62543765
class OtherClass {
boolean asBoolean(){
true
}
}
Now the test failure looks much like yours:
Expected no exception to be thrown, but got 'org.codehaus.groovy.runtime.powerassert.PowerAssertionError'
at spock.lang.Specification.noExceptionThrown(Specification.java:118)
at de.scrum_master.stackoverflow.q62543765.MyClassTest.use mock(MyClassTest.groovy:28)
Caused by: Assertion failed:
assert args.firstArg
| |
| Mock for type 'OtherClass'
['firstArg':Mock for type 'OtherClass', 'secondArg':'foo']
at de.scrum_master.stackoverflow.q62543765.MyClass.<init>(MyClass.groovy:5)
at de.scrum_master.stackoverflow.q62543765.MyClassTest.use mock(MyClassTest.groovy:26)
How would you fix that? Just stub the asBoolean method to return true:
def "use mock"() {
given:
def otherClass = Mock(OtherClass) {
asBoolean() >> true
}
when:
new MyClass([firstArg: otherClass, secondArg: "foo"])
then:
noExceptionThrown()
}

Stubbed method should return value depending on given mock parameter in Spock

I would like to have different returning results - depending on the given mocked parameter of a method. Please consider the following code snippet to follow my intention
class ExampleSpec extends Specification {
def "should return second value of list of return values"() {
given:
Person personBob = Mock()
Person personJackson = Mock()
PersonHelper stubbedPerson = Stub()
stubbedPerson.getNameOfBrother(personBob) >> "Billy Bob";
stubbedPerson.getNameOfBrother(personJackson) >> "Tommy Jackson";
when:
String actual = stubbedPerson.getNameOfBrother(personBob)
String actual2 = stubbedPerson.getNameOfBrother(personJackson)
then:
actual == "Billy Bob" // true
actual2 == "Tommy Jackson" // false "Billy Bob"
}
}
The test fails because the second call for var actual2 still return Billy Bob rather than Tommy Jackson. I know there is a way to return different values by call order but I would like to make it dependend on given mocks.
Using normal values - no Mock/Stub Proxies - as parameter values does actually work. I assume that the Spock engine can not differ between two mocks. But I am unsure about this, because the proxies do have IDs as instance fields.
For the record - stubbing with mock objects works. I've added simple Person and PersonHelper classes to your example and the test passes:
import spock.lang.Specification
class ExampleSpec extends Specification {
def "should return second value of list of return values"() {
given:
Person personBob = Mock()
Person personJackson = Mock()
PersonHelper stubbedPerson = Stub()
stubbedPerson.getNameOfBrother(personBob) >> "Billy Bob";
stubbedPerson.getNameOfBrother(personJackson) >> "Tommy Jackson";
when:
String actual = stubbedPerson.getNameOfBrother(personBob)
String actual2 = stubbedPerson.getNameOfBrother(personJackson)
then:
actual == "Billy Bob" // true
actual2 == "Tommy Jackson" // false "Billy Bob"
}
static class Person {
String name
}
static class PersonHelper {
String getNameOfBrother(Person person) {
return null
}
}
}
I have checked it with spock-core:1.1-groovy-2.4, spock-core:1.0-groovy-2.4 and even spock-core:0.7-groovy-2.0. All worked.
But what is even more important - such test does not make any sense. You don't test your code at all. You only test if mocking framework mocks correctly. This test could make some sense if you use Spock mocks in your production code, but this is not a valid assumption.
What may go wrong?
Think for a second about this test. According to your when: block you are trying to test if PersonHelper.getNameOfBrother(Person person) returns a valid name of a brother for two different objects. Now let's assume that this is the only test of a PersonHelper class in your project. Imagine what happens if suddenly implementation of getNameOfBrother method starts throwing NullPointerException for some random reason. Ask yourself - does your unit test protects you from such situation? Nope. Your test always passes, because you are stubbing the method you are testing. If you test a real implementation instead and you pass a real Person object then you will get notified about NullPointerException in the test. Otherwise you will see it when you deploy your code and some user's action calls this method and it fails.

Defining Spock mock behaviors

I am writing my first Spock test and read the docs on mocking interactions, but am still not seeing the "forest through the trees" on a few items.
I have a class, MyRealm, that performs authentication for my app. It has two dependencies, AuthService and ShiroAdapter. The former I'd like to mock and the latter I want to leave as-is (if at all possible). This is because the AuthService actually makes a backend connection to LDAP, so I want to mock it. But the ShiroAdapter just defines several utility methods that convert my objects into Apache Shiro security objects (principals, permissions, etc.). So it can be left un-mocked (methinks).
class MyRealmSpec extends Specification {
MyRealm realm
def setup() {
AuthService authService = Mock(AuthService)
// configure 'authService' mock <-- ?????
ShiroAdapter shiroAdapter = new ShiroAdapter()
realm = new MyRealm(authService: authService,
shiroAdapter: shiroAdapter)
}
def "authenticate throws ShiroException whenever auth fails"() {
when:
realm.authenticate('invalid_username', 'invalid_password')
then:
Throwable throwable = thrown()
ShiroException.isAssignableFrom(throwable)
}
}
I believe I'm very close, but am struggling to configure the mock to behave the way I want it to for the test. The Spock docs (linked above) only seem to document how to verify the number of times a mock method is called. I'm not interested in that here.
Here, MyRealm#authenticate(String,String) calls AuthService#doAuth(String,String) under the hood. So I need my mock AuthService instance to simply either return false (indicating failed auth) or to throw an ServiceFaulException if something unexpected happened.
Any ideas how I can accomplish this?
You are very close, an easy, short-hand way to check a thrown exception type is to put the Exception class in parenthesis. Ex:
def "authenticate throws ShiroException whenever auth fails"() {
when:
realm.authenticate('invalid_username', 'invalid_password')
then:
thrown(ShiroException)
}
You also need to mock the LDAP service call itself and simulate an exception or a failed login. The mock operations go in the then clause of your test.
def "authenticate throws ShiroException whenever auth fails"() {
setup:
String invalidUserName = 'invalid_username'
String invalidPassword = 'invalid_password'
when:
realm.authenticate(invalidUserName, invalidPassword)
then:
1 * authService.doAuth(invalidUserName, invalidPassword) >> returnClosure
thrown(ShiroException)
where:
returnClosure << [{throw new ShiroException()}, { false }]
}
Note that you will need to have the arguments on the mock statements match or use wild card matching.
To match on any String you can use the underscore syntax:
1 * authService.doAuth(_, _) >> false
There are a few different behavior objects you might be interested in.
Stub - You only define what gets returned
MyObject obj = Stub{method >> null}
Mocks - You define what gets returned and/or how many times a method is called
MyObject obj = Mock {1..3 methodCall >> false}
Spies - It creates your object but you can override specific methods as a mock (and your overrides can still make calls to the original code)
MyObject obj = Spy {methodCall >> false}
obj.otherMethodCall() // Calls code like normal
obj.methodCall() // Returns false like we told it to
It sounds like you need a stub, but you could use a mock without any issue. I mention spy, because it's a life-saver if your object ever is self dependent (in the future).
def "authenticate throws ShiroException whenever auth fails"() {
given:
AuthService authService = Stub(AuthService)
authService.doAuth(_,_) >> expectedError
MyRealm realm = new MyRealm(
authService: authService,
shiroAdapter: new ShiroAdapter())
when:
realm.authenticate("just enough to get", "to the doAuth method")
then:
thrown(ShiroException)
where:
expectedError << [ShiroException, /*other exceptions this method has to test*/]
}
The data/logic separation isn't needed, but it's a good approach for making tests more flexible and maintainable. Although in this case it's not really needed since you only have one exception to throw.
I would actually separate your failed authentication and exception-ed authentication tests. They're looking at fundamentally different behaviors and the test logic to test both situations is somewhat different. In the interest of maintainability/flexibility, it's in your interest to avoid testing too much (or too little) with each test.
def "authenticate throws ShiroException whenever auth fails"() {
given:
AuthService authService = Stub(AuthService)
authService.doAuth(_,_) >> { args ->
return args[0] == good && args[1] == good
}
MyRealm realm = new MyRealm(
authService: authService,
shiroAdapter: new ShiroAdapter())
expect:
realm.authenticate(username, password) == expectedAuthentication
where:
userName | password | expectedAuthentication
bad | good | false
bad | bad | false
good | good | true
}
Note on the above test, this tests...
The mock's computation for a return value (testing the test)
Any code that happens between calling authenticate and doAuth()
Hopefully that's what you intend. If there's nothing in .authenticate()'s logic that could break (it has complexity on par with a getter or setter method), this test is mostly a waste of time. The only way that logic could break is if something went wrong in the JVM (which is completely outside the responsibility of this test), or someone made a change sometime in the future (ok, even under the huge assumption that .authenticate() contains unbreakably basic logic the test has some value). My rambling off-topic point (terribly sorry); make sure to keep the what & why of your tests in mind. It will help you prioritize test cases and while working out the best ways to organize/separate test logic.

Groovy JsonBuilder call methods to add information

The following code works fine
def json = new JsonBuilder()
json {
writeNumbers(delegate, "myNumbers")
}
println json.toPrettyString()
def writeNumbers(json, name) {
json."$name" {
"w" 32
"h" 32
}
}
But as soon as I move or add a writeNumbers call inside of another 'scope', I get a stackoverflow exception; just like so
def json = new JsonBuilder()
json {
scopes {
writeNumbers(delegate, "myNumbers")
}
}
println json.toPrettyString()
def writeNumbers(json, name) {
json."$name" {
"w" 32
"h" 32
}
}
Result:
Caught: java.lang.StackOverflowError
java.lang.StackOverflowError
Why is this happening and how can I get around it?
Thanks
I think this is caused by the underlying error that the method writeNumbers is unknown when building the chain of closures.
You need to change:
writeNumbers(delegate, "myNumbers")
to
this.writeNumbers(delegate, "myNumbers")
And it will work... Interesting though, this feels like a bug... I'll investigate if I get some free time ;-)
Edit: Found this previous question which shows the same thing

Resources