Unable to mock local objects in method using Spock - groovy

I'm unable to mock the below local objects - env, service, creds. These are classes from imported cloud foundry dependencies.
How do I write a test case covering all conditions for below Groovy code using Spock or Junit 4 without refactoring the code?
import io.pivotal.cfenv.core.cfEnv
import io.pivotal.cfenv.core.cfCredentials
import io.pivotal.cfenv.core.cfService
class Test {
public String getPropertyValue() {
CfEnv env = new CfEnv();
CfService service = new CfService();
String propName = "test-name";
try {
service = env.findServiceByName(propName);
} catch (Exception e) {
return null;
}
CfCredentials creds = new CfCredentials();
Map<String, Object> props = service.getMap();
return props.get("prop.name").toString();
}
}

As your code is Groovy, you are able to use Spock's GroovySpy
see the docs
For example:
class ASpec extends Specification {
def "getPropertyValue() return null when env.findServiceByName throws an exception"() {
given:
CfEnv envMock = GroovySpy(global: true)
when:
def result = new Test().getPropertyValue()
then:
result == null
1 * envMock.findServiceByName(_) >> { throw new RuntimeException() }
}

Related

How do I test microprofile RestClientBuilder with mockito?

I'm trying to write up a junit to mock a response from a RestClientBuilder class but what I've had to do seems very convoluted and it's not even working.
In the following code I want the call to the getWorkBean(int) method in IResourceAPI to return a WorkBean object.
My junit method test code so far is:
import org.eclipse.microprofile.rest.client.RestClientBuilder;
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
PowerMockito.mockStatic(RestClientBuilder.class);
RestClientBuilder restClientBuilderNewBuilder = Mockito.mock(RestClientBuilder.class);
PowerMockito.when(RestClientBuilder.newBuilder()).thenReturn(restClientBuilderNewBuilder);
RestClientBuilder restClientBuilderBaseURI = Mockito.mock(RestClientBuilder.class);
Mockito.when(restClientBuilderNewBuilder.baseUri(any(URI.class))).thenReturn(restClientBuilderBaseURI);
IResourceAPI resourceAPI = Mockito.mock(IResourceAPI.class);
Mockito.when(restClientBuilderBaseURI.build(IResourceAPI.class)).thenReturn(resourceAPI);
Response.ResponseBuilder responseBuilder = Response.ok();
Mockito.when(resourceAPI.updateStatus(anyInt(), anyString(), any(WorkBeanAction.class))).thenReturn(responseBuilder.build());
WorkBean workBean = new WorkBean();
Jsonb jsonb = JsonbBuilder.create();
InputStream is = new ByteArrayInputStream(jsonb.toJson(workBean).getBytes());
responseBuilder = Response.ok().entity(is);
Mockito.when(resourceAPI.getWorkBean(anyInt())).thenReturn(responseBuilder.build());
The exception I'm getting back is:
javax.ws.rs.core.Response$ResponseBuilder.status(ILjava/lang/String;)Ljavax/ws/rs/core/Response$ResponseBuilder;
java.lang.AbstractMethodError: javax.ws.rs.core.Response$ResponseBuilder.status(ILjava/lang/String;)Ljavax/ws/rs/core/Response$ResponseBuilder;
After much playing around I have a solution.
private IWebAppResource webAppResource = Mockito.mock(IWebAppResource.class);
#Before
public void before() {
PowerMockito.mockStatic(RestClientBuilder.class);
RestClientBuilder restClientBuilderNewBuilder = Mockito.mock(RestClientBuilder.class);
PowerMockito.when(RestClientBuilder.newBuilder()).thenReturn(restClientBuilderNewBuilder);
RestClientBuilder restClientBuilderBaseURI = Mockito.mock(RestClientBuilder.class);
Mockito.when(restClientBuilderNewBuilder.baseUri(any(URI.class))).thenReturn(restClientBuilderBaseURI);
Mockito.when(restClientBuilderBaseURI.build(IWebAppResource.class)).thenReturn(webAppResource);
}
#Test
public void doSomethingFunky() {
Mockito.when(webAppResource.getResource(Mockito.anyInt())).thenReturn(Response.ok().build);
}

Implicit assert statement in Groovy

Given I have JUnit tests written in Groovy:
class AssertTests {
#Test
void "explicit assert statement"() {
def value = 42
assert value == 100
}
#Test
void "no assert statement"() {
def value = 42
value == 100
}
}
When I execute them,
explicit assert statement test fails as expected thanks to assert statement.
no assert statement test passes and I would expect it to fail in a similar way how it's done when I use http://spockframework.org
How can I achieve implicit assert behavior for tests written in plain Groovy?
The answer is simple - you can't. Spock uses AST transformations to grab the code written in the then: part and transform it into the code that makes an equivalent of assertion (not the exact assert.)
To illustrate this, here is your test written in Spock:
import spock.lang.Specification
class TestSpec extends Specification {
def "should fail"() {
when:
def value = 42
then:
assert value == 100
}
}
And here is what its bytecode decompiled back to Java looks like:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.GroovyObject;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.callsite.CallSite;
import org.spockframework.runtime.ErrorCollector;
import org.spockframework.runtime.SpockRuntime;
import org.spockframework.runtime.ValueRecorder;
import org.spockframework.runtime.model.BlockKind;
import org.spockframework.runtime.model.BlockMetadata;
import org.spockframework.runtime.model.FeatureMetadata;
import org.spockframework.runtime.model.SpecMetadata;
import spock.lang.Specification;
#SpecMetadata(
filename = "TestSpec.groovy",
line = 5
)
public class TestSpec extends Specification implements GroovyObject {
public TestSpec() {
CallSite[] var1 = $getCallSiteArray();
super();
}
#FeatureMetadata(
line = 7,
name = "should fail",
ordinal = 0,
blocks = {#BlockMetadata(
kind = BlockKind.WHEN,
texts = {}
), #BlockMetadata(
kind = BlockKind.THEN,
texts = {}
)},
parameterNames = {}
)
public void $spock_feature_0_0() {
CallSite[] var1 = $getCallSiteArray();
ErrorCollector $spock_errorCollector = (ErrorCollector)ScriptBytecodeAdapter.castToType(var1[0].callConstructor(ErrorCollector.class, false), ErrorCollector.class);
ValueRecorder $spock_valueRecorder = (ValueRecorder)ScriptBytecodeAdapter.castToType(var1[1].callConstructor(ValueRecorder.class), ValueRecorder.class);
Object var10000;
try {
Object value = 42;
try {
SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), "value == 100", Integer.valueOf(12), Integer.valueOf(16), (Object)null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(2)), ScriptBytecodeAdapter.compareEqual($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(0)), value), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(1)), 100))));
var10000 = null;
} catch (Throwable var14) {
SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, "value == 100", Integer.valueOf(12), Integer.valueOf(16), (Object)null, var14);
var10000 = null;
} finally {
;
}
var1[2].call(var1[3].call(this.getSpecificationContext()));
} finally {
$spock_errorCollector.validateCollectedErrors();
var10000 = null;
}
}
}
If you look at the SpockRuntime class, you will find that verifyCondition method checks if the condition found in the then: or and: block evaluates to true:
public static void verifyCondition(#Nullable ErrorCollector errorCollector, #Nullable ValueRecorder recorder,
#Nullable String text, int line, int column, #Nullable Object message, #Nullable Object condition) {
if (!GroovyRuntimeUtil.isTruthy(condition)) {
final ConditionNotSatisfiedError conditionNotSatisfiedError = new ConditionNotSatisfiedError(
new Condition(getValues(recorder), text, TextPosition.create(line, column), messageToString(message), null, null));
errorCollector.collectOrThrow(conditionNotSatisfiedError);
}
}
You can't avoid explicit assert in JUnit tests. You can hide them in some helper method, but you still need to call them. Keep in mind, that JUnit runner requires that the test method returns void, so you can't capture the result of a test method. (Replace void with def, and you will see that JUnit does not run your test.)
If you want to explore the world of compile-time metaprogramming in Groovy, you could experiment with writing your own AST transformations. Maybe there is a way to find boolean expression(s) and inject assert in front of it, but I can't guarantee you that it will work. If you look for an out-of-the-box solution for implicit asserts in Groovy JUnit tests, there is no such one.

