I have a test with loops in the then clause:
result.each {
it.name.contains("foo")
it.entity.subEntity == "bar"
}
for (String obj : result2) {
obj.name.contains("foo")
obj.entity.subEntity == "bar"
}
Newly I recognized that the loops are not really tested. No matter whether I have foo or bar or anything else, the test is always green :)
I found out, that loops have to be tested differently, e.g. with 'every'? But just changing the 'each' to 'every' throws exception:
result.every {
it.name.contains("foo")
it.entity.subEntity == "bar"
}
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
Spec expression: 1: expecting '}', found '==' # line 1, column 61.
s("foo") it.entity.rootEntity == "bar" }
How should I correctly use loops in my test? I am using spock 0.7-groovy-2.0
Either use explicit assert statements:
result.each {
assert it.name.contains("foo")
assert it.entity.subEntity == "bar"
}
Or a single boolean expression within every:
result.every {
it.name.contains("foo") && it.entity.subEntity == "bar"
}
Related
How does the Kotlin compiler decide whether an expression enclosed in { } is a block or a lambda?
Consider this:
val a: Int
if (cond)
a = 1
else
a = 2
can be written more succinctly as:
val a =
if (cond)
1
else
2
Similarly, one might think that this:
val a: () -> Int
if (cond)
a = { 1 }
else
a = { 2 }
should be able to be written more succinctly like this:
val a =
if (cond)
{ 1 }
else
{ 2 }
But this is not the same: a is now an Int, and not of type () -> Int, because now the { 1 } is no longer a lambda. What are the rules that say whether something is a lambda or a block?
I didn't look into the Kotlin lexer, but I guess there are few possible places in the code where the compiler expects either a single expression or a block of code. That includes the code immediately following most of control flow statements like: if, else, while, when (one of its cases), etc. If you put { in one of these places, it will be interpreted as a start of the block of code related to this control flow statement and not as a lambda.
This is as simple as that. Note that even if you hint the compiler about the type, it still won't work:
// compile error
val a: () -> Int = if (cond)
{ 1 }
else
{ 2 }
It will be interpreted more like this:
// compile error
val a: () -> Int = if (cond) {
1
} else {
2
}
{ after if condition is always interpreted as a start of block of code. You need to put double {, } in cases like this:
// works fine
val a: () -> Int = if (cond) {
{ 1 }
} else {
{ 2 }
}
To put it very succinctly and easy to remember, the first opening brace after an if/when/else/for is always assumed to be the opening of a block. Use double braces if you want a lambda in there.
This is specified in the Kotlin Language Specification, section 1.2: Syntax Grammar:
The grammar defines a block as statements between { and }. In most cases (such as function bodies) the syntax is different between a block and a lambda. Where it is the same is in the usage of controlStructureBody - these are the places where the block has a value, or where you could put a non-block expression in its place. If you search the whole spec document for "controlStructureBody", you'll find it's used in the following places:
For statement
While statement
Do-while statement
If expression
When expression
When entry
In every other place where a value is required, a '{' signifies the start of a lambda.
val a = 2
if (a==1 | a==2) {
}
This code doesnt compile - error "Unexpected tokens (use ; to separate expressions in the same line)
How to solve? why is that even a problem? and why do the tutorials not aware of that compilation error here ?
Kotlin playground with that code
looks like a mistake on translating it to kotlin.
if you look on the java part they use
if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT)
so in that case use or keyword or ;-) ||
fun main() {
println("Hello, world!!!")
val a = 2
if ((a==1) or (a==2)) {
println("fine")
}
}
I just put this test method together:
#Unroll
def 'super start edit should be called if cell is not empty'( boolean empty ){
given:
DueDateEditor editor = GroovySpy( DueDateEditor ){
isEmpty() >> empty
}
when:
editor.startEdit()
then:
if( empty){
0 * editor.callSuperStartEdit()
}
else {
1 * editor.callSuperStartEdit()
}
where:
empty | _
true | _
false | _
}
... it works OK in terms of the two tests passing... but when you make it fail it's very odd: the output if the parameter empty is false is
super start edit should be called if cell is not empty[1]
... and it is 0 if the parameter empty is true. Is this a bug?
I am writing an additional answer because
there is a small bug in Tim's solutions with regard to the title (but still his answer is technically absolutely correct!),
you don't need a GroovySpy here, a simple Spy absolutely suffices,
I want to show you an alternative way of testing without stubbing isEmpty(),
I want to show you how you can use just one interaction with the number of calls in a ternary expression instead of an if-else (even though error reporting is ugly then),
I want to comment on your way of testing in general (see the end of this post).
package de.scrum_master.stackoverflow.q61032514;
import java.time.LocalDate;
public class DueDateEditor {
String text;
public boolean isEmpty() {
return text == null || text.trim() == "";
}
public void startEdit() {
if (!isEmpty())
callSuperStartEdit();
}
public void callSuperStartEdit() {}
}
package de.scrum_master.stackoverflow.q61032514
import spock.lang.Specification
import spock.lang.Unroll
class DueDateEditorTest extends Specification {
#Unroll
def 'super start edit #shouldMsg be called if the cell is #cellStateMsg'() {
given:
DueDateEditor editor = Spy() {
isEmpty() >> empty
}
when:
editor.startEdit()
then:
(empty ? 0 : 1) * editor.callSuperStartEdit()
where:
empty << [true, false]
shouldMsg = empty ? 'should not' : 'should'
cellStateMsg = empty ? 'empty' : 'not empty'
}
#Unroll
def "super start edit #shouldMsg be called if cell text is '#text'"() {
given:
DueDateEditor editor = Spy()
editor.text = text
when:
editor.startEdit()
then:
(editor.isEmpty() ? 0 : 1) * editor.callSuperStartEdit()
// Or, if 'isEmpty()' has a side effect:
// (text ? 1 : 0) * editor.callSuperStartEdit()
where:
text << ["foo", "", null, "line 1\nline 2"]
shouldMsg = text ? 'should' : 'should not'
cellStateMsg = text ? 'not empty' : 'empty'
}
}
General remarks:
I would not test the internal wiring of a single class with interactions. The test will be brittle and if you refactor the class internally without changing the API at all, the test might break if the interactions are no longer as expected. I think this is over-specification and I would only use interaction testing for crucial interactions between different classes or maybe different instances of one class - "crucial" meaning things like the functionality of design patterns like Observer.
Having an if-else for differentiating two cases by two different interaction patterns if the whole test only knows exactly those two cases just makes the test less readable and more complex, see your own code as well as mine and Tim's. In such a case I would rather write two feature methods with simple titles and simple functionality, but without if-else or ternary expressions, without helper variables for titles etc.
P.S.: Sorry, I had to make up a sample class under test DueDateEditor in order to make my test compile and run as expected. As usual, Mike unfortunately didn't provide an MCVE but just a part of it.
Update: We talked about GroovySpy in our comments and, as I said, it will not work if your classes are Java classes and there is a final method in you want to stub, see the Spock manual. Here is proof for you:
package de.scrum_master.stackoverflow.q61032514;
public class TreeTableCell<A, B> {
String text;
public final boolean isEmpty() {
return text == null || text.trim() == "";
}
}
package de.scrum_master.stackoverflow.q61032514;
import java.time.LocalDate;
public class DueDateEditor extends TreeTableCell<String, LocalDate> {
public void startEdit() {
if (!isEmpty())
callSuperStartEdit();
}
public void callSuperStartEdit() {}
}
package de.scrum_master.stackoverflow.q61032514
import spock.lang.Specification
import spock.lang.Unroll
class DueDateEditorTest extends Specification {
#Unroll
def 'super start edit #shouldMsg be called if the cell is #cellStateMsg'() {
given:
DueDateEditor editor = GroovySpy() {
isEmpty() >> empty
}
when:
editor.startEdit()
then:
(empty ? 0 : 1) * editor.callSuperStartEdit()
where:
empty << [true, false]
shouldMsg = empty ? 'should not' : 'should'
cellStateMsg = empty ? 'empty' : 'not empty'
}
}
The test would work if your application classes were Groovy classes only. But if they are Java classes like in my example, the test will fail like this:
Too few invocations for:
(empty ? 0 : 1) * editor.callSuperStartEdit() (0 invocations)
Unmatched invocations (ordered by similarity):
1 * editor.startEdit()
methodName == "callSuperStartEdit"
| |
startEdit false
10 differences (44% similarity)
(s---------)tartEdit
(callSuperS)tartEdit
So in this case you cannot just use Groovy magic to check interactions. But as I said, you shouldn't do that anyway. Rather make sure that both startEdit() and callSuperStartEdit() do the right things. Check their results or, if they are void, check their side effects on the state of the subject under test or its collaborators.
Update 2: Regarding your original question about indexed method naming, actually #tim_yates gave the correct answer. I just want to add the corresponding Spock manual link explaining method unrolling and how you can influence naming using variables from the where: block.
No it's not a bug
You didn't tell spock how to name your tests differently so it adds the iteration (0 then 1) to the name
Change it to
#Unroll
def 'super start edit should be called if isEmpty for the cell returns #empty'(){
given:
DueDateEditor editor = GroovySpy( DueDateEditor ){
isEmpty() >> empty
}
when:
editor.startEdit()
then:
if( empty){
0 * editor.callSuperStartEdit()
}
else {
1 * editor.callSuperStartEdit()
}
where:
empty << [true, false]
}
I changed the where section, as a table with one column felt odd
edit
You can also do this:
#Unroll
def 'super start edit should be called if the cell is #msg'(){
given:
DueDateEditor editor = GroovySpy( DueDateEditor ){
isEmpty() >> empty
}
when:
editor.startEdit()
then:
if( empty){
0 * editor.callSuperStartEdit()
}
else {
1 * editor.callSuperStartEdit()
}
where:
empty << [true, false]
msg = empty ? 'empty' : 'not empty'
}
So i want to compare three members of an array with as little code as possible. Heres what i did:
for(i in 0..2) {
if(board[i][0] == board[i][1] == board[i][2]) {
return true
} else if(board[0][i] == board[1][i] == board[2][i]) {
return true
}
}
(All of the values ar Char's FYI) But it didnt work. I get this error message "Operator '==' cant be applied to 'Boolean' and 'Char'". I also tried using .equals, but that just didnt work. Any ideas on what to do?
You can write a small function to keep it more readable and tidy, especially if You need to do that comparison often:
fun allEqual(vararg items: Any) : Boolean {
for(i in 1..(items.size-1)){
if(items[0] != items[i]) return false
}
return true
}
And invoke simply by comma separating values:
allEqual(board[i][0], board[i][1], board[i][2])
I don't know Kotlin specifically, but most* languages don't allow you to compare 3 values at the same time. What your error message is communicating is that your code ends up comparing
"Is board[i][0] equal to board[i][1]?" which is true/false (Boolean)
to
board[i][2], which is a Char.
*I don't know of any, but maybe there's one out there that does.
You have included this condition:
if(board[i][0] == board[i][1] == board[i][2])
Firstly, this one is compared: board[i][1] == board[i][2]
After comparing, it returns true. After that if logic converts to:
if(board[i][0] == true)
Now, board[i][0] is a char and you are trying to compare it to a boolean which is not possible. That's why you are getting this error.
You have to change the logic to:
if((board[i][0] == board[i][1]) && (board[i][1] == board[i][2]))
So, your code will be:
for(i in 0..2) {
if((board[i][0] == board[i][1]) && (board[i][1] == board[i][2])) {
return true
} else if((board[0][i] == board[1][i]) && (board[1][i] == board[2][i])) {
return true
}
}
Another approach:
for (i in 0..2) {
if (board[i].toSet().size == 1)
return true
else if (board.map { it[i] }.toSet().size == 1)
return true
}
As the others said, your first comparison returns Boolean, and the second compares Boolean to Char.
You can use an extension function, and transitivity to simplify things:
fun Any.equalsAll(vararg others: Any):Boolean
{
others.forEach {
if(it!=this)
return false
}
return true
}
and call:
if (board[0][i].equalsAll(board[1][i], board[2][i]))
Is there in GoogleTest something like:
ASSERT_EQ_ONE_OF_TWO(TestValue, Value1, Value2)
which tests if TestValue == Value1 || TestValue == Value2?
This variant:
ASSERT_TRUE(TestValue == Value1 || TestValue == Value2)
is OK, but it does not show in log which value TestValue has if it fails.
Is there in GoogleTest something like
I think No.
is OK, but it does not show in log which value TestValue has if it
fails.
You can add addition log information like this:
TEST (ExampleTest, DummyTest)
{
// Arrange.
const int allowedOne = 7;
const int allowedTwo = 42;
int real = 0;
// Act.
real = 5;
// Assert.
EXPECT_TRUE (real == allowedOne || real == allowedTwo)
<< "Where real value: " << real
<< " not equal neither: " << allowedOne
<< " nor: " << allowedTwo << ".";
}
This code will be produce the following log when fails:
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from ExampleTest
[ RUN ] ExampleTest.DummyTest
/home/gluttton/ExampleTest.cpp:13: Failure
Value of: real == allowedOne || real == allowedTwo
Actual: false
Expected: true
Where real value: 5 not equal neither: 7 nor: 42.
[ FAILED ] ExampleTest.DummyTest (0 ms)
[----------] 1 test from ExampleTest (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (0 ms total)
[ PASSED ] 0 tests.
[ FAILED ] 1 test, listed below:
[ FAILED ] ExampleTest.DummyTest
You can use EXPECT_THAT() in combination with a container and the Contains() matcher to achieve this:
EXPECT_THAT((std::array{ Value1, Value2 }), Contains(TestValue));
Note that the braces after array are needed for list initialization and the parentheses around array are needed, because macros (such as EXPECT_THAT()) do not understand braces and would otherwise interpret the two arguments as three arguments.
This will give an output similar to this (created with GTest 1.10)
error: Value of: (std::array{ Value1, Value2 })
Expected: contains at least one element that is equal to 42
Actual: { 12, 21 }
Pro:
Prints all values
one-liner
works out of the box
Con:
Finding the value of TestValue in the output is not easy
Syntax is not straight forward
Modern compiler features are needed
C++11 is needed due to std::array and list initialization (still not available everywhere)
C++17 is needed due to CTAD, use std::initializer_list<T>{ ... } on C++11/14. Alternatively a free function can be used to deduce size and type of the array.
I haven't found anything "baked in" to do what you are asking, but a predicate assertion should be able to handle the type of assertion you are asking for. Additionally, GoogleTest will automatically print out the arguments and their values when the assertion does fail.
The assertion you would use in your case is
ASSERT_PRED3(<insert predicate>, TestValue, Value1, Value2)
The predicate is a function or functor that returns bool, where false fails the assertion. For your predicate, you could use a function like the following:
bool OrEqual(int testValue, int option1, int option2)
{
if (testValue == option1 ||
testValue == option2)
{
return true;
}
else
{
return false;
}
}
Of course, this is a simple example. Since you can provide any function or functor that takes the provided arguments, there is plenty that you can do with predicate assertions.
Here is the documentation: https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#predicate-assertions-for-better-error-messages
You can use the following:
ASSERT_THAT(TestValue, AnyOf(Value1, Value2));
or if you need floating point matching with doubles:
ASSERT_THAT(TestValue, AnyOf(DoubleEq(Value1), DoubleEq(Value2)));