I am getting weird results from a Spock unit test that I thought was being caused by a misuse of Groovy's TupleConstructor annotation. However, thanks to the help of another user, I see it is a problem with the way Spock is creating mocks. Although I have fixed the issue by replacing the injected mocks with real instances, I need to in fact get mocks working here.
My main classes:
#Canonical
#TupleConstructor(callSuper = true)
abstract class Vehicle {
Long id
}
#Canonical
#TupleConstructor(callSuper = true, includeSuperProperties = true)
abstract class Foobaz extends Vehicle {
String name
String label
String description
}
#Canonical
#TupleConstructor(callSuper = true, includeSuperProperties = true)
class Fizz extends Foobaz {
// This is an empty class that creates a meaningful name over the
// abstract Foobaz parent class. This may seem like bad design in
// this analogy, but I assure you it makes sense (from a Domain-Driven
// Design perspective) in my actual application.
}
#Canonical
#TupleConstructor(callSuper = true, includeSuperProperties = true)
class Car extends Vehicle {
Fizz fizz1
Fizz fizz2
#Override
String toString() {
"${fizz1.name} - ${fizz2.name}"
}
}
My Spock test:
class CarSpec extends Specification {
def "toString() generates a correct string"() {
given: "a Car with some mocked dependencies"
String f1 = 'fizzy'
String f2 = 'buzzy'
Fizz fizz1 = Mock(Fizz)
Fizz fizz2 = Mock(Fizz)
fizz1.name >> f1
fizz2.name >> f2
Car car = new Car(1L, fizz1, fizz2)
when: "we call toString()"
String str = car.toString()
then: "we get a correctly formatted string"
"${f1} - ${f2}" == str
}
}
But when I run this I get the following failure/error:
Condition not satisfied:
"${f1} - ${f2}" == str
| | | |
fizzy buzzy | null - null
false
<omitting details here for brevity>
Expected :null - null
Actual :fizzy - buzzy
Any ideas where I'm going awry?
If you change your specification to this:
class CarSpec extends Specification {
def "toString() generates a correct string"() {
given: "a Car with some mocked dependencies"
String f1 = 'fizzy'
String f2 = 'buzzy'
Fizz fizz1 = Mock()
Fizz fizz2 = Mock()
Car car = new Car(1L, fizz1, fizz2)
when: "we call toString()"
String str = car.toString()
then: "we get a correctly formatted string + getProperty('name') is called once on each Mock"
"$f1 - $f2" == str
1 * fizz1.getProperty('name') >> f1
1 * fizz2.getProperty('name') >> f2
}
}
So you define the interactions in the then block, then it should all work fine...
From our discussion on a different one of #smeeb's questions, I looked into this a bit more since I was very confused why this wasn't working.
I created my own test.
class SomeTest extends Specification {
static class Driver {
String getName(Superclass superclass) {
return superclass.name
}
}
static abstract class Superclass {
String name
}
static class Subclass extends Superclass {
}
def 'test'() {
given:
def driver = new Driver()
def subclass = Mock(Subclass)
subclass.name >> 'test'
expect:
driver.getName(subclass) == 'test'
}
}
It failed with the same problem that #smeeb saw.
driver.getName(subclass) == 'test'
| | | |
| null | false
| Mock for type 'Subclass' named 'subclass'
I tried changing a few different things and found that when I either removed the abstract modifier from Superclass or changed the return superclass.name to return superclass.getName() the test began working.
It seems there is a weird interaction on the Groovy-level between getting inherited public fields from an abstract superclass using the auto-generated accessors.
So, in your case either remove abstract modifier from FooBaz, or change your code to:
#Override
String toString() {
"${fizz1.getName()} - ${fizz2.getName()}"
}
Related
Given I have JUnit tests written in Groovy:
class AssertTests {
#Test
void "explicit assert statement"() {
def value = 42
assert value == 100
}
#Test
void "no assert statement"() {
def value = 42
value == 100
}
}
When I execute them,
explicit assert statement test fails as expected thanks to assert statement.
no assert statement test passes and I would expect it to fail in a similar way how it's done when I use http://spockframework.org
How can I achieve implicit assert behavior for tests written in plain Groovy?
The answer is simple - you can't. Spock uses AST transformations to grab the code written in the then: part and transform it into the code that makes an equivalent of assertion (not the exact assert.)
To illustrate this, here is your test written in Spock:
import spock.lang.Specification
class TestSpec extends Specification {
def "should fail"() {
when:
def value = 42
then:
assert value == 100
}
}
And here is what its bytecode decompiled back to Java looks like:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.GroovyObject;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.callsite.CallSite;
import org.spockframework.runtime.ErrorCollector;
import org.spockframework.runtime.SpockRuntime;
import org.spockframework.runtime.ValueRecorder;
import org.spockframework.runtime.model.BlockKind;
import org.spockframework.runtime.model.BlockMetadata;
import org.spockframework.runtime.model.FeatureMetadata;
import org.spockframework.runtime.model.SpecMetadata;
import spock.lang.Specification;
#SpecMetadata(
filename = "TestSpec.groovy",
line = 5
)
public class TestSpec extends Specification implements GroovyObject {
public TestSpec() {
CallSite[] var1 = $getCallSiteArray();
super();
}
#FeatureMetadata(
line = 7,
name = "should fail",
ordinal = 0,
blocks = {#BlockMetadata(
kind = BlockKind.WHEN,
texts = {}
), #BlockMetadata(
kind = BlockKind.THEN,
texts = {}
)},
parameterNames = {}
)
public void $spock_feature_0_0() {
CallSite[] var1 = $getCallSiteArray();
ErrorCollector $spock_errorCollector = (ErrorCollector)ScriptBytecodeAdapter.castToType(var1[0].callConstructor(ErrorCollector.class, false), ErrorCollector.class);
ValueRecorder $spock_valueRecorder = (ValueRecorder)ScriptBytecodeAdapter.castToType(var1[1].callConstructor(ValueRecorder.class), ValueRecorder.class);
Object var10000;
try {
Object value = 42;
try {
SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), "value == 100", Integer.valueOf(12), Integer.valueOf(16), (Object)null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(2)), ScriptBytecodeAdapter.compareEqual($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(0)), value), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(1)), 100))));
var10000 = null;
} catch (Throwable var14) {
SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, "value == 100", Integer.valueOf(12), Integer.valueOf(16), (Object)null, var14);
var10000 = null;
} finally {
;
}
var1[2].call(var1[3].call(this.getSpecificationContext()));
} finally {
$spock_errorCollector.validateCollectedErrors();
var10000 = null;
}
}
}
If you look at the SpockRuntime class, you will find that verifyCondition method checks if the condition found in the then: or and: block evaluates to true:
public static void verifyCondition(#Nullable ErrorCollector errorCollector, #Nullable ValueRecorder recorder,
#Nullable String text, int line, int column, #Nullable Object message, #Nullable Object condition) {
if (!GroovyRuntimeUtil.isTruthy(condition)) {
final ConditionNotSatisfiedError conditionNotSatisfiedError = new ConditionNotSatisfiedError(
new Condition(getValues(recorder), text, TextPosition.create(line, column), messageToString(message), null, null));
errorCollector.collectOrThrow(conditionNotSatisfiedError);
}
}
You can't avoid explicit assert in JUnit tests. You can hide them in some helper method, but you still need to call them. Keep in mind, that JUnit runner requires that the test method returns void, so you can't capture the result of a test method. (Replace void with def, and you will see that JUnit does not run your test.)
If you want to explore the world of compile-time metaprogramming in Groovy, you could experiment with writing your own AST transformations. Maybe there is a way to find boolean expression(s) and inject assert in front of it, but I can't guarantee you that it will work. If you look for an out-of-the-box solution for implicit asserts in Groovy JUnit tests, there is no such one.
How do I check for deep object equality with spock.
Lets say we have a super simple test that compares to identical person objects
def "A persons test"() {
setup:
def person1 = new Person("Foo", new Address("Bar"))
def person2 = new Person("Foo", new Address("Bar"))
expect:
person1 == person2
}
The test fails
Condition not satisfied:
person1 == person2
| | |
| | Person#6bedbc4d
| false
Person#57af006c
This looks like a very natural way of asserting equality.
One of the main reason to start using spock was to avoid having to write a lot of hamcrest boilerplate matchers code.
Spock has no built-in mechanism for performing deep Object comparison, because defining object equality is out of scope of any testing framework. You can do a various things.
1. Both classes are Groovy classes
If both your classes (Person and Address) are Groovy classes you can generate equals and hashCode methods using #EqualsAndHashCode annotation over both classes, like:
import groovy.transform.EqualsAndHashCode
import groovy.transform.TupleConstructor
import spock.lang.Specification
class PersonSpec extends Specification {
def "a person test"() {
setup:
def person1 = new Person("Foo", new Address("Bar"))
def person2 = new Person("Foo", new Address("Bar"))
expect:
person1 == person2
}
#TupleConstructor
#EqualsAndHashCode
static class Person {
String name
Address address
}
#TupleConstructor
#EqualsAndHashCode
static class Address {
String city
}
}
This is just a convenient alternative for implementing both methods in Groovy.
2. Both classes are Java classes
If you want to compare both objects with == operator then you will have to define equals and hashCode methods in both classes, something like:
public final class Person {
private final String name;
private final Address address;
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public Address getAddress() {
return address;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
if (name != null ? !name.equals(person.name) : person.name != null) return false;
return address != null ? address.equals(person.address) : person.address == null;
}
#Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (address != null ? address.hashCode() : 0);
return result;
}
static class Address {
private final String city;
public Address(String city) {
this.city = city;
}
public String getCity() {
return city;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Address address = (Address) o;
return city != null ? city.equals(address.city) : address.city == null;
}
#Override
public int hashCode() {
return city != null ? city.hashCode() : 0;
}
}
}
In this example both methods were defined using IntelliJ IDEA "Generate equals and hashCode" command.
3. I can use Lombok!
If you don't want to define both methods manually (because e.g. you have to remember to change them anytime you modify your class fields) then you can use Lombok's #EqualsAndHashCode annotation that does something similar to Groovy's annotation, but can be applied to any Java class.
4. I want to keep default equals and hashCode methods
Well, in this case you can try various things:
You can try comparing both objects field-by-field, like:
class PersonSpec extends Specification {
def "a person test"() {
setup:
def person1 = new Person("Foo", new Address("Bar"))
def person2 = new Person("Foo", new Address("Bar"))
expect:
person1.name == person2.name
and:
person1.address.city == person2.address.city
}
#TupleConstructor
static class Person {
String name
Address address
}
#TupleConstructor
static class Address {
String city
}
}
You can try using 3rd party tools like Unitils reflection assertion
That may sound bizarre, but you can compare JSON representation of both objects, something like:
import groovy.json.JsonOutput
import groovy.transform.TupleConstructor
import spock.lang.Specification
class PersonSpec extends Specification {
def "a person test"() {
setup:
def person1 = new Person("Foo", new Address("Bar"))
def person2 = new Person("Foo", new Address("Bar"))
expect:
new JsonOutput().toJson(person1) == new JsonOutput().toJson(person2)
}
#TupleConstructor
static class Person {
String name
Address address
}
#TupleConstructor
static class Address {
String city
}
}
Anyway, I would definitely suggest defining equals and hashCode in one way or another and simply use == operator. Hope it helps.
You can take advantage of Groovy's succinct map comparison syntax:
person1.properties == person2.properties
That only works for simple flat objects, not nested ones. You could adapt it like so:
person1.properties << ['address': person1.address.properties] == person2.properties << ['address': person2.address.properties]
...but JSON solution is more elegant at that point.
It seems, that you need to correct override your equals and hashcode methods.
In Groovy it can be done really easily, you need to use #Canonical annotation. It gives you more than equals and hashcode, buy the way.
I highly recommend you use Assertj for deep assertions. See below an example:
def "labels1 should be deeply equal to labels2"() {
when:
def labels1 = [new Label("labelA"), new Label("labelB")]
def labels2 = [new Label("labelB"), new Label("labelA")]
then:
assertThat(labels1)
.usingRecursiveComparison()
.ignoringCollectionOrder()
.isEqualTo(labels2)
}
Don't forget to add the Gradle dependency:
dependencies {
testImplementation "org.assertj:assertj-core:3.11.1"
}
I want to retrieve the annotation value used from MyAnnot. I am getting 3 annotations in the list even though there are only 2. Also, I have tried obtaining the used field of MyAnnot but with no success. I would like to return a map where MyAnnot's used is the key and type as the map's value.
// Then, define your class with it's annotated Fields
class MyClass {
#MyAnnot(used = "Hey", type = "There")
String fielda
#MyAnnot(used = "denn", type = "Ton")
String fieldc
}
def findAllPropertiesForClassWithAnotation(obj, annotClass) {
def op = []
def annos = []
def i = 0
obj.properties.findAll { prop ->
obj.getClass().declaredFields.find {
it.name == prop.key && annotClass in it.declaredAnnotations*.annotationType()
annos=it.declaredAnnotations
i++
if(annos)
op << annos[0] as Set
// println"Props ${annos[0]}"
}
}
op.each{ println "${it} and i is ${i}"}
}
// Then, define an instance of our class
MyClass a = new MyClass(fielda: 'tim', fieldc: 'dennisStar')
// And print the results of calling our method
println findAllPropertiesForClassWithAnotation(a, MyAnnot)
First of all you need to mark you annotation with: #Retention(RetentionPolicy.RUNTIME) to make it available for processing at runtime, so it will be:
#Retention(RetentionPolicy.RUNTIME)
#interface MyAnnot {
String used()
String type()
}
Then, used and type are not fields, nor properties but methods, so they must be invoked and get.
The script will be:
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Retention
#Retention(RetentionPolicy.RUNTIME)
#interface MyAnnot {
String used()
String type()
}
class MyClass {
#MyAnnot(used="Hey" ,type="There")
String fielda
#MyAnnot(used="denn", type="Ton")
String fieldc
}
def findAllPropertiesForClassWithAnotation( obj, annotClass ) {
def c = obj.getClass()
c.declaredFields.findAll { field ->
field.isAnnotationPresent(annotClass)
}.collect { found ->
def a = found.getAnnotation(annotClass)
[(a.used()): a.type()]
}.sum()
}
MyClass a = new MyClass(fielda: 'tim', fieldc: 'dennisStar')
println findAllPropertiesForClassWithAnotation(a, MyAnnot)
Mind that passing only a class of annotation is not sufficient, since you don't know the methods (used and type) to be invoked on the annotation. The following method will work only for MyAnnot class.
I have the following database table:
widget_types
------------
widget_type_id
widget_type_name
widget_type_alias
widget_type_description
This corresponds to the following Groovy entity class:
class WidgetType extends BaseLookupEntity {
Long id
String name
String alias
String description
}
In reality, WidgetType/widget_types really ought to be enums, because they are reference/lookup types with a small number of valid values:
RedWidget
SillyWidget
HappyWidget
BerserkingWidget
SausageWidget
For reasons outside the scope of this question, it is not really possible for me to OR/map the widget_types table to an enum. And so I have created a "helper enum":
enum WidgetTypeLookup {
Red,
Silly,
Happy,
Berserking,
Sausage
static WidgetTypeLookup toWidgetTypeLookup(WidgetType type) {
// TODO: ???
null
}
}
The idea here is that the JPA/OR layer will create WidgetType instances, but to be able to get real use out of them (type safety, etc.), I'd like to be able to convert them to WidgetTypeLookups:
// Inside some method...
WidgetType widgetType = getSomehowButStillNotSureWhichTypeItIs()
WidgetTypeLookup wtLookup = WidgetTypeLookup.toWidgetTypeLookup(widgetType)
switch(wtLookup) {
case Happy:
// etc...
}
So I'm struggling to find an efficient "Groovy way" of converting between the POGO type and the enum. Basically implementing the helper method. Any ideas?
I agree with the other answer that there might be better way to address your problem by improving OO design. Although I'll try to fit into your approach.
First - couldn't you just do it as follow and map the name as enum straight away?
class WidgetType extends BaseLookupEntity {
Long id
WidgetName name
String alias
String description
enum WidgetName {
Red,
Silly,
Happy,
Berserking,
Sausage
}
}
Second - the method you want to implement could be implemented like:
static WidgetTypeLookup toWidgetTypeLookup(WidgetType type) {
values().find {
it.name() == type.name
}
}
However:
the condition may need to be adjusted if names don't exactly match
you may need to handle somehow the case when there is no matching enum found
the name of the method should be rather something as fromWidgetType() then you'll have call like WidgetTypeLookup.fromWidgetType(widgetType) instead of redundant WidgetTypeLookup.toWidgetTypeLookup(widgetType)
Third - Even more groovy would be to implement custom type conversion as follows (I altered original classes names to reflect better what they are IMHO):
enum WidgetType {
Red,
Silly,
Happy,
Berserking,
Sausage
}
class WidgetTypeDetails {
Long id
String name
String alias
String description
Object asType(Class clazz) {
if (clazz == WidgetType) {
WidgetType.values().find {
it.name() == this.name
}
}
}
}
Then you can go like:
WidgetType widgetType = new WidgetTypeDetails(name: 'Red') as WidgetType
An enum is a limited number of fixed elements, unless that table have only fixed rows, Widget Red/Silly/etc should be subclasses.
You could implement the helper method inside the WidgetType class and operate specializations from an inner enum (though they can't reference the outer class)
class WidgetType {
Long id
String name
String alias
String description
enum Type {
RED,
SAUSAGE {
def install(container) {
"installing elongated component into $container"
}
},
SILLY,
HAPPY,
BERSERKING
def install(container) {
"installed ${name()} into $container"
}
}
Type getType() {
Type.values().find { it.name() == name }
}
}
red = new WidgetType(name: 'RED')
assert red.type.install("main container") ==
"installed RED into main container"
sausage = new WidgetType(name: 'SAUSAGE')
assert sausage.type.install("display") ==
"installing elongated component into display"
I think widget.install() is cooler and more OO (in a sense I don't need to pull the object guts to do something).
Another solution would be if WidgetType were an abstract class, and your ORM would instantiate the correct concrete type based on a specific value:
abstract class WidgetType {
Long id
String name
String alias
String description
abstract install(container)
enum Type {
RED(RedWidget),
SAUSAGE(SausageWidget),
}
static WidgetType from(type, properties) {
Type.values().find { it.name() == type }
.clazz
.newInstance(properties: properties)
}
}
class RedWidget extends WidgetType {
def install(container) { 'red installing into $container' }
}
class SausageWidget extends WidgetType {
def install(container) { 'elongated component installing into $container' }
}
The fake ORM:
class ORM {
def container = [
(1) : [
id: 1,
name: 'RED',
alias: 'my red alias',
description: 'this art red' ],
(2) : [
id: 2,
name: 'SAUSAGE',
alias: 'long component',
description: 'sausage component' ]
]
def get(id) {
container[it].with {
WidgetType.from(it.name, id)
}
}
}
Testing:
red = new ORM().get(1)
assert red.install('main') ==
'red installing into main'
sausage = new ORM().get(2)
assert sausage.install('display') ==
'elongated component installing into display'
I have a mock with a method that can accept all children of an abstract type.In a test,this method may be invoked by different children multiple times,and I want to check that it is invoked by specific type once like this:
1*mockObject.method(_) {SpecificType ct->
//check the ct's field value
}
the problem is that the closure always capture the first invocation due to:
mockObjectt.method(_)
can't distinguish different children types,so it match all invocations,then I tried another way like this:
1*mockObject.method({it instanceof SpecificType}) {SpecificType ct->
//check the ct's field value
}
but the problem of this way is that:
{SpecificType ct->
//Here,any check will return true,
//I think the checkings in here are ignored
}
is ignored always(I think I misused the groovy's closure here)
So question is:
Is there elegant way to capture the invocation by specific child type with specific values and ignore others?
What about: 1*mockObject.method({it instanceof SpecificType && it.field == fieldValue }) ?
Take a look at this sample code:
import spock.lang.Specification
class SimpleTestSpec extends Specification {
private static class A { }
private static class B extends A { }
private static class C extends B { }
private static class D extends A { }
private static class TestClass {
String foo(A value) {
return "A: ${value}"
}
String foo(B value) {
return "B: ${value}"
}
String foo(C value) {
return "C: ${value}"
}
String foo(D value) {
return "D: ${value}"
}
}
private static class ComponentClass {
private final TestClass testClass
ComponentClass(TestClass testClass) {
this.testClass = testClass
}
String foo(A value) {
return testClass.foo(value)
}
}
TestClass testClassMock = Mock(TestClass)
ComponentClass componentClass = new ComponentClass(testClassMock)
def setup() {
testClassMock.foo(_ as D) >> "DDD"
testClassMock.foo(_ as C) >> "CCC"
testClassMock.foo(_ as B) >> "BBB"
testClassMock.foo(_ as A) >> "AAA"
}
def "should use mocked DDD result"() {
when:
String result = testClassMock.foo(new D())
then:
result == "DDD"
}
def "should use mocked CCC result"() {
when:
String result = testClassMock.foo(new C())
then:
result == "CCC"
}
def "should use mocked BBB result"() {
when:
String result = testClassMock.foo(new B())
then:
result == "BBB"
}
def "should use mocked AAA result"() {
when:
String result = testClassMock.foo(new A())
then:
result == "AAA"
}
def "should record invocation based on dynamic type"() {
when:
componentClass.foo(new C())
then:
1 * testClassMock.foo(_ as C)
}
def "should return result associated with mocked invocation in runtime"() {
when:
String result = componentClass.foo(new D())
then:
result == "DDD"
}
}
Gist file: https://gist.github.com/wololock/c59151b67d4c9b0c0c8e
You can specify expected type of an argument using as casting operator. But there is one tricky part - you need to stub class behavior in particular order: from most specific to most general. Try to mix the order in setup() and then you will see that tests will start failing.
You can also make use of Groovy's dynamic method invocation - notice how ComponentClass behaves with injectest TestClass instance which was previously stubbed with expected behavior. You can also count the number of invocations associated with specific type even if external call was made through the most general type method.
I hope it will help you resolve your problem. Best!