Is it somehow possible to inline statement from closure or method into an assertion so it can be printed when the assertion fails?
If we have simple statement like assert 10 == (20 - 11) then Groovy's power assert prints it nicely like:
assert 10 == (20 - 11)
| |
false 9
However if we extract the statement into a closure like:
def c = { arg -> arg == (20 - 11) }
assert c(10)
then the output will be much less informative:
assert c(10)
|
false
I'm developing custom specific test framework and the use case is to have custom assertions represented as boolean closures or methods and run the assertion from one place.
You were getting such output becuase, closure is returning boolean and assert does look for true.
Approach #1 : If you want change the ouput, then may be you can pass the two arguments to closure instead of one argument as shown below. Of course, you may not want to compare equality with static number in your framework, so the second argument make sense.
def c = { arg1, arg2 -> arg1 == arg2 }
assert c(10, 20-11), 'comparison failed'
Output:
java.lang.AssertionError: Comparison failed. Expression: c.call(10, (20 - 11))
at Script1.run(Script1.groovy:2)
Approach #2: Here changed closure name appropriately, and doing the assertion inside itself. Since its name is assertEqual, it may unlikely misused for other assertions such as >, <.
def assertEqual = { arg1, arg2 -> assert arg1 == arg2, "Comparison failed: ${arg1} vs ${arg2}" }
assertEqual(10, 20-11)
Output:
java.lang.AssertionError: Comparison failed: 10 vs 9. Expression: (arg1 == arg2). Values: arg1 = 10, arg2 = 9
at Script1$_run_closure1.doCall(Script1.groovy:1)
at Script1.run(Script1.groovy:2)
You write more closures in this manner, such as assertGreaterThan assertLessThan for specific operations.
Approach #3: Here you can even pass what should be the error message to the closure.
def assertEqual = { arg1, arg2, message -> assert arg1 == arg2, message(arg1, arg2) }
assertEqual(10, 20-11) {op1, op2 -> "$op1 is not equal to $op2"}
Output:
java.lang.AssertionError: 10 is not equal to 9. Expression: (arg1 == arg2). Values: arg1 = 10, arg2 = 9
at Script1$_run_closure1.doCall(Script1.groovy:1)
at Script1.run(Script1.groovy:2)
Approach #4 Another variant where user can pass operation, and message. You may not only do equal, but also other operations as well. So, changing the closure name to myAssert.
def myAssert = { arg1, arg2, expression -> expression(arg1, arg2) }
//Greater than
myAssert(10, 20-11) {op1, op2 -> assert op1 > op2, "$op1 is not greater than $op2" }
//Less than
myAssert(10, 20-11) {op1, op2 -> assert op1 < op2, "$op1 is not less than $op2" }
//Equal
myAssert(10, 20-11) {op1, op2 -> assert op1 == op2, "$op1 is not equal to $op2" }
Output:
java.lang.AssertionError: 10 is not less than 9. Expression: (op1 < op2). Values: op1 = 10, op2 = 9
at Script1$_run_closure2.doCall(Script1.groovy:2)
at Script1$_run_closure1.doCall(Script1.groovy:1)
at Script1.run(Script1.groovy:2)
If you want to go with simple approach and just use equal assert, then Approach #2 is right one. Of course, you are the better one to choose which one suits better for your need.
Related
From Spock documentation:
Data tables, data pipes, and variable assignments can be combined as needed:
...
where:
a | _
3 | _
7 | _
0 | _
b << [5, 0, 0]
c = a > b ? a : b
This simple example produces a MissingPropertyException for y.
def test() {
expect:
x == 42
where:
y = 12
x << [y + 30, 54 - y]
}
What's wrong with this example?
You can't mix data pipe and variable assignment that way. Let's take a look at the source code. You can put a breakpoint in org.spockframework.runtime.ParameterizedSpecRunner class at line 49 and run the test with the debugger. You will see that currentFeature holds two parameters with names y and x:
You will also notice that there is a single dataProvider for variable name x defined:
This data provider exists because we have defined x as a data pipe, so it has to iterate over the list of variables and evaluate it in the context of data pipes. In this context it expects that y variable is also defined as a data pipe so it can take a value associated with the same index.
If you define your where: as:
where:
y << [12, 12]
x << [y + 30, 54 - y]
your test would succeed, because now y variable exists as a data provider and evaluating values for x accesses values for y using second data provider.
How to combine variable assignment with data pipes?
Consider following example:
#Unroll
def "variable assignment example"() {
expect:
x == 42
and:
c
where:
y << [12, 12]
x << [y + 30, 54 - y]
c = y < x
}
In this case variable c is evaluated twice for each element in data pipes y and x. When the first unroll happens y = 12, x = y + 30 => 12 + 30 => 42 and c evaluates to true because y<x. When the second unroll happens 12 value is assigned to y again, x evaluates to 42 (54 - y => 54 - 12 => 42) and c evaluates to true again.
Conclusion
I see that it may look like simple y = 12 variable assignment should be discovered by data pipe evaluation. Unfortunately it does not work that way. When data pipe gets evaluated it can only use values from other data pipes and any variable assignment is not seen in this context.
I think assignment y=12 is not allowed in a where block.
For example, this version works, two tests run as expected:
class SampleTest extends Specification {
#Unroll
def "test"() {
expect:
x == 42
where:
x << [12 + 30, 54 - 12]
}
}
If you absolutely need an assignment, the following will also work:
class SampleTest extends Specification {
#Unroll
def "test"() {
expect:
x == 42
where:
x << myfunc()
}
def myfunc() {
def y = 12
[y + 30, 54 - y]
}
}
I'm little bit confused using double value.
When I used as below :-
double foo = 20.46455
assert 20 == foo.round()
assert 20.46 == foo.round(2)
It's working fine. but when I used something like as :-
def foo = 20.46455
assert 20 == foo.round()
it throws :-
java.lang.NullPointerException
and
def foo = 20.46455
assert 20.46 == foo.round(2)
it throws :-
groovy.lang.MissingMethodException: No signature of method: java.math.BigDecimal.round() is applicable for argument types: (java.lang.Integer) values: [2]
Possible solutions: round(java.math.MathContext), find(), pow(int), power(java.lang.Integer), find(groovy.lang.Closure), and(java.lang.Number)
It means by default in groovy, value preserve in BigDecimal and BigDecimal.round() expect java.math.MathContext as input.
But My confusion start when I'm using Math.round() which except double as input then why below statement is getting passed while groovy preserve by default in BigDecimal?
def foo = 20.46455
assert 20 == Math.round(foo)
And why I have to use .toDouble() to pass my test case while foo has value in double format as below?
def foo = 20.46455
assert 20 == foo.toDouble().round()
assert 20.46 == foo.toDouble().round(2)
Note :- I don't want to know how to round a double value, I just want to know why groovy behaves differently in each case??
Groovy automatically and implicitly uses BigDecimal for any floating-point numbers unless you define a type or you add a suffix for the number (like D).
Examples:
def foo = 20.46455
println foo.getClass()
Output:
class java.math.BigDecimal
double foo = 20.45645
println foo.getClass()
Output:
class java.lang.Double
def foo = 20.45645d
println foo.getClass()
Output:
class java.lang.Double
Type conversions:
Groovy also has certain automatic type conversions and this is the reason why even though Math.round() only accepts double and float primitives as parameters, the code is not failing when you pass a BigDecimal. To prove this, you could implement your own round function and check what happens with the type conversions:
Examples:
def round(double foo) {
println foo.getClass()
return foo.round()
}
def foo = 20.46455
println foo.getClass()
assert 20 == round(foo)
Output:
class java.math.BigDecimal
class java.lang.Double
Some more valid examples of implicit conversions:
def round(float foo) {
println foo.getClass()
return foo.round()
}
def foo = 20.46455
println foo.getClass()
assert 20 == round(foo)
Output:
class java.math.BigDecimal
class java.lang.Float
def round(float foo) {
println foo.getClass()
return foo.round()
}
def foo = 20
println foo.getClass()
assert 20 == round(foo)
Output:
class java.lang.Integer
class java.lang.Float
def round(double foo) {
println foo.getClass()
return foo.round()
}
def foo = 20
println foo.getClass()
assert 20 == round(foo)
Output:
class java.lang.Integer
class java.lang.Double
def round(BigDecimal foo) {
println foo.getClass()
return foo
}
double foo = 20.0
println foo.getClass()
assert 20 == round(foo)
Output:
class java.lang.Double
class java.lang.BigDecimal
As a rule of thumb, if the number is floating point based (double, float, BigDecimal) there will be an implicit type conversion between each other, and the code will throw an exception when trying to convert to non-floating point numbers (like int or long). If a number is not a floating point type (int, long), it can be converted between non-floating and floating point types, as floating point numbers also include non-floating points as a subset (e.g. 1 can be represented with 1.0). This makes sense, as you can't pass the floating point information from a float to an int (20.5 can't be represented with an int variable), but in most cases you can do the opposite, with the occasional exception of overflows for big values (e.g a really big long number into a float variable).
I am trying to get myself started with Groovy, I am trying to find out the numbers within 1 to 100 that are divisible by 7.
def numbers=[1..100]
def divisibleBy7 = numbers.findAll {it % 7 == 0 }
divisibleBy7.each{println it}
Can you tell me what I'm doing wrong. The error message from groovy console is not very clear
Exception thrown
groovy.lang.MissingMethodException: No signature of method: groovy.lang.IntRange.mod() is applicable for argument types: (java.lang.Integer) values: [7]
Possible solutions: pop(), min(), max(), add(java.lang.Object), add(java.lang.Object), add(int, java.lang.Object)
at divisibleBy7$_run_closure1.doCall(divisibleBy7.groovy:2)
at divisibleBy7.run(divisibleBy7.groovy:2)
The problem is with [1..100]. The square brackets are the syntax for a list. Therefore, this is a list of IntRange instead of the range itself. What you want is:
def numbers = 1..100
def divisibleBy7 = numbers.findAll {it % 7 == 0 }
divisibleBy7.each{println it}
You can also use parens around ranges.
In Groovy,what is the difference between,
def a=1..5
def b= [*1..5]
def c=[1..5]
what does * in [*1..5] symbolize?
* represents a Spread Operator. Elaborating your example:
a = 1..5
b = [*1..5]
c = [1..5]
assert a.class.name == "groovy.lang.IntRange" //Is a range from 1 till 5
assert b.class.name == "java.util.ArrayList" //Spread the range in a list
assert c.class.name == "java.util.ArrayList" //Is a list
Extending #ataylor's explanation:
assert a.size() == 5
assert b.size() == 5
assert c.size() == 1
To reach each element in c you have to iterate over it (which is a range)
c.each{println it}
Groovy Goodness by Mr Haki has a detailed example of its usage.
When you put a range object in a list, you get a list with one element of type IntRange:
assert [1..5].size() == 1
By applying the spread operator it expands the range and you get a list with five elements, the actual integers the range represents:
assert [*1..5].size() == 5
Here * (spread operator) expands the range 1..5 and hence you get a list of integers in that range [1, 2, 3, 4, 5]
How do I display a value whether it is true or false in groovy? I'm using Eclipse as my IDE.
assert 4 * ( 2 + 3 ) - 6 == 14 //integers only
And also I don't understand 'assert' too well in Groovy. Is it like an if() statement/boolean in Java?
What role does 'assert' play in Groovy?
An assertion is similar to an if, it verifies the expression you provide: if the expression is true it continues the execution to the next statement (and prints nothing), if the expression is false, it raises an AssertionError.
You can customize the error message providing a message separated by a colon like this:
assert 4 * ( 2 + 3 ) - 5 == 14 : "test failed"
which will print:
java.lang.AssertionError: test failed. Expression: (((4 * (2 + 3)) - 5) == 14)
but I had to change the values of your test, in order to make it fail.
The use of assertions is up to your taste: you can use them to assert something that must be true before going on in your work (see design by contract).
E.g. a function that needs a postive number to work with, could test the fact the argument is positive doing an assertion as first statement:
def someFunction(n) {
assert n > 0 : "someFunction() wants a positive number you provided $n"
...
...function logic...
}
Groovy asserts are now quite impressive! They will actually print out the value of every variable in the statement (which is fantastic for debugging)
for example, it might print something like this if b is 5, a is {it^2} and c is 15:
assert( a(b) == c)
. | | | |
. 25 | != 15
. 5
(Well--something like that--Groovy's would probably look a lot better).
If we could just get this kind of print-out on an exception line...
assert 'asserts' that the result of the expression will be a true