Reputation: 109
I am learning Spock so this may be very basic.
public Random genRand() {
try {
return SecureRandom.getInstanceStrong();
}
catch (NoSuchAlgorithmException e) {
logger.debug(e.getMessage());
return new SecureRandom();
}
}
What I have tried so far is:
public void setup() {
mockClassName = spy(ClassName)
mockClassName.logger() >> mockLogger
}
def "exception test case"() {
given: "nothing"
when:"method call happens"
mockClassName.genRand()
then:"handle"
SecureRandom.getInstanceStrong()
}
This covers the try block only.
While trying:
public void setup() {
mockClassName = spy(ClassName)
mockClassName.logger() >> mockLogger
}
def "exception test case"() {
given: "nothing"
SecureRandom.getInstanceStrong() >> Exception
when:"method call happens"
mockClassName.genRand()
then:"catch"
NoSuchAlgorithmException e = thrown()
new SecureRandom()
}
This gives the error, expected exception of type java.security.NoSuchAlgorithmException
, but no exception was thrown.
Is it possible to do both try and catch in one test case? Feel free to make two.
Upvotes: 0
Views: 804
Reputation: 67387
Quoting my own comment:
Why don't you want to change the application code in order to make it more testable? You could factor out the
SecureRandom.getInstanceStrong()
call into an extra method, calling it fromgenRand()
, handling the exception there like before. Then you could stub that method in a spy for the error case, throwing an exception instead of returning a result. That would give you full coverage of all execution paths without the need to mock static methods of JDK classes.
I mean something like this:
package de.scrum_master.stackoverflow.q74797317;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Random;
public class RandomProvider {
public Random genRand() {
try {
return getRandStrong();
}
catch (NoSuchAlgorithmException e) {
System.err.println(e.getMessage());
return new SecureRandom();
}
}
protected SecureRandom getRandStrong() throws NoSuchAlgorithmException {
return SecureRandom.getInstanceStrong();
}
}
package de.scrum_master.stackoverflow.q74797317
import spock.lang.Specification
import java.security.NoSuchAlgorithmException
class RandomProviderTest extends Specification {
def "happy path"() {
given:
RandomProvider randomProvider = new RandomProvider()
expect:
randomProvider.genRand()
}
def "exception test case"() {
given:
RandomProvider randomProvider = Spy() {
getRandStrong() >> { throw new NoSuchAlgorithmException("oops") }
}
expect:
randomProvider.genRand()
}
}
Try it in the Groovy Web Console.
In my IDE, the coverage view looks like this:
Upvotes: 0
Reputation: 4811
You should not try to test several branches of your code in one test, so doing it in two tests is actually the right approach. If you try to find a simple description of your test and have to describe several kinds of results, then that's a hint that your approach is not good.
A reminder, you should name your test methods with "It...", for example:
"It returns a default SecureRandom, if no strong instance of SecureRandom can be found"
Another point: You code never leaks a NoSuchAlgorithmException
into Spec. This exception is caught in your production code, and then you retunr a valid SecureRandom
. You can only use thrown()
if your code actually throws an exception, meaning this is a hard expectation. Your test will fail if the exception is not thrown.
Upvotes: 1