Spring Controller Unit Test - spring-boot

I'm using Spring Boot, Spring Data JPA, and JUnit 5.
All IDs in entities are automatically generated by DB (#GeneratedValue).
So all entities don't include any constructors and setter methods for initializing ID.
When I implement unit tests for controllers (#WebMvcTest),
all ids of entities are null.
The problem is that some endpoints respond id, so when I implement unit tests for them, they respond null, that is, fail the test.
How can I implement unit tests for controllers that respond ID generated by DB?

You can add Getter methods to class and then use ObjectMapper to set id field.
Employee employee = new ObjectMapper().readValue(""{\"id\": \"1\"}", Employee.class);
Other approach is to use reflection in JUnit to set that field.
Employee employee = new Employee();
Field field = Employee.class.getDeclaredField("id");
field.setAccessible(true);
field.set(employee, "1");
Other ways to use reflection: Access a private field for a junit test

Related

Custom Query REST Data with Panache

I am using REST Data with Panache for JAX RESTful Web Service generation by extending PanacheEntityResource. In Spring land there is query builder mechanism that allows the Spring Data repository to generate an SQL query based on the name and return type of the custom method signature.
I'm trying to achieve the same thing using Panache, so far unsuccessfully.
#ResourceProperties(path = "tasks", paged = false)
public interface TaskResource extends PanacheEntityResource<Task, UUID> {
List<Task> findByOrganizationId(#QueryParam("organizationId") UUID organizationId);
}
I want to pass the organazation ID as a query parameter, such that my request will be http://localhost:8080/tasks?organizationId=1e7e669d-2935-4d6f-8b23-7a2497b0f5b0, and my response will return a list of Tasks whose organization ID matches the one provided. Is there support for this functionality?
That functionality is currently not supported.
See https://quarkus.io/guides/rest-data-panache and https://quarkus.io/guides/spring-data-rest for more details

Spring Boot + MongoDB+create collection

I have a problem. After having created a Spring Boot project with Eclipse and configuring the application.properties file, my collections are not created, whereas after execution, the Eclipse console signals that the connection to MongoDB has been carried out normally. I don't understand what's going on. With MySQL we had the tables created so I expected the creation of the collections, but nothing.
Summary, I don't see my collection (class annoted #Document) in MongoDB after deployment.
New collection won't be created until you insert at least one document. Refer the document Create Collection
You could do this in two ways through Spring. I tested the instructions below in Spring 2.1.7.
With just a #Document class, Spring will not create a collection in Mongo. It will however create a collection if you do the following:
You have a field you want to index in the collection, and you annotate it as such in the Java class. E.g.
#Indexed(unique = true)
private String indexedData;
Create a repository for the collection:
public interface MyClassRepository extends MongoRepository<MyClass, String> {
}
If you don't need/want an index, the second way of doing this would be to add some code that runs at startup, adds a dummy value in the collection and deletes it again.
#Configuration
public class LoadDatabase {
#Bean
CommandLineRunner initDb(MyClassRepository repository) {
// create an instance of your #Document annotated class
MyClass myDocument = new MyClass();
myDocument = repository.insert(myDocument);
repository.delete(myDocument);
}
}
Make sure your document class has a field of the correct type (String by default), annotated with #Id, to map Mongo's _id field.

#autowire beans and #value properties after object mapper deserialized json

I am using spring framework.
I am using objectMapper to desiriale store.json file:
service:
objectMapper.readValue(new File(jsonFilePath), Store.class)
store.json:
{
"type": "Store",
"name": "myStore",
}
Store.class:
#Value("${store.size:1000}")
private Integer sroreSize;
#autowire
private storePersistency storePersistency;
public Store(#JsonProperty("name") String name) {
super(name);
}
I am trying find out how to #autowire beans and #value properties in store.class, beans and properties that exist in applicationContext.
In current example sroreSize and storePersistency still null.
I know that I can inject fields to object mapper and then use #JacksonInject annotation but I have a lot of field to inject - not a good option for me.
Custom desirializer also not a good option for me.
Is there any way not to use custom desirializer or not to inject every bean/property that I need in store.class?
Something that injects all the beans and properties and I simply can use it in Store.class.
So you want some Store fields like storePersistency and sroreSize to be initialized once at application startup (which is when Spring will setup the application context) and then at runtime create multiple different Store objects differing in some fields as name that are initialized by Jackson.
I suggest annotating Store with #Component to get Spring to initialize #Value and #Autowired fields. The #Scope annotation will cause a new independent Store instance to be created each time. Simplified example:
#Component
#Scope(SCOPE_PROTOTYPE)
class Store {
private String name;
#Value("${store.size:1000}")
private Integer sroreSize;
}
Then the key is method readerForUpdating where you can pass an existing instance of Store and Jackson will update that instead of creating a new one as usually:
Store store = context.getBean(Store.class);
objectMapper.readerForUpdating(store).readValue("{\"name\":\"myStore\"}");
Where context is a Spring ApplicationContext reference that I autowired in a test class. You don't need to use the return value of readValue in this case, just inspect the existing store variable and name will be updated.

Spring data hibernate serialize item properties according to method

I have a question.
Lets imagine, we have an entity of Hotel. That entity contains for example id, name, Country object reference, theme foto path and list of other photo patches.
I'm using the Spring boot JPA + jackson. So repositories are like...
#Repository
public interface HotelRepository extends CrudRepository<HotelEntity, Long> {
List<HotelEntity> findTop3ByCountryOrderByPriorityDesc(CountryEntity country);
}
Is there any possibility to annotate entity fields with some jackson to tell Spring boot- "Hey, if you use this repository method (for example that findTop3ByCountryOrderByPriorityDesc method), take just name, id and theme photo.
(And this approach would be used in all entities and methods). Something like
#JsonSerializeClasses({"top3Hotels", "hotels"})
String name;
And usage in repositorz
#JsonSerializeClassesUsage({top3Hotels})
List<HotelEntity> findTop3ByCountryOrderByPriorityDesc(CountryEntitycountry);
(This method would return list of hotel entities that contains just names).
Note: It can work in a "negative" way too. So if I annotate some field with some "class in class", this field would not be included in return object from method, that has not this "inner class" included in its annotation.
Is this possible in Spring data with jackson?
Or I really have to return objects and in cases i dont want some properities, I have to iterate over them and "null" it manually?
Thank you.

How to set #CreatedDate in the past (for testing)

My spring-data-jpa backend has a class that populates the (test) database with a lot of test data. The class usese the spring data repositories to create entities. All my entities have a field annotated with #CreatedData and the corresponding #EntityListeners(AuditingEntityListener.class) annotation on the model class. This works fine so far. dateCreated is automatically set correctly.
But when running Junit test I sometimes need to create a (test) object with a dateCreated in the past. How can I archive this? Only via plain JDBC?
In case you are using Spring Boot, you can mock dateTimeProvider bean used in EnableJpaAuditing annotation. Spring Data uses this bean to obtain current time at the entity creation/modification.
#Import({TestDateTimeProvider.class})
#DataJpaTest
#EnableJpaAuditing(dateTimeProviderRef = "testDateTimeProvider")
public class SomeTest {
#MockBean
DateTimeProvider dateTimeProvider;
...
It is necessary to define actual testDateTimeProvider bean, but it won't be used at all, as you will use mock instead.
You can write mockito methods afterwards as usual:
#Test
public void shouldUseMockDate() {
when(dateTimeProvider.getNow()).thenReturn(Optional.of(LocalDateTime.of(2020, 2, 2, 0, 0, 0)));
... actual test assertions ...
I found a way that works for me (using plain JDBC):
First I create my domain objects for testing with spring-data-jpa:
MyModel savedModel = myRepo.save(myModel);
That automatically fills the "dateCreated" with timestamp of "now". Since I need creation dates in the past for testing I manually tweak them with plain JDBC:
#Autowired
JdbcTemplate jdbcTemplate;
[...]
// This syntax is for H2 DB. For MySQL you need to use DATE_ADD
String sql = "UPDATE myTable SET created_at = DATEADD('DAY', -"+ageInDays+", NOW()) WHERE id='"+savedLaw.getId()+"'";
jdbcTemplate.execute(sql);
savedModel.setCreatedAt(new Date(System.currentTimeMillis() - ageInDays* 3600*24*1000);
Do not forget to also setCreatedAt inside the returned model class.
I do not know exact test case which you are using, but i can see few solutions:
create a mock object, once the dateCreated is called return date
month ago
maybe use in-mem db, populate it with date before test
go with AOP from the link provided in comments

Resources