#SpringBootTest runing with test-properties (test database) file - java

I had a project on "spring boot 2" and I want to test it.
Table:
#Entity
#Table(name = "Contract")
public class Contract extends ADBObjectWithID<ContractBean>
{
#NotBlank
#Size(max = 512)
private String name;
#Size(max = 2056)
private String comment;
#Override
public ContractBean toBean()
{
return new ContractBean(getId(), getName(), getComment());
}
}
Repository is CrudRepository<Contract, Long>:
Service:
#Service
public class ContractServiceImpl implements ContractService
{
private ContractRepository contractRepository;
public ContractServiceImpl(ContractRepository contractRepository)
{
this.contractRepository = contractRepository;
}
#Override
#Transactional
public Contract saveObject(ContractBean contractBean)
{
Contract contract;
if (contractBean.getId() == null)
{
contract = new Contract();
}
else
{
contract = findById(contractBean.getId()).orElseThrow(() -> new NullPointerException("Contract not found"));
}
contract.setName(contractBean.getName());
contract.setComment(contractBean.getComment());
return contractRepository.save(contract);
}
#Override
#Transactional
public void deleteObject(ContractBean contractBean)
{
}
#Override
public Optional<Contract> findById(Long id)
{
return contractRepository.findById(id);
}
}
I wanting to test "Service" layer and testing it in the test database. Parameters of the test database available in the "application-test.properties", but I running test, "SpringBoot" used the real database from "application.properties".
Test:
#RunWith(SpringRunner.class)
#SpringBootTest
public class ContractTest
{
#Autowired
private ContractService contractService;
#Test
public void createContract()
{
String name = "Contract name";
String comment = "Contract comment";
ContractBean contractBean = new ContractBean();
contractBean.setName(name);
contractBean.setComment(comment);
Contract contract = contractService.saveObject(contractBean);
Assert.assertEquals(name, contract.getName());
Assert.assertEquals(comment, contract.getComment());
contractBean = contract.toBean();
Assert.assertEquals(name, contractBean.getName());
Assert.assertEquals(comment, contractBean.getComment());
}
}
Pls, tell me, how do I switch to the test base? I trying #PropertySource("classpath:application-test.properties") and #TestPropertySource("classpath:application-test.properties"), but not work

Run with Spring Profile test.
-Dspring.profiles.active=test
You can add the default profile as test into your application.yml to pick it automatically.
spring:
profiles.active: test

Related

repoistory.save() getting invoked with invalid entry when unit testing

