I'm rewriting some JUnit test into Spock to take advantage of the data driven test style.
I'm struggling a bit with how to provide the verification with something dynamic.
Here's what I have so far:
def "domestic rules"(from, to, oneWay, check) {
expect:
String mealResponse = getMealResponse(new BookingOptions.BookingOptionsBuilder().setFrom(from).setTo(to).setOneWay(oneWay).build());
check(mealResponse)
where:
from | to | oneWay || check
'MNL' | 'PEK' | true || assertNoMeals()
}
def assertNoMeals = {
assert JsonAssert.with(it)
.assertThat('$.links', hasSize(1))
.assertThat('$.links[0].rel', is("http://localhost:9001/api/docs/rels/ink/meal-allocations"))
.assertThat('$.links[0].uri', startsWith("http://localhost:9001/api/tenants/acme/meals/allocations/"));
}
Unfortunately, I get a NullPointerException at the line with the first row of data.
I guess thats because the closure is being run at that point, rather than just declared.
Is there a way to do this better?
Change
def "domestic rules"(from, to, oneWay, check) {
To
#Unroll
def "domestic rules from #from to #to one way #oneWay"() {
def "domestic rules"() {
when: 'get meals using certain parameters'
String mealResponse = getMealResponse(new BookingOptions.BookingOptionsBuilder().setFrom(from).setTo(to).setOneWay(oneWay).build())
then: 'the json response should contain some contents (improve the message here!)'
JsonAssert.with(mealResponse)
.assertThat('$.links', hasSize(1))
.assertThat('$.links[0].rel', is(somethingToUseInAssertions))
where:
from | to | oneWay || somethingToUseInAssertions
'MNL' | 'PEK' | true || 'just some example'
}
The above should help you get in the right track. Notice that you should have some values only in the examples. If you need some logic in the assertions, use a value which indicates what kind of assertion needs to be made... but it's a very bad idea to use a closure as an example.
If you really want to make your test hard to maintain and go ahead and use closures as "values" in your examples, then do something like this:
def "domestic rules"() {
when:
String mealResponse = getMealResponse(new BookingOptions.BookingOptionsBuilder().setFrom(from).setTo(to).setOneWay(oneWay).build())
then:
check(mealResponse)
where:
from | to | oneWay || check
'MNL' | 'PEK' | true || this.&assertNoMeals
}
boolean assertNoMeals(mealResponse) {
assert JsonAssert.with(mealResponse)
.assertThat('$.links', hasSize(1))
.assertThat('$.links[0].rel', is("http://localhost:9001/api/docs/rels/ink/meal-allocations"))
.assertThat('$.links[0].uri', startsWith("http://localhost:9001/api/tenants/acme/meals/allocations/"))
return true // pass!
}
I advise you to learn both Groovy and Spock before writing something that is more reasonable. It's not hard, but it does take at least a few hours!
Related
Is there any security concerns todo with debug.setlocal? If so can you please tell me how exactly it works.
Lua threads run on top of a "stack", a list of items that make up most of the memory in the language. Everything, from "global" variables to tables, sits happily on this list.
When you create a local variable and execute your code, the stack would look something like this:
local a = "the letter a"
STACK
+---+----------------+
| 1 | "the letter a" |
+---+----------------+
Now, let's say we are malicious code. We know that the following code is used to run our program:
local isAdministrator = false
local Code = "malicious code here!"
loadstring(Code)()
Well, that means the stack of the loader looks something like this:
+---+-------+
| 1 | false | isAdministrator
+---+-------+----------------+
| 2 | "malicious code here!" | Code
+---+---------------------+--+
| 3 | function loadstring |
+---+---------------------+--+
| 4 | function MaliciousCode |
+---+------------------------+
Now, let's say changing isAdministrator is of some value to us. Let's look at how we would do that.
1 We need to get lua to modify the "stack" above our function. Imagine lua is a tree, like so:
otherScript
+ loader
+ maliciousCode
Each step in this tree has it's own stack, and each variable from the stack above it is "accessible" to the stack below it, thanks to the debug library.
If the "stack" we are currently on is stack #1 (which is always true)
Then the function that called the current function is on stack #2 (which is almost always true)
Now, looking at the stack of the function we want to change, isAdministrator is in slot 1, the first one. So, to change this, we juuuust:
debug.setlocal(2,1,true)
And bam. isAdministrator is now true. We are free to ruin your computer!
Do not fret- there is a way to stop this. It is really easy to take away the debug library:
local func = loadstring("malicious code!")
setfenv(func, {
-- Here we insert what functions our baby function can use. Let's give it the string, table, and math libraries- which are pretty safe
math = math, table = table, string = string,
-- Oh, and let's give it the `os.time` function as well.
os = { time = os.time }
})
func()
Now, func cannot access the debug library, and you are free to use your silly isAdministrator locals in peace.
I have the below spock specification and want to update the map from data table. Can some body help achieve this
def "groovy map update"() {
setup: "step1"
Map json = [
user :[
name : 'ABC'
]]
when: "step2"
println ('Before modification:')
println (json)
then: "step3"
json.with {
//user.name = value // this one works
(field) = value // this one does not work
}
println ('After modification:')
println (json)
where:
field | value
'user.name' | 'XYZ'
}
The then section is intended for asserts and not for updates etc. So you have to update the map in the when section and then test the result in the then section. For example like this:
def "groovy map update"() {
setup: 'create json'
Map json = [user: [name: 'ABC']]
when: 'update it'
def target = json
for (node in path - path.last()) {
target = target[node]
}
target[path.last()] = value
then: 'check the assignment'
json.user.name == value
where:
path | value
['user', 'name'] | 'XYZ'
}
One way how to update nested Map value can be by using list of path nodes instead of field notation and then iterate over them to obtain the last Map instance and set the value there:
def target = json
for (node in path - path.last()) {
target = target[node]
}
target[path.last()] = value
The accepted solution is correct, I just want to show an alternative doing the same in a slightly different way, assuming you want to stick with the dotted notation for field in your where: block. I just added two more test cases in order to make sure it works as expected.
#Unroll
def "set #field to #value"() {
setup: 'create json'
Map json = [user: [name: 'ABC', address: [street: '21 Main St', zip: '12345', city: 'Hometown']]]
when: 'update it'
def subMap = json
field.split("[.]").each {
if (subMap[it] instanceof Map)
subMap = subMap[it]
else
subMap[it] = value
}
println json
then: 'check the assignment'
json.newField == value ||
json.user.name == value ||
json.user.address.zip == value
where:
field | value
'newField' | 'dummy'
'user.name' | 'XYZ'
'user.address.zip' | '98765'
}
Update: If you want to save a few lines of code you can also use a fold (or reduce or accumulate) operation via inject(..) as described here
#Unroll
def "set #field to #value"() {
setup: 'create json'
Map json = [user: [name: 'ABC', address: [street: '21 Main St', zip: '12345', city: 'Hometown']]]
when: 'update it'
field.split("[.]").inject(json) { subMap, key ->
subMap[key] instanceof Map ? subMap[key] : subMap.put(key, value)
}
println json
then: 'check the assignment'
json.newField == value ||
json.user.name == value ||
json.user.address.zip == value
where:
field | value
'newField' | 'dummy'
'user.name' | 'XYZ'
'user.address.zip' | '98765'
}
Whether you find that readable or not may depend on your familiarity with topics like functional programming in general or map/reduce in particular. The charm here in addition to brevity is that we no longer need a local variable outside of our closure but we just inject (hence the method name) the result of iteration n to iteration n+1.
BTW, as a nice side effect inject(..) as I am using it here returns the previous value of the value you set or overwrite. Just add println in front of field.split("[.]").inject(json) ... in order to see it.
Update 2: Please note that both variants only work if there is no existing field value of type Map in the target field because of the instanceof Map check heuristics in my code. I.e. these two cases would not work:
'user.address' | [street: '23 Test Blvd', zip: '33333', city: 'Somewhere']
'user.address' | '23 Test Blvd, 33333 Somewhere'
This one would work, though, because there is no preexisting value:
'user.alternativeAddress' | [street: '23 Test Blvd', zip: '33333', city: 'Somewhere']
I have written a code for my application.
def "Test for file type #FileFormat"() {
given:
HttpURLConnection connection = getHandlerURL('endpoint')
connection.setDoOutput(true)
connection.setRequestMethod("POST")
connection.setRequestProperty(HttpHeaders.CONTENT_TYPE, "RdfFormat."+RDFFileFormat+".toMIME()")
rdfStatement = ModelFactory.createDefaultModel().read(new ByteArrayInputStream(readRDFfromfile(Filename).bytes), null, "RdfFormat."+RDFFileFormat.toString()).listStatements().nextStatement()
when:
connection.getOutputStream().write(readRDFfromfile(Filename).bytes)
then:
connection.getResponseCode() == HTTP_CREATED
where:
FileFormat | Filename | RDFFileFormat
'N-TRIPLES' | 'n-triples.nt' | "NTRIPLES"
}
When I run my code I am getting error: SampleTest.Test for file type #FileFormat:37 ยป Riot in last line of Given clause.
The test is passing if I use RdfFormat.NTRIPLES.toString() instead of using the parameter RDFFileFormat passed from Where clause.
Tried assigning def format1 = "RdfFormat."+RDFFileFormat+".toString()" and using format1, but got same error.
Is there any way I can make it work?
I think you probably want:
connection.setRequestProperty(HttpHeaders.CONTENT_TYPE, RdfFormat."$RDFFileFormat".toMIME())
Currently implementing GEB,Spock,Groovy. I come across the scenario like
There is a set of data's in the spock table. I have to pass the modulename as a parameter, Search from the spock table then return two values user id and password. Below code is skeleton code
My question is how to search module name based on parameter?
How to return two data's ?
Class Password_Collection extends Specification {
def "Secure password for search and Data Driven"(String ModuleName) {
expect:
// Search based on modulename in where
// pick the values and return the picked data
where:
Module | User_Name | Pass_word
login_Pass | cqauthor1 | SGVsbG8gV29ybGQ =
AuthorPageTest_Pass | cqauthor2 | DOIaRTd35f3y4De =
PublisherPage_pass | cqaauthor3 | iFK95JKasdfdO5 ==
}
}
If you provide the code it would be great help to learn and imeplement.
You don't need to search the table yourself or pick that data. Spock will do that automatically for you
In the expect: block just write your unit test that uses Module, User_Name and Pass_word. Spock will automatically run the test 3 times (as many as the rows of the table) passing each row in turn to your test.
Remove the argument ModuleName from the test method. It is not needed.
I suggest you read the Spock documentation on Data Driven tests a bit more.
class YourSpec extends Specification {
def "Secure password for search and Data Driven"(Module, User_Name, Pass_Word) {
expect:
classUnderTest.getUserNameForModule(Module) == User_Name
classUnderTest.getPasswordForModule(Module) == Pass_Word
where:
Module | User_Name | Pass_word
login_Pass | cqauthor1 | SGVsbG8gV29ybGQ =
AuthorPageTest_Pass | cqauthor2 | DOIaRTd35f3y4De =
PublisherPage_pass | cqaauthor3 | iFK95JKasdfdO5 ==
}
}
What Spock will do is run your test one time for each row in the data table from the "where" block, passing Module, User_Name, Pass_Word as parameters and assert your expectations in the "expect" block.
Please refer to Spock Data Driven Testing documentation for more details.
I have seen _._ in the source code of a few Spock Specs. Does anyone know what that means?
For example,
def "test something"() {
given:
someClass = Mock(SomeClass)
and:
1 * someClass.someMethod() >> returnSomething
0 * _._
when:
String str = someClass.someMethod().toString()
then:
str == 'returnedValue'
}
_ is like a wildcard.
_.someMethod()
Means the method someMethod called on any mocked object, likewise
myDomain._
Means anything called on myDomain.
And following that logic _._ means anything called on any mock object. This can be useful if you don't want any more methods called:
0 * _._
Extra Reading
You should read the docs for some more information on the kinds of things _ can be used for.