How to mock a single method for a class in Spock?

I have a method like below in my class that I'm trying to test:
class SomeHelper {
ByteArrayOutputStream fooBar (Request request) {
ByteArrayOutputStream baos = someParser.parseData(getRequestFileInputStream(request.filename))
return baos
}
InputStream getRequestFileInputStream(String filename) {
//return intputStream of object from S3
}
....
}
In the above, getRequestFileInputStream is a method that takes as parameter a name of the file. It fetches the inputstream of that file from AWS S3. While testing fooBar method from Spock, I would like to provide a mock for the getRequestFileInputStream method because I don't want to use the implementation of this method that is in the class since it goes to another bucket name.
Is it possible to do this?
Below is what I've tried:
class SomeHelperSpec extends Specification{
//this is the implementation of getRequestFileInputStream I want to use while testing
InputStream getObjectFromS3(String objectName) {
def env = System.getenv()
AwsClientBuilder.EndpointConfiguration endpoint = new AwsClientBuilder.EndpointConfiguration(env["endpoint_url"], env["region_name"])
AmazonS3ClientBuilder builder = AmazonS3ClientBuilder.standard()
builder.setEndpointConfiguration(endpoint)
builder.setCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(env["ACCESS_KEY"], env["SECRET_KEY"])))
AmazonS3 s3 = builder.build()
return s3.getObject("testbucket", objectName).getObjectContent()
}
def "test fooBar" () {
given:
someHelper = new SomeHelper()
someHelper.getRequestFileInputStream(_) >> getObjectFromS3(fileName)
someHelper.someParser = Mock(SomeParser) {
....
}
Request requestInstance = new Request()
request.filename = fileName
request.fileType = fileType
expect:
someHelper.fooBar(requestInstance).getText == returnVal
where:
fileType | fileName | returnVal
"PDF" | "somepdf.pdf" | "somereturnval"
}
}
However, the above doesn't work because it is still trying to call the original implementation of getRequestFileInputStream in SomeHelper instead of using the mocked implementation provided in the spec.
You can use a spy
def someHelper = Spy(SomeHelper)
someHelper.getRequestFileInputStream(_) >> getObjectFromS3(fileName)
See Spies.
You can use a real object but with overridden method:
given:
someHelper = new SomeHelper() {
#Override
InputStream getRequestFileInputStream(String filename) {
return getObjectFromS3(fileName)
}
}

