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
CodePudding user response:
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 beshould 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 :-)
CodePudding user response:
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")));
}
}
}