How to test when bean is using a custom Lombok Builder - java

The following code works where my bean is using Lombok Builder annotations with a custom builder.
I am having issues with testing when I want to create this bean with values.
I can no longer use Setters (intentional) and can't just use .builder() due to the custom builder.
My question is, how do I create this bean in my tests? Note that I do want to use a builder and not looking to use Lombok's #Value annotation. The custom builder is necessary to work with Jackson.
One possibility I can think of is to add a constructor inside the custom builder which I hopefully don't need to do just for the sake of testing.
Please advice. Thanks.
Working Bean setup.
#Getter
#JsonDeserialize(builder = MyData.MyDataBuilder.class)
#Builder(builderClassName = "MyDataBuilder", toBuilder = true)
public class MyData {
#JsonProperty("some_key")
private String skey;
#JsonProperty("name")
private String name;
// needed to work with Jackson
#JsonPOJOBuilder(withPrefix = "")
static class MyDataBuilder {}
}
Test trying to create the Object. Following won't work as mentioned above.
MyData.builder()
.skey("12345")
.name("some_name")
.build();
or
MyData myData = new MyData();
myData.skey("12345");
myData.name("some_name");

You can customize the builder class to have any access level you want. Lombok makes it public by default if you don't customize it.
If the class itself and at least one constructor are public, it is possible to create instances from everywhere (not just the same package). But then there is no reason in most cases why the builder class should not be public, too: If the builder pattern is beneficial in its package, it will probably also be beneficial everywhere else.
If you don't want your class to be instantiated in other packages, then the builder should not be public (and also no constructor). However, then your test is also somehow flawed or in the wrong package.
So either make the custom builder class public, or change your test.

Related

How does Jackson #JsonProperty() work when used to annotate private fields?

Specifically I am wondering how when deserializing an object the deserializer could set a private field? Thinking of an example class like this:
public class MyClass {
#JsonProperty( "My String" );
private String myString;
}
If this is deserialized using objectMapper.readValue(json, MyClass.class); how does the resulting object have this field set if it is marked as private?
Calling Field.setAccessible(true) before reading or writing a value through reflection does the trick here.
For details see the corresponding javadoc: https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/AccessibleObject.html#setAccessible-boolean-
But use with care ;-)
Quite a few frameworks allow access to private fields in this manner by using Field.setAccessible(true). It allows the application to ignore the Java language visibility rules (i.e. private) and read or change the value via an instance of the Reflection API Field class.
A little more can be found in this question:
Java reflection - impact of setAccessible(true)
The short answer is that it can't normally. We use lombok to generate the getter/setter for the variables, but you can of course write your own. Jackson has the same visibility as your main code does, so a private field cannot be mapped without some public getter/setter OR configuring the object mapper like so... objectMapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);.
It wouldn't be able to serialize that either normally. You can use Lombok #Getter and #Setter on the class level so Jackson can work with myString, or put #JsonAutoDetect(fieldVisibility = Visibility.ANY) at the class level like below.
#JsonAutoDetect(fieldVisibility = Visibility.ANY)
public class MyClass {
#JsonProperty( "My String" );
private String myString;
}

How to add a Method and Field in a Lombok Like fashion to a Java Class?

I have the following case:
I have class with a field and a Annotation:
public class TestClass{
#Property
private Object testValue;
}
My goal is to create a Method for each annotated field that would create a "boilerplate code in a lombok like fashion" for the Property for each Value that is annotated that should look like this
public Object getTestValueProperty{
return testValue;
}
I can access the fields over reflection and anotations, And read the Type etc. But there is no Way to "Create" a Method Structure and adding it to the class on runtime.
I looked into the following: http://notatube.blogspot.com/2010/12/project-lombok-creating-custom.html
But I failed to get access the JavacAnnotationHandler to create the new Method. Since this article is 10 Years Old, I would assume this is not up to date anymore and that there are other ways to "inject" a method into a class.
If you could give me any resources or Ideas how to research this topic I would appriciate it.

Dagger 2: When to use constructor injections and when to use field injections?