Spock - mocking external service

I'm very new in spock framework testing and I didn't find any example where I can find needed information. Therefore, I think the best way is to show an example what I need to have.
e.g. test class in spock:
def "getData" (){ // this is test of getData method from ExternalService
when:
Result result = externalService.getData()
then:
result.msg = 'SUCCESS'
}
Service Class:
public class ExternalService(){
private ServiceConnector serviceConnector;
public Result getData(){
Result result = serviceConnector.callAndGet();
prepareInformation(data);
updateStatuses(data);
return result;
}
}
Class Data as a Domain Class:
public class Data {
private String msg
private int Id
// +getters/setters
}
And now I have getData test and would like to mock the only method callAndGet(). It means every time I call callAndGet I need to have object Data with msg SUCCESS but all other methods from getData method should be called normally.
Is it quite understandable? The question is how can we inject/mock the service class ExternalService into the spock test class?
What you need to do is to mock the ServiceConnector class and pass it via constructor (e.g.). See below:
#Grab('org.spockframework:spock-core:1.0-groovy-2.4')
#Grab('cglib:cglib-nodep:3.1')
import spock.lang.*
class Test extends Specification {
def 'some spec'() {
given:
def serviceConnector = Mock(ServiceConnector) {
callAndGet() >> new Result(msg: 'SUCCESS')
}
def externalService = new ExternalService(serviceConnector)
when:
Result result = externalService.getData()
then:
result.msg == 'SUCCESS'
}
}
public class ExternalService {
private ServiceConnector serviceConnector
public ExternalService(ServiceConnector serviceConnector) {
this.serviceConnector = serviceConnector
}
public Result getData() {
Result result = serviceConnector.callAndGet()
prepareInformation(result)
updateStatuses(result)
result
}
private void prepareInformation(Result data) {
}
private void updateStatuses(Result data) {
}
}
public class ServiceConnector {
public Result callAndGet() {
}
}
public class Result {
String msg
}
You shouldn't try to mock "only one method" of your services. It's just a sign of a bad design, your code is not testable. You should better isolate the dependencies of your class with small services and mock this services in an unit test. Then test the upper layer with an integration test and a complete implementation of your dependencies.
In your example, ServiceConnector should be an interface, which can be easily mocked. With this condition, the test can be written as :
def "test a mocked service connector"() {
given: "a service connector"
def serviceConnector = Mock(ServiceConnector)
and: "an external service"
def externalService = new ExternalService(serviceConnector:serviceConnector)
when: "Data is loaded"
def result = externalService.data
then: "ServiceConnector is called"
serviceConnector.callAndGet() >> new Result(msg:"SUCCESS")
and: "Data is mocked"
result.msg == "SUCCESS"
}
However, if ServiceConnector is a class and you can't change this, you can use a Partial Mock in Spock. This kind of test are hard to maintain, and can have a lot of side effect :
def "test a mocked service connector"() {
given: "a service connector"
def serviceConnector = Spy(ServiceConnector) {
callAndGet() >> new Result(msg:"SUCCESS")
}
and: "an external service"
def externalService = new ExternalService(serviceConnector:serviceConnector)
when: "Data is loaded"
def result = externalService.data
then: "Data is mocked"
result.msg == "SUCCESS"
}

