JUNIT Test for partial update with java springboot mongodb - java

Hello I am new to JUnit test and Mockito and my question is how can I write a unit test for this custom partial update method in my customArticleRepository, which has no return parameter?
I have a POJO object named Article and I would like to partially update it. The REST endpoint PUT get a DTO Object Classification Mask with the specific updated value as request body, call the Service layer ArticleService, which call my CustomArticleRepository method.
this is my Article object
public class Article {
private String id;
#Field("_cls")
private String inheritance;
private String title;
private Date published;
private String content;
private String link;
private String summary;
private String description;
private Date updated;
private String primary;
private String[] secondary;
private String category;
private String[] categories;
private String person;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
private Date deleted_date;
private Boolean is_new;
private Boolean edited;
private Aws aws;
this is my DTO Object
public class ClassificationMask {
private String id;
private String title;
private String content;
private String primary;
private String[] secondary;
private Float sentiment_positive;
private Float sentiment_negative;
private Float sentiment_neutral;
private Float sentiment_mixed;
private String category;
private String person;
private Boolean is_new;
private Datasource datasource;
and this is my partialupdate method in the repository
public void partialUpdateMask(String id, ClassificationMask articleUpdate) {
Query query = new Query(where("id").is(id));
Update update = new Update();
if(articleUpdate.getPrimary() !=null) { update.set("primary", articleUpdate.getPrimary()); }
if(articleUpdate.getSecondary() !=null){ update.set("secondary", articleUpdate.getSecondary());}
if(articleUpdate.getCategory() !=null){ update.set("category", articleUpdate.getCategory());}
if(articleUpdate.getPerson() !=null){ update.set("person",articleUpdate.getPerson());}
if(articleUpdate.getIs_new() !=null){ update.set("is_new",articleUpdate.getIs_new()); }
update.set("edited",true);
mongoTemplate.updateFirst(query,update ,Article.class);
}

Testing void method we could have:
Method that modifies the status of the object under test: so you can verify that the state of the sut is consistent with your aspectative
Method doesn't modify the status of the SUT but, for example, writes data on the DB so you can verify that your DAO is invoked
This is the second case and you can have something like this:
Mockito.verify(mongoTemplate).updateFirst(query, update, Article.class);
In this manner you're testing that
the method really calls updateFirst
the method calss updateFirst using the right parameters

Related

Parsing to Object api fields like eMail, bNumber, iKindCd causes null value

I try to parse reponse from Api to my custom object.
Most fields are parsed correctly, except fields named like: eMail, iKindName, bNumber, uTypeName, iStartDT, iKindCd, uTypeCd (first small, second capital letter)
In fields like that I have null values if I use my custom object ResponseV2.
If I use Object type instead of ResponseV2 - fields eMail, iKindName, bNumber, uTypeName, iStartDT, iKindCd, uTypeCd are not null
Whats wrong with that fields (with pattern: first small and second capital letter) in my ReponseV2? Should I use some annotations, like #JsonProperty here?
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
#Data
public class ResultV2 implements Serializable {
private String country;
private String federationNumber;
private String city;
private String regon;
private String managerSurname;
private String countryCd;
private String pib;
private String institutionUuid;
private String lNumber;
private String siTypeName;
private String managerName;
private String ministryNumber;
private String eMail;
private String supervisingInstitutionID;
private String nip;
private String street;
private String www;
private String espAddress;
private String voivodeship;
private String id;
private String iKindName;
private String federationComposition;
private String lastRefresh;
private String postalCd;
private String bNumber;
private String panNumber;
private List<BranchesV2> branches;
private String krs;
private String supervisingInstitutionName;
private String iLiqStartDT;
private String eunNumber;
private String uTypeName;
private String institutionUid;
private String phone;
private String iStartDT;
private String iLiqDT;
private String name;
private String iKindCd;
private String siTypeCd;
private String yearPib;
private String uTypeCd;
private String dataSource;
private String voivodeshipCode;
private String status;
private String statusCode;
}
I have solution: it works if I have
#JsonProperty ("eMail") private String eMail;
instead of
private String eMail;
But why?
Lombok #Data annotation is generating setter method as
public void setEMail(final String eMail) {
this.eMail = eMail;
}
However json mapper expects the setter method to be with lower case letter e like in example below, so it could not find the method.
public void seteMail(final String eMail) {
this.eMail = eMail;
}
Just use JsonProperty like #JsonProperty("eMail") on those fields. It would work fine.
Here is a detailed explanation on why it work like this: Why does Jackson 2 not recognize the first capital letter if the leading camel case word is only a single letter long?

Why in my code do I have this inheritance problem? Create an object passing a specialized type instead of declared abstract super type

Working on a Java application I am finding the following problem related to inheritance.
I have the following situation:
1) I defined an abstract class named TrendFromExcelAbstract. This class contains some basic fields common to other classes which extend it and represent different tabs of an Excel file (but this is not important now):
public abstract class TrendFromExcelAbstract {
private Long id;
private String date;
private String time;
private String excelDocumentName;
private String excelDocumentSheet;
public TrendFromExcelAbstract() {
super();
}
public TrendFromExcelAbstract(Long id, String date, String time, String excelDocumentName,
String excelDocumentSheet) {
super();
this.id = id;
this.date = date;
this.time = time;
this.excelDocumentName = excelDocumentName;
this.excelDocumentSheet = excelDocumentSheet;
}
................................................................
................................................................
GETTER AND SETTER METHODS
................................................................
................................................................
}
Then I have a class named CompVibrAndTempDTO extending the previous abstract class:
public class CompVibrAndTempDTO extends TrendFromExcelAbstract {
// Temperature reading point
private String tempReadingPointA;
private String tempReadingPointB;
private String tempReadingPointC;
private String tempReadingPointD;
private String tempReadingPointE;
private String tempReadingPointF;
private String tempReadingPointG;
private String tempReadingPointH;
private String tempReadingPointI;
private String tempReadingPointJ;
private String tempReadingPointK;
private String tempReadingPointL;
private String tempReadingPointM;
private String tempReadingPointN;
private String tempReadingPointO;
private String tempReadingPointP;
// Vibration reading point
private String vibrReadingPointA;
private String vibrReadingPointB;
private String vibrReadingPointC;
private String vibrReadingPointD;
private String vibrReadingPointE;
private String vibrReadingPointF;
private String vibrReadingPointG;
private String vibrReadingPointH;
private String vibrReadingPointI;
private String vibrReadingPointJ;
private String vibrReadingPointK;
private String vibrReadingPointL;
public CompVibrAndTempDTO() {
super();
}
public CompVibrAndTempDTO(Long id, String date, String time, String excelDocumentName, String excelDocumentSheet,
String tempReadingPointA, String tempReadingPointB, String tempReadingPointC, String tempReadingPointD,
String tempReadingPointE, String tempReadingPointF, String tempReadingPointG, String tempReadingPointH,
String tempReadingPointI, String tempReadingPointJ, String tempReadingPointK, String tempReadingPointL,
String tempReadingPointM, String tempReadingPointN, String tempReadingPointO, String tempReadingPointP,
String vibrReadingPointA, String vibrReadingPointB, String vibrReadingPointC, String vibrReadingPointD,
String vibrReadingPointE, String vibrReadingPointF, String vibrReadingPointG, String vibrReadingPointH,
String vibrReadingPointI, String vibrReadingPointJ, String vibrReadingPointK, String vibrReadingPointL) {
........................................................................................................................
........................................................................................................................
........................................................................................................................
}
........................................................................................................................
........................................................................................................................
GETTER AND SETTER METHODS
........................................................................................................................
........................................................................................................................
}
Then I have another DTO class named ExcelTabGeneralInfoDTO like this:
public class ExcelTabGeneralInfoDTO {
private String excelDocumentName;
private String excelSheetName;
private List<TrendFromExcelAbstract> trendOfTabList;
public ExcelTabGeneralInfoDTO() {
super();
}
public ExcelTabGeneralInfoDTO(String excelDocumentName, String excelSheetName,
List<TrendFromExcelAbstract> trendOfTabList) {
super();
this.excelDocumentName = excelDocumentName;
this.excelSheetName = excelSheetName;
this.trendOfTabList = trendOfTabList;
}
..................................................................
..................................................................
GETTER AND SETTER METHODS
..................................................................
..................................................................
}
As you can see this class contains this list field:
private List<TrendFromExcelAbstract> trendOfTabList;
The idea is that this field represents a list of any objects that derive from TrendFromExcelAbstract
The problem is that in another class I am trying to do something like this:
public List<ExcelTabGeneralInfoDTO> getCompVibrAndTempTab() {
List<CompVibrAndTempDTO> tabTrendList = excelRepo.findCompVibrAndTempTab();
String excelDocumentName;
String excelSheetName;
if(tabTrendList.size() >=0 ) {
excelDocumentName = tabTrendList.get(0).getExcelDocumentName();
excelSheetName = tabTrendList.get(0).getExcelDocumentSheet();
}
ExcelTabGeneralInfoDTO result = new ExcelTabGeneralInfoDTO(excelDocumentName, excelSheetName, tabTrendList);
return result;
}
So basically at this line:
ExcelTabGeneralInfoDTO result = new ExcelTabGeneralInfoDTO(excelDocumentName, excelSheetName, tabTrendList);
I am trying to create a new ExcelTabGeneralInfoDTO, passing to it the tabTrendList list having type List.
I get the following error message:
Description Resource Path Location Type
The constructor ExcelTabGeneralInfoDTO(String, String, List<CompVibrAndTempDTO>) is undefined ExcelServiceImpl.java /energy-prg-be/src/main/java/com/springboot/excelapi/services line 425 Java Problem
Description Resource Path Location Type
Type mismatch: cannot convert from List<ExcelTabGeneralInfoDTO> to List<CompVibrAndTempDTO> ExcelResource.java /energy-prg-be/src/main/java/com/springboot/excelapi/resources line 167 Java Problem
My idea is that I can use the more general type (ExcelTabGeneralInfoDTO) and then pass the child type (CompVibrAndTempDTO). But it seems that my assumption is false. What is wrong? What am I missing? Why can't I do something like this? How can I fix it?
ExcelTabGeneralInfoDTO does not have a defined constructor for List<CompVibrAndTempDTO>, only for List<TrendFromExcelAbstract>. Try declaring tabTrendList as a List<TrendFromExcelAbstract> and see if it works.

I want to remove few fields from JSON result field T result. I am not able to to delete specific fields from T result

As per code given i want to remove few fields from in ResultJSON.
In field I am going pass list of ResultBean.
I am setting fields in result field like resultJson.setResult(ResultBeanList)
Note: ResultBean is not a JSON object. It simple Java bean.
Hope You understand my issue.
public class ResultJson<T> {
private Integer success;
private T result;
private String message;
private String errorCode;
}
public class ResultBean implements Serializable {
private static final long serialVersionUID = 1L;
private String customerName;
private String customerCode;
private String customerAlias;
private String pOR;
private String pOD;
}
We can skip/remove fields in JSON but question how can we delete fields from inside of JSON fields.

Hibernate #PreUpdate: check what has been changed

Question: How to check which fields has been changed inside method annotated with #PreUpdate?
OPTIONAL: if the answer to the question above is "It's impossible, than maybe there are another ways to solve my problem"
I want automatically update modified Tourist's field each time we change something in it.
Except the situation when we modify only location. Means if we change location only - it should be persisted, but modified mustn't be changed.
Already present code:
#Entity
public class Tourist {
private long id;
private String firstName;
private String lastName;
private Date created;
private Date modified;
private String location;
#PreUpdate
public void preUpdate() {
modified = new Date(); //PROBLEM : change modified even if only location field has been changed!
}
....
}
Updated: After some investigations I found that I can solve it with help of interceptors (extend EmptyInterceptor):
public class TouristInterceptor extends EmptyInterceptor{
Session session;
private Set updates = new HashSet();
public void setSession(Session session) {
this.session=session;
}
public boolean onFlushDirty(Object entity,Serializable id,
Object[] currentState,Object[] previousState,
String[] propertyNames,Type[] types)
throws CallbackException {
if (entity instanceof Tourist){
if (somethingChangedExceptLocation())
updates.add(entity);
}
return false;
}
But disadvantage of this approach is to intercept everything when you need to intercept the single entity.
Updated Questions:
How to intercept only Tourist entity flush calls?
Is that possible to do the same with help of events? Means PreUpdateEvent which contains new and old state
There is a simple non-JPA solution which is as follows but which which does have some repetitive code but is a solution when you do not have too many fields:
#Entity
public class Tourist {
private long id;
private String firstName;
private String lastName;
private Date created;
private Date modified;
private String location;
public void setFirstName(String firstName){
if(! this.firstName.equals(firstName){
modified = new Date();
}
this.firstName = firstName;
}
public void setLastName(String lastName){
if(! this.lastName.equals(lastName){
modified = new Date();
}
this.lastName= lastName;
}
}
Otherwise I would go with saving the previous state on load as suggested in another answer but in a slightly cleaner way.
#Entity
public class Tourist {
private long id;
private String firstName;
private String lastName;
private Date created;
private Date modified;
private String location;
#Transient
private Tourist previousState;
#PostLoad
public void setPreviousState() {
previousState = new Tourist();
//copy fields
}
#PreUpdate
public void preUpdate() {
if (isModified()) {
modified = new Date();
}
}
private boolean isModified() {
boolean modified = false;
if (!firstName.equals(previousState.firstName) {
modified = true;
}
//check other fields
return modified;
}
}
I really encourage you not to do this logic in your Entity !
You should decide whether to change the modified or not at your business logic.
I mean, when you are changing only the location call merge only. And whenever you are changing something else call the <Tourist_instance>.setModified(new Date()); before you call the merge.
To verify if anything else got changed, I suggest having a transient boolean field in your Entity that you set to true whenever you change something else other than location (the idea in the comment won't be sufficient to test all the fields, only if you create a transient field to remember previous value of each one of them)
You will then test this boolean in your preUpdate method
But I highly don't recommend this workaround.
Or, another highly unrecommended way :
#Entity
public class Tourist {
private long id;
private String firstName;
private String lastName;
private Date created;
private Date modified;
private String location;
#Transient
private String firstNamePrevious;
#Transient
private String lastNamePrevious;
#Transient
private Date createdPrevious;
#Transient
private Date modifiedPrevious;
#Transient
private String locationPrevious;
#PreUpdate
public void preUpdate() {
if(!onlyLocationGotChanged()){
modified = new Date();
}
}
....
#PostLoad
public void postLoad(){
//Set all previous fields to actual values
firstNamePrevious = firstName;
lastNamePrevious = lastName;
//...etc.
}
}
Although it is much late, however I enountered a problem where I required to apply update security on certain fields in entity. I solved it using Reflections. Have a look at this thread. spring security for certain entity variables

Is there any way to avoid duplicating attributes in this code?

Hi i want to know if exist a way to avoid duplicate code in this code. Now i have an action class named CustomerAction this class handle the behaviour of the request (it's like a controller) and i have a CustomerPOJO with attributes like id, name, last_name etc. Now i have to add attributes to CustomerAction to handle the data submited from the form. Is there any way to bypass the action with my CustomerPOJO ?
public class CustomerAction {
private String nombre;
private String apellido;
private String dni;
private String fechaNac;
private String obraSocial;
private String nroAsociado;
private String plan;
private String password;
private String email;
private String telParticular;
private String telCelular;
private static final Log log = LogFactory
.getLog(CustomerAction.class);
public String execute() throws Exception {
if ("cancelar".equals(this.getAccion())) {
log.debug("Executing 'cancelar' action");
return "login";
}
if ("registro".equals(accion)) {
log.debug("Executing 'registro' action");
IReferenceDataBusinessDelegate ud = new ReferenceDataBusinessDelegate();
ud.signCustomer(this.getNombre(), this.getApellido(),
this.getDni(), this.getCorreo(), this.getContrasena());
return "login";
}
}
public class Customers implements java.io.Serializable {
private long id;
private String dni;
private String name;
private String lastName;
private String email;
private String password;
private String phone;
private String cellphone;
private Date birthDate;
private Date creationDate;
private Date lastAccessDate;
private byte active;
private Set<Profesionales> profesionaleses = new HashSet<Profesionales>(0);
private Set<Pacientes> pacienteses = new HashSet<Pacientes>(0);
public Customers() {
}
}
Yes, use ModelDriven, and use a Customers as the model.
http://struts.apache.org/2.x/docs/model-driven.html
You'll need to make sure the "modelDriven" interceptor is in your stack.
How/where to initialize the model depends on your particular usage scenario; you can do it in a getter as shown in the docs, in a prepare() method if you need to reload it from the DB, etc.
I'm not sure what you mean by "bypass the action."
Please note that the ad-hoc dispatch mechanism implemented here with the accion parameter duplicates functionality provided by Struts 2 using the method attribute of the action configuration. I don't recommend using ad-hoc dispatch mechanisms as it makes understand program flow more difficult than necessary.

Categories