I was kind of lazy and used to use almost entirely field injections. I was just providing empty constructor, put my #Inject fields I everything was looking nice and simple. However field injection have its trade-offs so I've devised some simple rules that help me to decide when to used field and when to use constructor injections. I will appreciate any feedback if there is mistake in my logic or if you have additional considerations to add.
First some clarification in order to be on the same page:
Constructor injection:
#Inject
public SomeClass(#Named("app version") String appVersion,
AppPrefs appPrefs) {...
Same with the field injection:
public class SomeClass {
#Inject
#Named("app version") String mAppVersion;
#Inject
AppPrefs appPrefs;
Rule 1: MUST use field injection if I don't control creation of the object (think Activity or Fragment in Android). If some (non-dagger aware) framework is creating my object and handles it to me I have no choice but to inject it manually after I receive the instance.
Rule 2: MUST use constructor injection if the class is/may be used in another project that does not use Dagger 2. If the other project(s) do not use Dagger they cannot use DI so the user have to create the object the "old" way using new.
Rule 3: PREFER constructor injection when working with class hierarchies because it is easier to create unit tests.
Clarification:
Considering the following structure that uses field injection:
package superclass;
public class SuperClass {
#Inject
HttpClient mHttpClient;
...
}
.
package differentpackage;
public class SubClass extends SuperClass {
public SubClass() {
}
}
When I am creating unit test for SubClass in directory test/java/differentpackage I have no choice but to bring up the entire DI infrastructure in order to be able to inject the HttpClient. In contrast, if I was using constructor injection like this:
public class SuperClass {
private final HttpClient mHttpClient;
#Inject
public SuperClass(HttpClient httpClient) {
mHttpClient = httpClient;
}
}
in my unit test I could simply:
HttpClient mockHttp = mock(HttpClient.class);
Subclass tested = new Subclass(mockHttp);
// tests
So basically now I am in the other extreme: I tend to rely mostly on constructor injections and use field injections only when 'Rule 1' applies.
The only 'problem' that I have with the constructor injects is that for 'end' classes constructors sometimes become quite overloaded with parameters and they look verbose and ugly like this:
#Inject
public ModelMainImpl(#Named("app version") String appVersion,
AppPrefs appPrefs,
LoginPrefs loginPrefs,
#ForApplication Context appContext,
NetworkInfoProvider networkInfoProvider,
AndroidEventPoster androidEventPoster,
Session session,
ForgeExchangeManager exchangeManager,
HttpFunctionality httpFunctionality,
#Named("base url") String baseUrl,
#Named("forge result producer") ResultProducer<ForgeExchangeResult> resultProducer
) {
Guys, what are your rules to choose between constructor and field injects? I am missing something, are there errors in my logic?
Use constructor injection. if you can't, use property injection.
Rule 1 seems ok, like decorations or attributes you can use Property(field) injection.
Rule 2 seems ok, because who uses your class they have to follow your constructor. They may not know they have to intilaize your property also.
Rule 3 It's not just good for unit test. It's good for applying Single Responsibilty. It's easier to see your object graph.Otherwise you will hide it with property.
If we come in your question, yes there a lot of parameters in your constructor. But the solution is not property injection. You can refactor your code and use aggregate services

Bloch's Builder Pattern with Spring

I have numerous constructors inside one of my Classes so I thought it would be a good idea to to implement Bloch's "Builder Pattern" (see http://www.informit.com/articles/article.aspx?p=1216151&seqNum=2) for one of the Classes (Spring Project).
It's quite possible, I have missed something as I am getting an "IllegalArgumentException" when running a Test Case against the Class. Does Spring allow for this type of stuff or shall I just opt for the conventional multiple constructor approach?
java.lang.IllegalArgumentException: No serializer found for class com.AllTweets$Builder and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: com.AllTweets["builder"])
at org.codehaus.jackson.map.ObjectMapper._convert(ObjectMapper.java:2502)
at org.codehaus.jackson.map.ObjectMapper.convertValue(ObjectMapper.java:2482)
at org.springframework.data.redis.hash.JacksonHashMapper.toHash(JacksonHashMapper.java:52)
at org.springframework.data.redis.hash.DecoratingStringHashMapper.toHash(DecoratingStringHashMapper.java:4
...
Class
public class AllTweets implements Serializable{
....
public Builder getBuilder() {
return new Builder();
}
public static class Builder {
private AllTweets build;
public Builder() {
build = new AllTweets();
}
public Builder isTweet(Tweet tweet){
build.id = tweet.getId();
return this;
}
public Builder isRetweet(Retweet retweet){
build.id = retweet.getId();
return this;
}
public AllTweets build(){
return build;
}
//Acessors
}
This exception doesn't have much to do with Spring. It only has to do with how Jackson automatically serializes your objects to JSON. You added a method getBuilder() to your class, and Jackson sees that as a rgular property of your bean that must be serialized, although it shouldn't be. So, choose another name for this method (like builder()), or annotate it with #JsonIgnore to make Jackson aware that this property must not be serialized.
That said, I really don't understand why this method is not static. You shouldn't have to create a new AllTweets object to be able to get a builder from it, to be able to build another AllTweets object. The method should thus be static, to be able to simply do
AllTweets result = AllTweets.getBuilder().isTweet(foo).isShare(bar).build();
And making it static will also solve the initial problem, because the getBuilder() method won't be considered as a bean property anymore by Jackson. See Google Guava for an example implementation of this pattern: note that the method is static, and is named builder().

Applying same annotation on multiple fields

Is it possible to apply same annotation on multiple fields (if there are many private fields and it just looks awkward to annotate them all.
So What I have is like
#Autowired private BlahService1 blahService1;
#Autowired private BlahService2 blahService2;
#Autowired private BlahService3 blahService3;
and so on
I tried the following but it won't work
#Autowired{
private BlahService1 blahService1;
private BalhService2 blahService2;
}
Some thing fancy with custom annotations perhaps?
No, but you could annotate your constructor rather than your fields. This would have the additional benefit to make your class more easily testable, by injecting mock dependencies when constructing the instance to test (which is the main reason why dependency injection is useful) :
#Autowired
public MyClass(BlahService1 blahService1, BlahService2 blahService2, BlahService3 blahService3) {
this.blahService1 = blahService1;
this.blahService2 = blahService2;
this.blahService3 = blahService3;
}
There's nothing built-in to the language that allows that kind of multi-annotations.
Many frameworks however opt to allow some kind of "default-annotation" on the class level.
For example, it would be possible for the framework to allow an #Autowired annotation at the class level to imply that each field should be auto-wired. That's entirely up to the framework to implement, however.
You can try extending AutoWired annotation interface with setting default values of fields, setting its target type to fields, and whenever it is not required you can turn it of by passing appropriate values to annotations on only those fields.

Categories