Correct way to configure gradle plugin extensions with groups of dynamic objects

I am trying to write my own gradle plugin and it needs to be able to configure a set of objects - how many of these objects and what they're called is up to the user.
The doco for creating custom gradle plugins with advanced customisability is quite poor. It mentions project.container() method to do this kind of thing, but I couldn't figure out how to make it work in my usecase.
This is an example of my plugin's configuration DSL as it stands:
teregrin {
terraformVersion = '0.6.6'
root("dev"){
accessKey = "flobble"
}
root("prd"){
}
}
And this is my plugin extension object that allows me to configure it:
class TeregrinPluginExtension {
boolean debug = false
boolean forceUnzip = false
String terraformVersion = null
Set<TeregrinRoot> roots = []
def root(String name, Closure c){
def newRoot = new TeregrinRoot(name)
c.setDelegate(newRoot)
c()
roots << newRoot
}
}
The extensions wired up in my plugin in the standard way:
project.extensions.create("teregrin", TeregrinPluginExtension)
This works ok, but it's a pretty ugly configuration style, not really in the style of the typical gradle DSL.
How can I change my plugin configuration DSL to be something like this:
teregrin {
terraformVersion = '0.6.6'
roots {
dev {
accessKey = "flobble"
}
prd {
}
}
}
The gradle way of implementing such DSL is by using extensions and containers:
apply plugin: SamplePlugin
whatever {
whateverVersion = '0.6.6'
conf {
dev {}
qa {}
prod {
accessKey = 'prod'
}
}
}
task printWhatever << {
println whatever.whateverVersion
whatever.conf.each { c ->
println "$c.name -> $c.accessKey"
}
}
class SamplePlugin implements Plugin<Project> {
void apply(Project project) {
project.extensions.create('whatever', SampleWhatever)
project.whatever.extensions.conf = project.container(SampleConf)
project.whatever.conf.all {
accessKey = 'dev'
}
}
}
class SampleWhatever {
String whateverVersion
}
class SampleConf {
final String name
String accessKey
SampleConf(String name) {
this.name = name
}
}
while groovy way of doing implementing such DSL is meta programming - you need to implement methodMissing in this particular case. Below is a very simple example that demonstrates how it works:
class SomeExtension {
def devConf = new SomeExtensionConf()
void methodMissing(String name, args) {
if ('dev'.equals(name)) {
def c = args[0]
c.resolveStrategy = Closure.DELEGATE_FIRST
c.delegate = devConf
c()
} else {
throw new MissingMethodException("Could not find $name method")
}
}
def getDev() {
devConf
}
}
class SomeExtensionConf {
def accessKey
}
project.extensions.create('some', SomeExtension)
some {
dev {
accessKey = 'lol'
}
}
assert 'lol'.equals(some.dev.accessKey)
Of course it has no error checking - so the args size and type of each argument need to be validated - it's omitted for the sake of brevity.
Of course there's no need to create a separate class for each configuration (I mean dev, prod, etc.). Create a single class that holds configuration and store them all in a Map where key is configuration name.
You can find a demo here.

Resources