Analog of TestNG data provider in Spock
up vote
0
down vote
favorite
I'm new to Spock, and currently switching to it, but I inherited plenty of test configuration files which need to be re-used. Each config file is a JSON, having the same name as Spec class. For each test method there is a list of maps with parameters, e.g.:
LoginSpec.json:
"My first test": [
"user": "user_one",
"role": "ADMIN"
,
"user": "user_two",
"role": "REPORTER",
"other_param": "other"
],
"Some Other Test Method": [
"url": "/lab1",
"button_name": "Show news popup"
]
TestNG allowed me to pass test method name in data provider method, so I could return the list of maps depending on test class name and test method name. I had only one data provider method in my base class:
public Object getData(String method)
DataReader reader = new JsonReader()
return reader.parse(packageFullName, getClass().simpleName, method)
As a result of this method I get an array of Maps to use in each of test iteration. And then I just specify this method as a DataProvider:
@Test(dataProvider = "getData", priority = 1)
void EULA_1(Map data) <====
Pages.login.openLoginPage()
Pages.login.logIn(data.user) <====
...
This works perfectly: declared ones in the base class, it automatically receives the test and provides test data.
The question is: is there a way to apply similar approach in Spock tests?
I'd like to have some getData() method in my base class, where I'm able to read tests parameters depending on the test method name and then pass them into where block.
I tried to use my json reader as shown below:
def "My first test"()
setup:
println(data)
when:
...
then:
...
where:
data = dataReader.parse("JobE2E", "LoginSpec.json", "My first test")
This example gives me required list of maps, but has two problems:
data here - is the full list of maps, not one map for each iteration;- I'm forced to explicitly type the name of test method, class etc.
Summing up:
What is the best way to implement a data provider which will receive test method name and return a list of maps?
java testing groovy testng spock
add a comment |
up vote
0
down vote
favorite
I'm new to Spock, and currently switching to it, but I inherited plenty of test configuration files which need to be re-used. Each config file is a JSON, having the same name as Spec class. For each test method there is a list of maps with parameters, e.g.:
LoginSpec.json:
"My first test": [
"user": "user_one",
"role": "ADMIN"
,
"user": "user_two",
"role": "REPORTER",
"other_param": "other"
],
"Some Other Test Method": [
"url": "/lab1",
"button_name": "Show news popup"
]
TestNG allowed me to pass test method name in data provider method, so I could return the list of maps depending on test class name and test method name. I had only one data provider method in my base class:
public Object getData(String method)
DataReader reader = new JsonReader()
return reader.parse(packageFullName, getClass().simpleName, method)
As a result of this method I get an array of Maps to use in each of test iteration. And then I just specify this method as a DataProvider:
@Test(dataProvider = "getData", priority = 1)
void EULA_1(Map data) <====
Pages.login.openLoginPage()
Pages.login.logIn(data.user) <====
...
This works perfectly: declared ones in the base class, it automatically receives the test and provides test data.
The question is: is there a way to apply similar approach in Spock tests?
I'd like to have some getData() method in my base class, where I'm able to read tests parameters depending on the test method name and then pass them into where block.
I tried to use my json reader as shown below:
def "My first test"()
setup:
println(data)
when:
...
then:
...
where:
data = dataReader.parse("JobE2E", "LoginSpec.json", "My first test")
This example gives me required list of maps, but has two problems:
data here - is the full list of maps, not one map for each iteration;- I'm forced to explicitly type the name of test method, class etc.
Summing up:
What is the best way to implement a data provider which will receive test method name and return a list of maps?
java testing groovy testng spock
add a comment |
up vote
0
down vote
favorite
up vote
0
down vote
favorite
I'm new to Spock, and currently switching to it, but I inherited plenty of test configuration files which need to be re-used. Each config file is a JSON, having the same name as Spec class. For each test method there is a list of maps with parameters, e.g.:
LoginSpec.json:
"My first test": [
"user": "user_one",
"role": "ADMIN"
,
"user": "user_two",
"role": "REPORTER",
"other_param": "other"
],
"Some Other Test Method": [
"url": "/lab1",
"button_name": "Show news popup"
]
TestNG allowed me to pass test method name in data provider method, so I could return the list of maps depending on test class name and test method name. I had only one data provider method in my base class:
public Object getData(String method)
DataReader reader = new JsonReader()
return reader.parse(packageFullName, getClass().simpleName, method)
As a result of this method I get an array of Maps to use in each of test iteration. And then I just specify this method as a DataProvider:
@Test(dataProvider = "getData", priority = 1)
void EULA_1(Map data) <====
Pages.login.openLoginPage()
Pages.login.logIn(data.user) <====
...
This works perfectly: declared ones in the base class, it automatically receives the test and provides test data.
The question is: is there a way to apply similar approach in Spock tests?
I'd like to have some getData() method in my base class, where I'm able to read tests parameters depending on the test method name and then pass them into where block.
I tried to use my json reader as shown below:
def "My first test"()
setup:
println(data)
when:
...
then:
...
where:
data = dataReader.parse("JobE2E", "LoginSpec.json", "My first test")
This example gives me required list of maps, but has two problems:
data here - is the full list of maps, not one map for each iteration;- I'm forced to explicitly type the name of test method, class etc.
Summing up:
What is the best way to implement a data provider which will receive test method name and return a list of maps?
java testing groovy testng spock
I'm new to Spock, and currently switching to it, but I inherited plenty of test configuration files which need to be re-used. Each config file is a JSON, having the same name as Spec class. For each test method there is a list of maps with parameters, e.g.:
LoginSpec.json:
"My first test": [
"user": "user_one",
"role": "ADMIN"
,
"user": "user_two",
"role": "REPORTER",
"other_param": "other"
],
"Some Other Test Method": [
"url": "/lab1",
"button_name": "Show news popup"
]
TestNG allowed me to pass test method name in data provider method, so I could return the list of maps depending on test class name and test method name. I had only one data provider method in my base class:
public Object getData(String method)
DataReader reader = new JsonReader()
return reader.parse(packageFullName, getClass().simpleName, method)
As a result of this method I get an array of Maps to use in each of test iteration. And then I just specify this method as a DataProvider:
@Test(dataProvider = "getData", priority = 1)
void EULA_1(Map data) <====
Pages.login.openLoginPage()
Pages.login.logIn(data.user) <====
...
This works perfectly: declared ones in the base class, it automatically receives the test and provides test data.
The question is: is there a way to apply similar approach in Spock tests?
I'd like to have some getData() method in my base class, where I'm able to read tests parameters depending on the test method name and then pass them into where block.
I tried to use my json reader as shown below:
def "My first test"()
setup:
println(data)
when:
...
then:
...
where:
data = dataReader.parse("JobE2E", "LoginSpec.json", "My first test")
This example gives me required list of maps, but has two problems:
data here - is the full list of maps, not one map for each iteration;- I'm forced to explicitly type the name of test method, class etc.
Summing up:
What is the best way to implement a data provider which will receive test method name and return a list of maps?
java testing groovy testng spock
java testing groovy testng spock
asked Nov 6 at 13:45
BohdanN
199118
199118
add a comment |
add a comment |
3 Answers
3
active
oldest
votes
up vote
1
down vote
You can solve problem with data
using this approach:
data << dataReader.parse('JobE2E', "$getClass().name.json", 'My first test')
It will iterate the list of maps so each test iteration will be parametrized only by that map.
Current test name can be obtained by:
specificationContext.currentFeature.name
And current iteration name by:
specificationContext.currentIteration.name
But both are not accessible in the where
section because it is executed before the test itself where only values from shared context are available. So here I'm afraid that you have to enter the test name manually.
Update: I found solution how to get feature name in where
section for you. It is realized by an own extension using interceptor.
Feature details container:
class FeatureDetails
String name
Extension annotation:
import org.spockframework.runtime.extension.ExtensionAnnotation
import java.lang.annotation.ElementType
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Target
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@ExtensionAnnotation(FeatureDetailsExtension.class)
@interface ShareFeatureDetails
Spock extension with inline interceptor implementation:
import org.spockframework.runtime.extension.AbstractAnnotationDrivenExtension
import org.spockframework.runtime.model.FeatureInfo
class FeatureDetailsExtension extends AbstractAnnotationDrivenExtension<ShareFeatureDetails>
def featureDetails = new FeatureDetails()
@Override
void visitFeatureAnnotation(ShareFeatureDetails annotation, FeatureInfo feature)
feature.addInterceptor( i ->
featureDetails.name = feature.name
feature.spec.allFields.each f ->
if (f.type == FeatureDetails.class && f.readValue(i.getInstance()) == null)
f.writeValue(i.getInstance(), featureDetails)
i.proceed()
)
Example usage of the extension:
class DataProviderSpec extends Specification
@Shared
FeatureDetails currentFeature
@Unroll("Test #data.a * 2 = #data.b")
@ShareFeatureDetails
def 'test'()
when:
println data
then:
data.a * 2 == data.b
where:
data << loadData()
@Unroll("Test #data.a * 3 = #data.b")
@ShareFeatureDetails
def 'another test'()
when:
println data
then:
data.a * 3 == data.b
where:
data << loadData()
def loadData()
// this is hard coded example
println "$getClass().name.$currentFeature.name"
if ('test' == currentFeature.name) return [[a: 1, b: 2], [a: 2, b: 4]]
if ('another test' == currentFeature.name) return [[a: 3, b: 9], [a: 4, b: 12]]
return
// ... use load from data file (JSON, YAML, XML, ...) instead:
// return dataReader.parse("$getClass().name.json", currentFeature.name)
And the output of above example:
DataProviderSpec.test
[a:1, b:2]
[a:2, b:4]
DataProviderSpec.another test
[a:3, b:6]
[a:4, b:8]
First idea was to use only annotated String featureName
field in the spec class but there is a problem where visitFeatureAnnotation()
method works with different spec instance during each call while loadData()
method is executed each time on the first instance.
Note: You can also add description with values specific to the current iteration using @Unroll
annotation. For example:
@Unroll("Test #data.a * 2 = #data.b")
def 'test'()
setup:
...
when:
...
then:
data.a * 2 == data.b
where:
data << getData('test')
def getData(String methodName)
if ('test' == methodName) return [[a: 1, b: 2], [a: 2, b: 4]]
...
Will produce:
Test 1 * 2 = 2
Test 2 * 2 = 4
Yep, thank you. This is very similar to what I have now: getData(Stirng name) method in base spec class, where I get class name and package. But I still forced to duplicate spec name in getData when calling it in where block. Really no change to get spec name there?
– BohdanN
Nov 12 at 10:53
I found solution how to get feature name in the where section and updated the answer accordingly. It is realized by a simple own Spock extension. I hope it will help to you.
– cgrim
Nov 12 at 13:30
Maybe I misunderstood something, but this solution suggests to list all the specs' names in dataprovider, in order to use if statements. This is still code duplication.
– BohdanN
Nov 12 at 14:06
loadData()
method is only example of how it works - you can adapt it on your case and load data from JSON file instead of using if statements. So instead ofloadData()
you can use your:dataReader.parse("JobE2E", "$getClass().name.json", currentFeature.name)
– cgrim
Nov 12 at 14:10
I read again your answer. Thank you, will try your extension!
– BohdanN
Nov 12 at 14:23
add a comment |
up vote
0
down vote
You could use JsonSlurper
. It basically parses JSON and returns an Object which could be a List or a Map (just cast it). You can easily use it in your were
block (remember to only use static
or @Shared
in there).
Here is some documentation about JSON in Groovy.
Hi Michael, thanks for the answer, but it's not a question of how to parse JSON. I actually have a JSON parser which works okay for me and returns a map with params (I use Gson instead of JsonSlurper but no matter here). My question is how to implement passing required spec info into the parser. Does Spock have any kind of pre-test hook where I can get test's name and load the params into list of maps and then put this list into where?
– BohdanN
Nov 9 at 10:40
add a comment |
up vote
0
down vote
Solved.
The following method, declared in the BaseSpec
class, gets the name of current spec automatically on a stage of where
block and load params from a config file accordingly:
protected List<Map<String, Object>> getData()
String methodName = StackTraceUtils.sanitize(new Throwable()).stackTrace[1].methodName
FeatureInfo spec = specificationContext.currentSpec.features.find
FeatureInfo info ->
info.dataProviders.any
it.dataProviderMethod.name == methodName
Class className = getClass()
String packageFullName = className.package.name
String packageName = packageFullName[(packageFullName.lastIndexOf(".") + 1)..-1]
TestDataReader reader = new JsonReader()
return reader.parse(packageName, className.simpleName, spec.name)
Usage in class, which is a subclass of BaseSpec
class:
def "My custom name spec"()
when:
...
then:
...
where:
data << getData()
add a comment |
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
1
down vote
You can solve problem with data
using this approach:
data << dataReader.parse('JobE2E', "$getClass().name.json", 'My first test')
It will iterate the list of maps so each test iteration will be parametrized only by that map.
Current test name can be obtained by:
specificationContext.currentFeature.name
And current iteration name by:
specificationContext.currentIteration.name
But both are not accessible in the where
section because it is executed before the test itself where only values from shared context are available. So here I'm afraid that you have to enter the test name manually.
Update: I found solution how to get feature name in where
section for you. It is realized by an own extension using interceptor.
Feature details container:
class FeatureDetails
String name
Extension annotation:
import org.spockframework.runtime.extension.ExtensionAnnotation
import java.lang.annotation.ElementType
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Target
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@ExtensionAnnotation(FeatureDetailsExtension.class)
@interface ShareFeatureDetails
Spock extension with inline interceptor implementation:
import org.spockframework.runtime.extension.AbstractAnnotationDrivenExtension
import org.spockframework.runtime.model.FeatureInfo
class FeatureDetailsExtension extends AbstractAnnotationDrivenExtension<ShareFeatureDetails>
def featureDetails = new FeatureDetails()
@Override
void visitFeatureAnnotation(ShareFeatureDetails annotation, FeatureInfo feature)
feature.addInterceptor( i ->
featureDetails.name = feature.name
feature.spec.allFields.each f ->
if (f.type == FeatureDetails.class && f.readValue(i.getInstance()) == null)
f.writeValue(i.getInstance(), featureDetails)
i.proceed()
)
Example usage of the extension:
class DataProviderSpec extends Specification
@Shared
FeatureDetails currentFeature
@Unroll("Test #data.a * 2 = #data.b")
@ShareFeatureDetails
def 'test'()
when:
println data
then:
data.a * 2 == data.b
where:
data << loadData()
@Unroll("Test #data.a * 3 = #data.b")
@ShareFeatureDetails
def 'another test'()
when:
println data
then:
data.a * 3 == data.b
where:
data << loadData()
def loadData()
// this is hard coded example
println "$getClass().name.$currentFeature.name"
if ('test' == currentFeature.name) return [[a: 1, b: 2], [a: 2, b: 4]]
if ('another test' == currentFeature.name) return [[a: 3, b: 9], [a: 4, b: 12]]
return
// ... use load from data file (JSON, YAML, XML, ...) instead:
// return dataReader.parse("$getClass().name.json", currentFeature.name)
And the output of above example:
DataProviderSpec.test
[a:1, b:2]
[a:2, b:4]
DataProviderSpec.another test
[a:3, b:6]
[a:4, b:8]
First idea was to use only annotated String featureName
field in the spec class but there is a problem where visitFeatureAnnotation()
method works with different spec instance during each call while loadData()
method is executed each time on the first instance.
Note: You can also add description with values specific to the current iteration using @Unroll
annotation. For example:
@Unroll("Test #data.a * 2 = #data.b")
def 'test'()
setup:
...
when:
...
then:
data.a * 2 == data.b
where:
data << getData('test')
def getData(String methodName)
if ('test' == methodName) return [[a: 1, b: 2], [a: 2, b: 4]]
...
Will produce:
Test 1 * 2 = 2
Test 2 * 2 = 4
Yep, thank you. This is very similar to what I have now: getData(Stirng name) method in base spec class, where I get class name and package. But I still forced to duplicate spec name in getData when calling it in where block. Really no change to get spec name there?
– BohdanN
Nov 12 at 10:53
I found solution how to get feature name in the where section and updated the answer accordingly. It is realized by a simple own Spock extension. I hope it will help to you.
– cgrim
Nov 12 at 13:30
Maybe I misunderstood something, but this solution suggests to list all the specs' names in dataprovider, in order to use if statements. This is still code duplication.
– BohdanN
Nov 12 at 14:06
loadData()
method is only example of how it works - you can adapt it on your case and load data from JSON file instead of using if statements. So instead ofloadData()
you can use your:dataReader.parse("JobE2E", "$getClass().name.json", currentFeature.name)
– cgrim
Nov 12 at 14:10
I read again your answer. Thank you, will try your extension!
– BohdanN
Nov 12 at 14:23
add a comment |
up vote
1
down vote
You can solve problem with data
using this approach:
data << dataReader.parse('JobE2E', "$getClass().name.json", 'My first test')
It will iterate the list of maps so each test iteration will be parametrized only by that map.
Current test name can be obtained by:
specificationContext.currentFeature.name
And current iteration name by:
specificationContext.currentIteration.name
But both are not accessible in the where
section because it is executed before the test itself where only values from shared context are available. So here I'm afraid that you have to enter the test name manually.
Update: I found solution how to get feature name in where
section for you. It is realized by an own extension using interceptor.
Feature details container:
class FeatureDetails
String name
Extension annotation:
import org.spockframework.runtime.extension.ExtensionAnnotation
import java.lang.annotation.ElementType
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Target
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@ExtensionAnnotation(FeatureDetailsExtension.class)
@interface ShareFeatureDetails
Spock extension with inline interceptor implementation:
import org.spockframework.runtime.extension.AbstractAnnotationDrivenExtension
import org.spockframework.runtime.model.FeatureInfo
class FeatureDetailsExtension extends AbstractAnnotationDrivenExtension<ShareFeatureDetails>
def featureDetails = new FeatureDetails()
@Override
void visitFeatureAnnotation(ShareFeatureDetails annotation, FeatureInfo feature)
feature.addInterceptor( i ->
featureDetails.name = feature.name
feature.spec.allFields.each f ->
if (f.type == FeatureDetails.class && f.readValue(i.getInstance()) == null)
f.writeValue(i.getInstance(), featureDetails)
i.proceed()
)
Example usage of the extension:
class DataProviderSpec extends Specification
@Shared
FeatureDetails currentFeature
@Unroll("Test #data.a * 2 = #data.b")
@ShareFeatureDetails
def 'test'()
when:
println data
then:
data.a * 2 == data.b
where:
data << loadData()
@Unroll("Test #data.a * 3 = #data.b")
@ShareFeatureDetails
def 'another test'()
when:
println data
then:
data.a * 3 == data.b
where:
data << loadData()
def loadData()
// this is hard coded example
println "$getClass().name.$currentFeature.name"
if ('test' == currentFeature.name) return [[a: 1, b: 2], [a: 2, b: 4]]
if ('another test' == currentFeature.name) return [[a: 3, b: 9], [a: 4, b: 12]]
return
// ... use load from data file (JSON, YAML, XML, ...) instead:
// return dataReader.parse("$getClass().name.json", currentFeature.name)
And the output of above example:
DataProviderSpec.test
[a:1, b:2]
[a:2, b:4]
DataProviderSpec.another test
[a:3, b:6]
[a:4, b:8]
First idea was to use only annotated String featureName
field in the spec class but there is a problem where visitFeatureAnnotation()
method works with different spec instance during each call while loadData()
method is executed each time on the first instance.
Note: You can also add description with values specific to the current iteration using @Unroll
annotation. For example:
@Unroll("Test #data.a * 2 = #data.b")
def 'test'()
setup:
...
when:
...
then:
data.a * 2 == data.b
where:
data << getData('test')
def getData(String methodName)
if ('test' == methodName) return [[a: 1, b: 2], [a: 2, b: 4]]
...
Will produce:
Test 1 * 2 = 2
Test 2 * 2 = 4
Yep, thank you. This is very similar to what I have now: getData(Stirng name) method in base spec class, where I get class name and package. But I still forced to duplicate spec name in getData when calling it in where block. Really no change to get spec name there?
– BohdanN
Nov 12 at 10:53
I found solution how to get feature name in the where section and updated the answer accordingly. It is realized by a simple own Spock extension. I hope it will help to you.
– cgrim
Nov 12 at 13:30
Maybe I misunderstood something, but this solution suggests to list all the specs' names in dataprovider, in order to use if statements. This is still code duplication.
– BohdanN
Nov 12 at 14:06
loadData()
method is only example of how it works - you can adapt it on your case and load data from JSON file instead of using if statements. So instead ofloadData()
you can use your:dataReader.parse("JobE2E", "$getClass().name.json", currentFeature.name)
– cgrim
Nov 12 at 14:10
I read again your answer. Thank you, will try your extension!
– BohdanN
Nov 12 at 14:23
add a comment |
up vote
1
down vote
up vote
1
down vote
You can solve problem with data
using this approach:
data << dataReader.parse('JobE2E', "$getClass().name.json", 'My first test')
It will iterate the list of maps so each test iteration will be parametrized only by that map.
Current test name can be obtained by:
specificationContext.currentFeature.name
And current iteration name by:
specificationContext.currentIteration.name
But both are not accessible in the where
section because it is executed before the test itself where only values from shared context are available. So here I'm afraid that you have to enter the test name manually.
Update: I found solution how to get feature name in where
section for you. It is realized by an own extension using interceptor.
Feature details container:
class FeatureDetails
String name
Extension annotation:
import org.spockframework.runtime.extension.ExtensionAnnotation
import java.lang.annotation.ElementType
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Target
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@ExtensionAnnotation(FeatureDetailsExtension.class)
@interface ShareFeatureDetails
Spock extension with inline interceptor implementation:
import org.spockframework.runtime.extension.AbstractAnnotationDrivenExtension
import org.spockframework.runtime.model.FeatureInfo
class FeatureDetailsExtension extends AbstractAnnotationDrivenExtension<ShareFeatureDetails>
def featureDetails = new FeatureDetails()
@Override
void visitFeatureAnnotation(ShareFeatureDetails annotation, FeatureInfo feature)
feature.addInterceptor( i ->
featureDetails.name = feature.name
feature.spec.allFields.each f ->
if (f.type == FeatureDetails.class && f.readValue(i.getInstance()) == null)
f.writeValue(i.getInstance(), featureDetails)
i.proceed()
)
Example usage of the extension:
class DataProviderSpec extends Specification
@Shared
FeatureDetails currentFeature
@Unroll("Test #data.a * 2 = #data.b")
@ShareFeatureDetails
def 'test'()
when:
println data
then:
data.a * 2 == data.b
where:
data << loadData()
@Unroll("Test #data.a * 3 = #data.b")
@ShareFeatureDetails
def 'another test'()
when:
println data
then:
data.a * 3 == data.b
where:
data << loadData()
def loadData()
// this is hard coded example
println "$getClass().name.$currentFeature.name"
if ('test' == currentFeature.name) return [[a: 1, b: 2], [a: 2, b: 4]]
if ('another test' == currentFeature.name) return [[a: 3, b: 9], [a: 4, b: 12]]
return
// ... use load from data file (JSON, YAML, XML, ...) instead:
// return dataReader.parse("$getClass().name.json", currentFeature.name)
And the output of above example:
DataProviderSpec.test
[a:1, b:2]
[a:2, b:4]
DataProviderSpec.another test
[a:3, b:6]
[a:4, b:8]
First idea was to use only annotated String featureName
field in the spec class but there is a problem where visitFeatureAnnotation()
method works with different spec instance during each call while loadData()
method is executed each time on the first instance.
Note: You can also add description with values specific to the current iteration using @Unroll
annotation. For example:
@Unroll("Test #data.a * 2 = #data.b")
def 'test'()
setup:
...
when:
...
then:
data.a * 2 == data.b
where:
data << getData('test')
def getData(String methodName)
if ('test' == methodName) return [[a: 1, b: 2], [a: 2, b: 4]]
...
Will produce:
Test 1 * 2 = 2
Test 2 * 2 = 4
You can solve problem with data
using this approach:
data << dataReader.parse('JobE2E', "$getClass().name.json", 'My first test')
It will iterate the list of maps so each test iteration will be parametrized only by that map.
Current test name can be obtained by:
specificationContext.currentFeature.name
And current iteration name by:
specificationContext.currentIteration.name
But both are not accessible in the where
section because it is executed before the test itself where only values from shared context are available. So here I'm afraid that you have to enter the test name manually.
Update: I found solution how to get feature name in where
section for you. It is realized by an own extension using interceptor.
Feature details container:
class FeatureDetails
String name
Extension annotation:
import org.spockframework.runtime.extension.ExtensionAnnotation
import java.lang.annotation.ElementType
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Target
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@ExtensionAnnotation(FeatureDetailsExtension.class)
@interface ShareFeatureDetails
Spock extension with inline interceptor implementation:
import org.spockframework.runtime.extension.AbstractAnnotationDrivenExtension
import org.spockframework.runtime.model.FeatureInfo
class FeatureDetailsExtension extends AbstractAnnotationDrivenExtension<ShareFeatureDetails>
def featureDetails = new FeatureDetails()
@Override
void visitFeatureAnnotation(ShareFeatureDetails annotation, FeatureInfo feature)
feature.addInterceptor( i ->
featureDetails.name = feature.name
feature.spec.allFields.each f ->
if (f.type == FeatureDetails.class && f.readValue(i.getInstance()) == null)
f.writeValue(i.getInstance(), featureDetails)
i.proceed()
)
Example usage of the extension:
class DataProviderSpec extends Specification
@Shared
FeatureDetails currentFeature
@Unroll("Test #data.a * 2 = #data.b")
@ShareFeatureDetails
def 'test'()
when:
println data
then:
data.a * 2 == data.b
where:
data << loadData()
@Unroll("Test #data.a * 3 = #data.b")
@ShareFeatureDetails
def 'another test'()
when:
println data
then:
data.a * 3 == data.b
where:
data << loadData()
def loadData()
// this is hard coded example
println "$getClass().name.$currentFeature.name"
if ('test' == currentFeature.name) return [[a: 1, b: 2], [a: 2, b: 4]]
if ('another test' == currentFeature.name) return [[a: 3, b: 9], [a: 4, b: 12]]
return
// ... use load from data file (JSON, YAML, XML, ...) instead:
// return dataReader.parse("$getClass().name.json", currentFeature.name)
And the output of above example:
DataProviderSpec.test
[a:1, b:2]
[a:2, b:4]
DataProviderSpec.another test
[a:3, b:6]
[a:4, b:8]
First idea was to use only annotated String featureName
field in the spec class but there is a problem where visitFeatureAnnotation()
method works with different spec instance during each call while loadData()
method is executed each time on the first instance.
Note: You can also add description with values specific to the current iteration using @Unroll
annotation. For example:
@Unroll("Test #data.a * 2 = #data.b")
def 'test'()
setup:
...
when:
...
then:
data.a * 2 == data.b
where:
data << getData('test')
def getData(String methodName)
if ('test' == methodName) return [[a: 1, b: 2], [a: 2, b: 4]]
...
Will produce:
Test 1 * 2 = 2
Test 2 * 2 = 4
edited Nov 12 at 14:18
answered Nov 9 at 21:13
cgrim
1,1181418
1,1181418
Yep, thank you. This is very similar to what I have now: getData(Stirng name) method in base spec class, where I get class name and package. But I still forced to duplicate spec name in getData when calling it in where block. Really no change to get spec name there?
– BohdanN
Nov 12 at 10:53
I found solution how to get feature name in the where section and updated the answer accordingly. It is realized by a simple own Spock extension. I hope it will help to you.
– cgrim
Nov 12 at 13:30
Maybe I misunderstood something, but this solution suggests to list all the specs' names in dataprovider, in order to use if statements. This is still code duplication.
– BohdanN
Nov 12 at 14:06
loadData()
method is only example of how it works - you can adapt it on your case and load data from JSON file instead of using if statements. So instead ofloadData()
you can use your:dataReader.parse("JobE2E", "$getClass().name.json", currentFeature.name)
– cgrim
Nov 12 at 14:10
I read again your answer. Thank you, will try your extension!
– BohdanN
Nov 12 at 14:23
add a comment |
Yep, thank you. This is very similar to what I have now: getData(Stirng name) method in base spec class, where I get class name and package. But I still forced to duplicate spec name in getData when calling it in where block. Really no change to get spec name there?
– BohdanN
Nov 12 at 10:53
I found solution how to get feature name in the where section and updated the answer accordingly. It is realized by a simple own Spock extension. I hope it will help to you.
– cgrim
Nov 12 at 13:30
Maybe I misunderstood something, but this solution suggests to list all the specs' names in dataprovider, in order to use if statements. This is still code duplication.
– BohdanN
Nov 12 at 14:06
loadData()
method is only example of how it works - you can adapt it on your case and load data from JSON file instead of using if statements. So instead ofloadData()
you can use your:dataReader.parse("JobE2E", "$getClass().name.json", currentFeature.name)
– cgrim
Nov 12 at 14:10
I read again your answer. Thank you, will try your extension!
– BohdanN
Nov 12 at 14:23
Yep, thank you. This is very similar to what I have now: getData(Stirng name) method in base spec class, where I get class name and package. But I still forced to duplicate spec name in getData when calling it in where block. Really no change to get spec name there?
– BohdanN
Nov 12 at 10:53
Yep, thank you. This is very similar to what I have now: getData(Stirng name) method in base spec class, where I get class name and package. But I still forced to duplicate spec name in getData when calling it in where block. Really no change to get spec name there?
– BohdanN
Nov 12 at 10:53
I found solution how to get feature name in the where section and updated the answer accordingly. It is realized by a simple own Spock extension. I hope it will help to you.
– cgrim
Nov 12 at 13:30
I found solution how to get feature name in the where section and updated the answer accordingly. It is realized by a simple own Spock extension. I hope it will help to you.
– cgrim
Nov 12 at 13:30
Maybe I misunderstood something, but this solution suggests to list all the specs' names in dataprovider, in order to use if statements. This is still code duplication.
– BohdanN
Nov 12 at 14:06
Maybe I misunderstood something, but this solution suggests to list all the specs' names in dataprovider, in order to use if statements. This is still code duplication.
– BohdanN
Nov 12 at 14:06
loadData()
method is only example of how it works - you can adapt it on your case and load data from JSON file instead of using if statements. So instead of loadData()
you can use your: dataReader.parse("JobE2E", "$getClass().name.json", currentFeature.name)
– cgrim
Nov 12 at 14:10
loadData()
method is only example of how it works - you can adapt it on your case and load data from JSON file instead of using if statements. So instead of loadData()
you can use your: dataReader.parse("JobE2E", "$getClass().name.json", currentFeature.name)
– cgrim
Nov 12 at 14:10
I read again your answer. Thank you, will try your extension!
– BohdanN
Nov 12 at 14:23
I read again your answer. Thank you, will try your extension!
– BohdanN
Nov 12 at 14:23
add a comment |
up vote
0
down vote
You could use JsonSlurper
. It basically parses JSON and returns an Object which could be a List or a Map (just cast it). You can easily use it in your were
block (remember to only use static
or @Shared
in there).
Here is some documentation about JSON in Groovy.
Hi Michael, thanks for the answer, but it's not a question of how to parse JSON. I actually have a JSON parser which works okay for me and returns a map with params (I use Gson instead of JsonSlurper but no matter here). My question is how to implement passing required spec info into the parser. Does Spock have any kind of pre-test hook where I can get test's name and load the params into list of maps and then put this list into where?
– BohdanN
Nov 9 at 10:40
add a comment |
up vote
0
down vote
You could use JsonSlurper
. It basically parses JSON and returns an Object which could be a List or a Map (just cast it). You can easily use it in your were
block (remember to only use static
or @Shared
in there).
Here is some documentation about JSON in Groovy.
Hi Michael, thanks for the answer, but it's not a question of how to parse JSON. I actually have a JSON parser which works okay for me and returns a map with params (I use Gson instead of JsonSlurper but no matter here). My question is how to implement passing required spec info into the parser. Does Spock have any kind of pre-test hook where I can get test's name and load the params into list of maps and then put this list into where?
– BohdanN
Nov 9 at 10:40
add a comment |
up vote
0
down vote
up vote
0
down vote
You could use JsonSlurper
. It basically parses JSON and returns an Object which could be a List or a Map (just cast it). You can easily use it in your were
block (remember to only use static
or @Shared
in there).
Here is some documentation about JSON in Groovy.
You could use JsonSlurper
. It basically parses JSON and returns an Object which could be a List or a Map (just cast it). You can easily use it in your were
block (remember to only use static
or @Shared
in there).
Here is some documentation about JSON in Groovy.
answered Nov 9 at 6:53
Michael
1,0581712
1,0581712
Hi Michael, thanks for the answer, but it's not a question of how to parse JSON. I actually have a JSON parser which works okay for me and returns a map with params (I use Gson instead of JsonSlurper but no matter here). My question is how to implement passing required spec info into the parser. Does Spock have any kind of pre-test hook where I can get test's name and load the params into list of maps and then put this list into where?
– BohdanN
Nov 9 at 10:40
add a comment |
Hi Michael, thanks for the answer, but it's not a question of how to parse JSON. I actually have a JSON parser which works okay for me and returns a map with params (I use Gson instead of JsonSlurper but no matter here). My question is how to implement passing required spec info into the parser. Does Spock have any kind of pre-test hook where I can get test's name and load the params into list of maps and then put this list into where?
– BohdanN
Nov 9 at 10:40
Hi Michael, thanks for the answer, but it's not a question of how to parse JSON. I actually have a JSON parser which works okay for me and returns a map with params (I use Gson instead of JsonSlurper but no matter here). My question is how to implement passing required spec info into the parser. Does Spock have any kind of pre-test hook where I can get test's name and load the params into list of maps and then put this list into where?
– BohdanN
Nov 9 at 10:40
Hi Michael, thanks for the answer, but it's not a question of how to parse JSON. I actually have a JSON parser which works okay for me and returns a map with params (I use Gson instead of JsonSlurper but no matter here). My question is how to implement passing required spec info into the parser. Does Spock have any kind of pre-test hook where I can get test's name and load the params into list of maps and then put this list into where?
– BohdanN
Nov 9 at 10:40
add a comment |
up vote
0
down vote
Solved.
The following method, declared in the BaseSpec
class, gets the name of current spec automatically on a stage of where
block and load params from a config file accordingly:
protected List<Map<String, Object>> getData()
String methodName = StackTraceUtils.sanitize(new Throwable()).stackTrace[1].methodName
FeatureInfo spec = specificationContext.currentSpec.features.find
FeatureInfo info ->
info.dataProviders.any
it.dataProviderMethod.name == methodName
Class className = getClass()
String packageFullName = className.package.name
String packageName = packageFullName[(packageFullName.lastIndexOf(".") + 1)..-1]
TestDataReader reader = new JsonReader()
return reader.parse(packageName, className.simpleName, spec.name)
Usage in class, which is a subclass of BaseSpec
class:
def "My custom name spec"()
when:
...
then:
...
where:
data << getData()
add a comment |
up vote
0
down vote
Solved.
The following method, declared in the BaseSpec
class, gets the name of current spec automatically on a stage of where
block and load params from a config file accordingly:
protected List<Map<String, Object>> getData()
String methodName = StackTraceUtils.sanitize(new Throwable()).stackTrace[1].methodName
FeatureInfo spec = specificationContext.currentSpec.features.find
FeatureInfo info ->
info.dataProviders.any
it.dataProviderMethod.name == methodName
Class className = getClass()
String packageFullName = className.package.name
String packageName = packageFullName[(packageFullName.lastIndexOf(".") + 1)..-1]
TestDataReader reader = new JsonReader()
return reader.parse(packageName, className.simpleName, spec.name)
Usage in class, which is a subclass of BaseSpec
class:
def "My custom name spec"()
when:
...
then:
...
where:
data << getData()
add a comment |
up vote
0
down vote
up vote
0
down vote
Solved.
The following method, declared in the BaseSpec
class, gets the name of current spec automatically on a stage of where
block and load params from a config file accordingly:
protected List<Map<String, Object>> getData()
String methodName = StackTraceUtils.sanitize(new Throwable()).stackTrace[1].methodName
FeatureInfo spec = specificationContext.currentSpec.features.find
FeatureInfo info ->
info.dataProviders.any
it.dataProviderMethod.name == methodName
Class className = getClass()
String packageFullName = className.package.name
String packageName = packageFullName[(packageFullName.lastIndexOf(".") + 1)..-1]
TestDataReader reader = new JsonReader()
return reader.parse(packageName, className.simpleName, spec.name)
Usage in class, which is a subclass of BaseSpec
class:
def "My custom name spec"()
when:
...
then:
...
where:
data << getData()
Solved.
The following method, declared in the BaseSpec
class, gets the name of current spec automatically on a stage of where
block and load params from a config file accordingly:
protected List<Map<String, Object>> getData()
String methodName = StackTraceUtils.sanitize(new Throwable()).stackTrace[1].methodName
FeatureInfo spec = specificationContext.currentSpec.features.find
FeatureInfo info ->
info.dataProviders.any
it.dataProviderMethod.name == methodName
Class className = getClass()
String packageFullName = className.package.name
String packageName = packageFullName[(packageFullName.lastIndexOf(".") + 1)..-1]
TestDataReader reader = new JsonReader()
return reader.parse(packageName, className.simpleName, spec.name)
Usage in class, which is a subclass of BaseSpec
class:
def "My custom name spec"()
when:
...
then:
...
where:
data << getData()
answered Nov 12 at 14:17
BohdanN
199118
199118
add a comment |
add a comment |
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53173193%2fanalog-of-testng-data-provider-in-spock%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown