I followed this tutorial (https://spring.io/guides/tutorials/react-and-spring-data-rest/#react-and-spring-data-rest-part-5) to experiment Spring Data REST and I wanted to test the CRUD with TestRestTemplate
.
- Add (postForEntity) is ok.
- Delete (delete) is ok.
- Read (getForEntity) is ok.
- Update (template.exchange(URL, HttpMethod.PUT, entity, String.class, ID)) only works when I don't have any relation with other entities... and I don't understand why.
Here's an example :
@Data
@Entity
public class Dojo {
private @Id @GeneratedValue Long id;
private String name;
private String location;
private Date created;
@OneToMany(mappedBy = "dojo")
@JsonIgnore
private List<Workshop> workshops;
private Dojo() {}
public Dojo(String name, String location) {
this.name = name;
this.location = location;
this.created = new Date();
this.workshops = new ArrayList<>();
}
//getters and setters ...
}
@Data
@Entity
public class Workshop {
private @Id @GeneratedValue Long id;
private String name;
@ManyToOne
private Dojo dojo;
private Workshop() {}
public Workshop(String name, Dojo dojo) {
this.name = name;
this.dojo = dojo;
}
}
So, I have a bidirectionnal 1:n relation between Dojo & Workshop. The @JsonIgnore annotation is here to avoid an infinite loop with the JSON Marshaller. The repositories are standard
public interface WorkshopRepository extends CrudRepository<Workshop, Long> {}
Now my test : I want to update a workshop. Sounds good, doesn't work.
@Test
public void testUpdateWorkshop() throws Exception {
final String DOJO_NAME="My Dojo";
final String DOJO_LOCATION="Liege";
final String WORKSHOP_NAME="Stuff";
final String HOST_PORT="http://localhost:8080";
//creation of a dojo
final Dojo DOJO = dojoRep.save(new Dojo(DOJO_NAME,DOJO_LOCATION));
//creation of a workshop
Workshop workshop = workshopRep.save(new Workshop(WORKSHOP_NAME,DOJO));
String newValue = "After Test";
System.out.println("before update");
System.out.println(workshop.getName()+" == "+WORKSHOP_NAME);
Long oldID = workshop.getId();
//As you can see I didn't modify the workshop object
HttpEntity<Workshop> entity = new HttpEntity<Workshop>(workshop);
ResponseEntity<String> response = template.exchange(HOST_PORT+"/api/workshops/"+oldID, HttpMethod.PUT, entity, String.class, oldID);
assert response.getStatusCodeValue() == 200;
//re-Get the updated workshop
workshop = workshopRep.findOne(oldID);
System.out.println("after update");
System.out.println(workshop.getName()+" == "+WORKSHOP_NAME);
// as I didn't set the newValue, it must fail and workshop.getName() must stay equal to "Stuff".
Assert.assertEquals("Update does not work",newValue,workshop.getName());
}
I run mvn clean test
and
before update
Stuff == Stuff
after update
My Dojo == Stuff
Failed tests:
WorkshopTests.testUpdateWorkshop:218 Update not work expected:<[After Test]> but was:<[My Dojo]>
So basically, I didn't change anything into my object but
- Result code is 200.
- It changed a property of my object.
- The name was modified to take the dojo.name value !
Just ... Why ?
More information :
- When I create a new workshop object with a new name (using the newValue ;-) ) and a new Dojo and try to update the existing workshop, the result is still the same. workshop.dojo unchanged and name copied from dojo.name. So basically, my update doesn't work.
I also try with
mockMvc
instead ofTestRestTemplate
like this.mockMvc.perform(put(HOST_PORT+"/api/workshops/"+oldID) .contentType(MediaType.APPLICATION_JSON_UTF8) .content(convertObjectToJsonBytes(workshop)) );
with the function
private byte[] convertObjectToJsonBytes(Object object) throws IOException { ObjectMapper mapper = new ObjectMapper(); System.out.println("log my face "); System.out.println(mapper.writeValueAsString(object)); return mapper.writeValueAsBytes(object); }
And the log seems to rightly parse my object before update...
{"id":1,"name":"Stuff","dojo":{"id":1,"name":"My Dojo","location":"Liege","created":1500799092330}}
but still doesn't work :(
When I run the app (mvn spring-boot:run), a GET on localhost:8080/api/workshops/1 returns
{ "name" : "Stuff", "_links" : { "self" : { "href" : "http://localhost-core:8080/api/workshops/1" }, "workshop" : { "href" : "http://localhost-core:8080/api/workshops/1" }, "dojo" : { "href" : "http://localhost-core:8080/api/workshops/1/dojo" } } }
- If I change the property name of my Dojo class by nameD and I update with a new name and a new Dojo (previously saved into DB), the name is updated but not the dojo.
To summarize my questions are :
- Just ... why ?
- What is the correct way to update an object like Workshop with a HTTP request ?
- What is the correct way to test this update ?
Thanks to all and have a nice day ! :-)