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)
}
}
Related
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() }
}
I've my main class something like this:
class MyClass{
String bar(String inputString){
String url = "https:x.y.z/p/q"; //the URL is framed dynamically based on other class attributes
final String payloadInJson = getPayload(inputString)
final String response = doPostRequest(url, payloadInJson)
}
private static String doPostRequest(final String url, final String postData) throws IOException {
final RequestBody body = RequestBody.create(JSON, postData)
final Request request = new Request.Builder()
.url(url)
.post(body)
.build()
final Response response = createOkHttpClient().newCall(request).execute()
if (!response.isSuccessful()) {
throw new RuntimeException("...")
}
response.networkResponse().header("Location")
}
private static OkHttpClient createOkHttpClient() {
Config config = new ConfigBuilder()
.withTrustCerts(true)
.build()
def httpClient = HttpClientUtils.createHttpClient(config)
httpClient = httpClient.newBuilder().authenticator(Authenticator.NONE).build()
httpClient
}
}
and My Consumer test case is:
#SpringBootTest(classes = Application.class)
#AutoConfigureStubRunner(stubsMode = StubRunnerProperties.StubsMode.LOCAL, ids = ["com.ex:foobar:+:stubs:8090"])
class MyClassTest{
#Inject
private MyClass myClass
def 'happyPath'(){
given:
...
when:
String res = myClass.bar('lorem...')
}
}
Question is how to mock the OkHttp URL and use localhost?
Or is it that in the test case, I can refer the actual URL framed?
If you use Spring Cloud Contract, we start an HTTP server on a given or random port. It's enough for you to set you OK Http Client to point to the started server. Example
PSEUDOCODE:
class MyClass{
private String url = "https:x.y.z/p/q";
String bar(String inputString){
final String payloadInJson = getPayload(inputString)
final String response = doPostRequest(this.url, payloadInJson)
}
// package scope
void setUrl(String url) {
this.url = url;
}
}
and in your test you can then set the stub's port and url
THE TEST (PSEUDOCODE AGAIN):
#SpringBootTest(classes = Application.class)
#AutoConfigureStubRunner(stubsMode = StubRunnerProperties.StubsMode.LOCAL, ids = ["com.ex:foobar"])
class MyClassTest{
#Inject
private MyClass myClass
#StubRunnerPort("foobar") int stubPort;
def 'happyPath'(){
given:
myClass.url = "http://localhost:${stubPort}"
when:
String res = myClass.bar('lorem...')
}
}
a better option is to use a proper #Configuration class where you define beans and inject the URL via constructor. Anyways, hopefully, it shows you how you can approach the problem.
I am quite new to groovy and getting following error when running the below method. I am trying to pass xml file name and Map
RD.groovy
Given(~'^input currency "([^"]*)"$') { String baseCurr ->
fromCurr = baseCurr
}
When(~'^insert end Currency "([^"]*)"$') { String tragetCurr ->
toCurr = tragetCurr
}
Then(~'^get the expected end currency value "([^"]*)"$') { String result ->
assert result == currCon(fromCurr, toCurr)
}
private currCon(fromCurr, toCurr)
{
def binding = ["fromCurr": fromCurr, "toCurr": toCurr]
response = Consumer.currConvert("request/CurrencyConvert.xml",binding) --> This is line 119
assert 200 == response.status
return response.data.ConversionRateResult.toString()
}
ClassA.groovy
package abc.api.member
import abc.util.Log
import abc.util.TemplateUtil
import groovyx.net.http.ContentType
import abc.api.RestClient
class ClassA extends ClassB{
ClassA(RestClient restClient) {
super(restClient)
}
def currConvert(String xmlFilename, Map binding) {
return currencyConvertRequest(TemplateUtil.xmlFromTemplate(xmlFilename, binding))
}
def currencyConvertRequest(xmlString) {
def params = [path : 'CurrencyConvertor.asmx',
headers: globeHeaders(),
body: xmlString]
return restClient.post(params)
}
Consumer.Groovy
package abc.api.member
import geb.Browser
import org.apache.http.client.utils.URIBuilder
import abc.api.RestClient
import abc.browser.member.Admin
class Consumer {
Browser browser
String token
String userId
#Delegate
private ClassA classA
Consumer(url) {
browser = new Browser()
browser.baseUrl = baseUrl(url)
restClient = new RestClient(url)
classA = new ClassA(restClient)
}
private baseUrl(url) {
URI uri = URI.create(url)
URIBuilder builder = new URIBuilder()
URI result =builder.setHost(uri.host). //
setPath(uri.path). //
setPort(uri.port). //
setScheme(uri.scheme).
setUserInfo("Cons", "pbiCons").build()
return result.toURL().toString()
}
Error:
groovy.lang.MissingMethodException: No signature of method: abc.api.consumer.Consumer.currConvert() is applicable for argument types: (org.codehaus.groovy.runtime.GStringImpl, java.util.LinkedHashMap) values: [request/globe/CurrencyConvert.xml, [fromCurr:AUD, ...]]
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:55)
at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:51)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:120)
at RD.currCon(RD.groovy:119)
After searching the issue it turned out its a common issue. Couldn't figure out though. Because all solutions are subjective.
Just curious where I am doing wrong
Thanks
currConvert is an instance method, but it's being called as if it was a static method.
I had a similar problem like this :
class Example {
static void main (String [] args) {
printMessage(obj)
}
def printMessage(obj) {
}
}
I was getting the same exception at printMessage(obj).
It got fixed after changing it to like this :
class Example {
static void main (String [] args) {
new Example().printMessage(obj)
}
def printMessage(obj) {
}
}
I want to create a groovy class og script like this:
//...
def slurper = new ConfigSlurper().parse(someFile)
//...
//The exact method declaration
def methodCall(def arg){
//Whatever i want to do
}
//Maybe it is easier with methodMissing
def methodMissing(def args) {
//Whatever i want to do
}
The file to slurp could look like this:
some {
property = methodCall("with arg")
}
The question is how i can delegate the "methodCall" to the class or script that parses with the configslurper? At the moment it will give you a methodMissing.
I thik that this blog post have an example of what are you trying to do. It's more complicated than a methodMissing but can be done.
Thanks to Sérgio Michels link I found a solution:
public class ScriptWithMethods extends Script {
String scriptText;
public ScriptWithMethods(File file) {
scriptText = file.text
}
public void run() {
GroovyShell shell = new GroovyShell();
Closure closure = shell.evaluate("{it->$string}");
closure.resolveStrategy = Closure.DELEGATE_FIRST
closure.delegate = this
closure.call()
}
def methodCall(def arg){
//Whatever i want to do
}
}
//...
def script = new ScriptWithMethods(scriptText:someFile)
def slurper = new ConfigSlurper().parse(script)
Of cause you could also use "method missing", but this works in my usecase
I call some cloud that allows to execute groovy scripts.
I return data as xml from this scripts.
I use code like this:
def writer;
def xml;
writer = new StringWriter();
xml = new MarkupBuilder(writer);
xml.Response() {
node('arrtibute1': value4arrtibute1);
}
But I need to use more sophisticated way to calculating values. I want to put a lot of different nodes in this.
def writer;
def xml;
writer = new StringWriter();
xml = new MarkupBuilder(writer);
xml.Response() {
Function1();
Function2();
}
...
and implementations of this functions.
public void Function1(){
node1('arrtibute1': value4arrtibute1);
}
public void Function2(){
someOtherNode1('arrtibute1': otherValue4arrtibute1, ...);
}
Latest code doesn't work. The reason why it doesn't work is functions don't know that they run in context of response and looking for methods node1 and someOtherNode1.
When I try to pass xml in functions and try to create new response there I have deformed structure of xml document (document in document).
My question: how to let code in function to "know" that they are run in context of response?
You need to pass the builder into the functions you are calling like so:
import groovy.xml.MarkupBuilder
value4arrtibute1 = 'val1'
otherValue4arrtibute1 = 'val2'
public void function1( MarkupBuilder builder ){
builder.node1('arrtibute1': value4arrtibute1 )
}
public void function2( MarkupBuilder builder ){
builder.someOtherNode1('arrtibute1': otherValue4arrtibute1 )
}
String output = new StringWriter().with { writer ->
new MarkupBuilder(writer).with { xml ->
xml.Response() {
function1( xml )
function2( xml )
}
}
writer
}
println output
#tim_yates is correct in his answer, although I'd like to share another method of accomplishing the same sort of thing without having to pass the builder or delegate around.
In practice I usaually would make both function1 and function2 Closures and set their delegates to the builder.
import groovy.xml.MarkupBuilder
value4arrtibute1 = 'val1'
otherValue4arrtibute1 = 'val2'
Closure function1 = {
node1('arrtibute1': value4arrtibute1 )
}
Closure function2 = {
someOtherNode1('arrtibute1': otherValue4arrtibute1 )
}
String output = new StringWriter().with { writer ->
new MarkupBuilder(writer).with { xml ->
xml.Response() {
firstNode()
xml.with function1
xml.with function2
}
}
writer
}
println output