I'm using java validation API to validate fields in my Note class:
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "note")
public class Note {
#Id
#Column(name = "id", nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "date", columnDefinition = "DATE")
private LocalDate date;
#NotBlank(message = "Enter a topic")
#Column(name = "topic")
private String topic;
#NotBlank(message = "Content can't be empty")
#Column(name = "content")
private String content;
#Column(name = "type")
private NoteType noteType;
#NotNull
#ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
#JoinColumn(name = "user_id")
#JsonIgnore
private User user;
}
NoteService:
#Service
#AllArgsConstructor
public class NoteService {
#Autowired
private NoteRepository noteRepository;
#Autowired
private UserRepository userRepository;
public void addNote(#Valid Note note) {
note.setUser(getLoggedInUser());
if (validateNote(note)) {
noteRepository.save(note);
}
}
public List<Note> getNotes() {
return getLoggedInUser().getNotes();
}
public Note editNote(Note newNote, Long id) {
noteRepository.editNoteById(newNote, id);
return newNote;
}
public List<Note> getNotesByTopic(String topic) {
List<Note> notes = noteRepository.getNotesByTopicAndUser(topic, getLoggedInUser());
return notes;
}
public boolean validateNote(Note note) {
return validateNoteType(note.getNoteType())
&& note.getDate() != null;
}
public boolean validateNoteType(NoteType type) {
return type.equals(NoteType.NOTE)
|| type.equals(NoteType.SKILL);
}
public User getLoggedInUser() {
return userRepository.findByEmail(SecurityContextHolder.getContext().getAuthentication().getName());
}
}
Test:
#ExtendWith(MockitoExtension.class)
#ExtendWith(SpringExtension.class)
class NoteServiceTest {
#Mock
private NoteRepository noteRepositoryMock;
#Mock
private UserRepository userRepositoryMock;
#Mock
SecurityContext mockSecurityContext;
#Mock
Authentication authentication;
private NoteService noteService;
#BeforeEach
void setUp() {
noteService = new NoteService(noteRepositoryMock, userRepositoryMock);
Mockito.when(mockSecurityContext.getAuthentication()).thenReturn(authentication);
SecurityContextHolder.setContext(mockSecurityContext);
}
#Test
void shouldAddNote() {
LocalDate date = LocalDate.now();
Note note = new Note(0L, date, "test", "", NoteType.NOTE, null);
noteService.addNote(note);
Mockito.verify(noteRepositoryMock).save(note);
}
}
The field user in the Note class is annotated with #NotNull and I'm passing a null user to this note but the note is still getting saved. Same thing when I pass an empty string. Any idea why that is happening? I'm new to unit testing
I'm new to unit testing - your perfectly valid question has nothing to do with unit testing.
#NotNull does nothing on it own. Its actually a contract stating the following:
A data member (or anything else annotated with #NotNull like local variables, and parameters) can't be should not be null.
For example, instead of this:
/**
* #param obj should not be null
*/
public void MyShinyMethod(Object obj)
{
// Some code goes here.
}
You can write this:
public void MyShinyMethod(#NotNull Object obj)
{
// Some code goes here.
}
P.S.
It is usually appropriate to use some kind of annotation processor at compile time, or something that processes it at runtime. But I don't really know much about annotation processing. But I am sure Google knows :-)
You need to activate validation on you service class with the #Validated annotation so the validation of parameters kicks in.
#Service
#AllArgsConstructor
#Validated
public class NoteService {
...
See Spring #Validated in service layer and Spring Boot: How to test a service in JUnit with #Validated annotation? for more details.
If for some reason you need to manually perform the validation you can always do something like this:
#Component
public class MyValidationImpl {
private final LocalValidatorFactoryBean validator;
public MyValidationImpl (LocalValidatorFactoryBean validator) {
this.validator = validator;
}
public void validate(Object o) {
Set<ConstraintViolation<Object>> set = validator.validate(o);
if (!set.isEmpty()) {
throw new IllegalArgumentException(
set.stream().map(x -> String.join(" ", x.getPropertyPath().toString(), x.getMessage())).collect(
Collectors.joining("\n\t")));
}
}
}
So your noteRepository is Mocked, so you it's not actually calling save on your repository.
Mockito.verify(noteRepositoryMock).save(note);
All you are verifying here is that a call to save is made, not that it was successful.

How to test #RestController when all of the mapping return with ResponseEntity?

I need to do unit testing on a #RestController where every method returns with a ResponseEntity.
I have a CRUD repository to use but I don't know how can I test it with the ResponseEntities.
#RestController
#RequestMapping("/events")
public class EventController {
#Autowired
private EventRepository eventRepository;
#GetMapping("")
public ResponseEntity<Iterable<Event>> getAll() {
return ResponseEntity.ok(eventRepository.findAll());
}
#GetMapping("/{id}")
public ResponseEntity<Event> get(#PathVariable Integer id) {
Optional<Event> event= eventRepository.findById(id);
if (event.isPresent()) {
return ResponseEntity.ok(event.get());
} else {
return ResponseEntity.notFound().build();
}
}
#PostMapping("")
public ResponseEntity<Event> post(#RequestBody Event event) {
EventsavedEvent = eventRepository.save(event);
return ResponseEntity.ok(savedEvent);
}
.
.
.
So far so good , I can help you .
First of all, you must add unit test dependency.
After that you must examine below code.
Below code only consist for create.
Good luck.
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
#ActiveProfiles("dev")
public class EventControllerTests {
#Autowired
private TestRestTemplate testRestTemplate;
#Test
public void testCreateEvent() {
Event event = new Event(); // Your entity
event.setEventName("Test"); // Your entity attributes
URI location = testRestTemplate.postForLocation("http://localhost:8080/events", event);
Event event2 = testRestTemplate.getForObject(location, Event.class);
MatcherAssert.assertThat(event2.getEventName(), Matchers.equalTo(event.getEventName()));
}
}

Spring Boot Repository.save() methods do not work - No commit

I scan two QR code and try to get them from My QR Code Android Mobile App and save it with repository.save() in my Local db.
My app send List to Backend but don't insert to db. When I run localhost/8090, i don't get back anything.
In Browser show only this:
-Find Devices
-Device Code
-Device ID
Developer.java
#Entity
public class Developer {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id = 0;
private String deviceCode;
private String deviceId;
public Developer() {
super();
}
public Developer(String deviceCode, String deviceID)
{
super();
this.deviceCode = deviceCode;
this.deviceId = deviceId;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String deviceCode() {
return deviceCode;
}
public void set DeviceCode(String deviceCode) {
this.deviceCode;
}
public String deviceId() {
return deviceId;
}
public void set DeviceId(String deviceId) {
this.deviceId;
}
}
DeveloperRepository.java
import org.springframework.data.repository.CrudRepository;
public interface DeveloperRepository extends CrudRepository<Developer, Long> {
}
DeveloperController.java
#Controller
public class DevelopersController {
#Autowired
DeveloperRepository repository;
#RequestMapping(value = "/", method = RequestMethod.POST)
#ResponseBody
private String addDevices(Developer deviceCodeAndId) {
System.out.println("xyz!");
if (!repository.exists(deviceCodeAndId.getId())) {
repository.save(deviceCodeAndId);
return "successfully added " + deviceCodeAndId.getId();
}
return deviceCodeAndId.getId();
}
#RequestMapping(value = "/showall",method = RequestMethod.GET)
public String index(Model model) {
model.addAttribute("index",repository.findAll());
return "index";
}
}
deviceCodeAndID is Class from Android App which scanned with app!
index.html
Have you enabled transaction management. Even you use Spring boot data repository. You need to enable transaction management, else by default everything will be read mode. And for read mode there is no need to transaction. But when you do any operation that will change data in DB, you need to perform transaction management.
Use #EnableTransactionManagement on in application class, and #Transactional in DAO or service class
#SpringBootApplication
public class Application {
#Autowired
DeveloperRepository developerRepository;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
Here is my Application Class

Jackson's Access.WRITE_ONLY during test null

I'm currently playing around with Jackson's de/serialization features and I encountered a problem, I don't know how to solve.
During my test the #JsonProperty(access = JsonProperty.Access.WRITE_ONLY) annotation is ignored and it only shows null.
However with e.g. Postman everything works as expected.
I using just a Spring Boot Starter with Web Starter and Test Starter dependency.
Example Code:
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
#RestController
class JacksonExampleRestController {
#PostMapping("/api")
public void getResti(#RequestBody JacksonModel jacksonModel) {
System.out.println(jacksonModel.getId());
System.out.println(jacksonModel.getPassword());
}
}
class JacksonModel {
private String id;
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String password;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Test:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = DemoApplication.class)
public class DemoApplicationTests {
private MockMvc mockMvc;
#Before
public void setUp() {
JacksonExampleRestController jacksonExampleRestController = new JacksonExampleRestController();
mockMvc = MockMvcBuilders.standaloneSetup(jacksonExampleRestController)
.build();
}
#Test
public void testJackson() throws Exception {
JacksonModel jacksonModel = new JacksonModel();
jacksonModel.setId("id");
jacksonModel.setPassword("password");
mockMvc.perform(post("/api").
contentType(APPLICATION_JSON_UTF8)
.content(convertObjectToJsonBytes(jacksonModel)));
}
public static byte[] convertObjectToJsonBytes(Object object)
throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return mapper.writeValueAsBytes(object);
}
}
Is this the default behaviour and do I have to configure something in my test or is it something else I don't see right now?
Ignoring all the annotations can be problematic. To handle a finer configuration you can implement your custom JacksonAnnotationIntrospector:
public class IgnoreJacksonWriteOnlyAccess extends JacksonAnnotationIntrospector {
#Override
public JsonProperty.Access findPropertyAccess(Annotated m) {
JsonProperty.Access access = super.findPropertyAccess(m);
if (access == JsonProperty.Access.WRITE_ONLY) {
return JsonProperty.Access.AUTO;
}
return access;
}
}
Then, after instantiating the mapper:
mapper.setAnnotationIntrospector(new IgnoreJacksonWriteOnlyAccess());
add the line for your ObjectMapper:
mapper.disable(MapperFeature.USE_ANNOTATIONS);
I had the same problem, with a similar setup. The problem is in the test input data. Basically, writeValueAsBytes() will ignore the password while serializing exactly as instructed by the annotation.
Note that, Access.WRITE_ONLY basically means "SetterOnly" or "DeserializationOnly" not the other way around.

LazyInitializationException encountered when using load instead of get with Hibernate

I am using JPA, Hibernate and Spring MVC. In the controller class all the methods works greatly. When I test them in the web browser the public String getModuleFormation(long id) method, that returns an object, and it gives me the following error:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
as a root cause, but yesterday I tried it, and it worked without problem in the localhost:45045/GestionModules/detail/xx URL.
What could cause this problem?
My detail.jsp:
<c:if test="${!empty detailModule}">
${detailModule.idModule}
${detailModule.libModule}
</c:if>
POJO Class + JPA :
#Entity
#Table(name="ModuleFormation")
public class ModuleFormation {
private long idModule;
private String libModule;
public ModuleFormation() {
// TODO Auto-generated constructor stub
}
public ModuleFormation(String libModule) {
this.libModule = libModule;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "seqModule")
#SequenceGenerator(name="seqModule", sequenceName = "seqModuleFormation")
#Column(name="idModule")
public long getIdModule() {
return this.idModule;
}
public void setIdModule(long idModule) {
this.idModule = idModule;
}
#Column(name="libModule", nullable=false, length = 100)
public String getLibModule() {
return this.libModule;
}
public void setLibModule(String libModule) {
this.libModule = libModule;
}
}
DAO Class :
#Repository
public class ModuleFormationDAOImpl implements ModuleFormationDAO {
#Autowired
private SessionFactory sessionFactory;
public void ajouterModuleFormation(ModuleFormation module) {
sessionFactory.getCurrentSession().save(module);
}
public void supprimerModuleFormation(long idModule) {
ModuleFormation module = (ModuleFormation) sessionFactory.getCurrentSession().load(ModuleFormation.class, idModule);
if(module != null)
sessionFactory.getCurrentSession().delete(module);
}
public List<ModuleFormation> listModuleFormation() {
return sessionFactory.getCurrentSession().createQuery("from ModuleFormation")
.list();
}
public ModuleFormation getModuleFormation(long idModule) {
return (ModuleFormation) sessionFactory.getCurrentSession().load(ModuleFormation.class, idModule);
}
public void majModuleFormation(ModuleFormation module) {
sessionFactory.getCurrentSession().merge(module);
}
}
Service Class :
#Service
public class ModuleFormationServiceImpl implements ModuleFormationService {
#Autowired
private ModuleFormationDAO moduleDao;
#Transactional
public void ajouterModuleFormation(ModuleFormation module) {
moduleDao.ajouterModuleFormation(module);
}
#Transactional
public void supprimerModuleFormation(long idModule) {
moduleDao.supprimerModuleFormation(idModule);
}
#Transactional
public List<ModuleFormation> listModuleFormation() {
return moduleDao.listModuleFormation();
}
#Transactional
public ModuleFormation getModuleFormation(long idModule) {
return moduleDao.getModuleFormation(idModule);
}
#Transactional
public void majModuleFormation(ModuleFormation module) {
moduleDao.majModuleFormation(module);
}
}
Controller Class :
#Controller
public class ModuleFormationController {
#Autowired
private ModuleFormationService moduleService;
#RequestMapping("/module")
public String listModulesFormations(Map<String, Object> map) {
map.put("module", new ModuleFormation());
map.put("moduleList", moduleService.listModuleFormation());
return "module";
}
#RequestMapping(value = "/ajouter", method = RequestMethod.POST )
public String ajouterModuleFormation(#ModelAttribute("module")
ModuleFormation module,BindingResult result) {
moduleService.ajouterModuleFormation(module);
return "redirect:/module";
}
#RequestMapping(value = "/supprimer/{idModule}")
public String supprimerModuleFormation(#PathVariable("idModule")
long idModule) {
moduleService.supprimerModuleFormation(idModule);
return "redirect:/module";
}
#RequestMapping(value= "/detail/{idModule}")
public String getModuleFormation(#PathVariable("idModule")
long idModule,Map<String, Object> map) {
map.put("detailModule", moduleService.getModuleFormation(idModule));
return "/detail";
}
#RequestMapping(value= "/detail/modifier", method = RequestMethod.POST )
public String majModuleFormation(#ModelAttribute("detailModule")
ModuleFormation module, BindingResult result) {
moduleService.majModuleFormation(module);
return "detail/{idModule}";
}
}
The Javadoc on the Hibernate Session#load(Class, Serializable) method says:
Return the persistent instance of the given entity class with the given identifier,
assuming that the instance exists. This method might return a proxied instance that
is initialized on-demand, when a non-identifier method is accessed.
When you access a property on the object in your JSP the session which loaded the object has been closed.
Use Session#get(Class, Serializable) to ensure that you don't load a proxy.
Instead of sessionFactory.getCurrentSession().load(ModuleFormation.class, idModule), have you tried sessionFactory.getCurrentSession().get(ModuleFormation.class, idModule)?

Categories