In Groovy there is an @
operator which enables direct field access. However it looks like it won't work for fields declared in super class. Consider two Java (not Groovy) classes:
class Entity {
private Long id;
Long getId() {
return id;
}
}
class User extends Entity {
}
Then invoking direct access in Groovy
User user = new User();
user.@id = 1L
ends up with exception: groovy.lang.MissingFieldException: No such field: id for class User
When I try to use standard access user.id = 1L
I get groovy.lang.ReadOnlyPropertyException: Cannot set readonly property: id for class User
Is there any option to access field declared in super class?
You would probably need to declare the property as protected instead:
class Entity {
protected Long id;
Long getId() {
return id * 2;
}
}
class User extends Entity {
}
User user = new User();
user.@id = 1L
assert user.@id == 1L
assert user.id == 2L
This is a modified example for the direct access field operator.
You can access via regular Java reflection but I'm not sure how to make this more "Groovy".
User user = new User()
fields = user.getClass().superclass.declaredFields
idField = fields[0]
idField.accessible = true
idField.set(user, 2L)
println idField.get(user)
Private fields can't be accessed from the children classes (they aren't inherited). Although Groovy lets you access private fields easier than Java reflection, it still can't access fields which aren't exist.
Let me revive this question as I'm going through a similar thing recently. Here are a few thoughts...
As it's needed for tests perhaps a comparison by equals would do instead of checking/setting fields directly?
Pattern worth considering if the alternative is de-encapsulation. I mean, if there is no need to touch these fields in prod code ideally they shouldn't need being touched in tests either.
Entity
would have own fields based equals
and hashCode
implemented. Lombok's @EqualsAndHashCode
annotation can help with reducing the boilerplate.
Test goes then like expect: new Entity(id) == new Entity(id)
(assuming there is such constructor).
If equality is not the way to go package scope might give better encapsulation with an appropriate package structure (main/src/java):
package foo.bar
public class Entity {
Long id
}
Control over the id
access is more flexible now and independent from the test code. While User extends Entity
can have no access to the id
if placed in a different package, the test code can have a class accessing it, like for example (src/test/groovy):
package foo.bar
class EntityAccess {
private final Entity entity
EntityAccess(Entity entity) {
this.entity = entity
}
static EntityAccess access(Entity entity) {
new EntityAccess(entity)
}
Long getId() {
entity.id
}
void setId(Long id) {
entity.id = id
}
}
I'm sure the boilerplate can be reduced with some fancy Groovy AST annotations. The point is prod code can keep the fields hidden while in tests, with a static import for EntityAccess.access
, a field can be accessed without any Groovy hax0rs:
given: access(entity).id = 5
to set or expect: access(entity).id == 5
to assert on entity's id.
If possible though, I would've rather kept both the Entity
and User
immutable and tested by equality without altering the objects state.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.