How to make unit test with exception using JUnit5? - spring

I have UserService and there GET method which returns the object found by id. Let's say:
#Service
public class UserService {
#Autowired
UserRepository repository;
public User getUserById(Long id) {
return repository.findById(id)
.orElseThrow(() -> new UserNotFoundException(id));
}
}
I made simple test if the user is found:
#ExtendWith(MockitoExtension.class)
class UserServiceTest {
#Mock
private UserRepository userRepository;
#InjectMocks
private UserService userService;
#Test
public void whenGivenId_shouldReturnUser_ifFound() {
User user = new User();
user.setId(89L);
when(userRepository.findById(user.getId())).thenReturn(Optional.of(user));
User expected = userService.getUserById(user.getId());
assertThat(expected).isSameAs(user);
verify(userRepository).findById(user.getId());
}
}
Now I want to make a method, let's say: public void shouldThrowExceptionWhenUserDoesntExist(). How to make it in JUnit5? In JUnit4 I would make something like this:
#Test(expected = UserNotFoundException.class)
public void shouldThrowExceptionWhenUserDoesntExist() {
User user = new User();
user.setId(89L);
user.setName("Test Name");
given(userRepository.findById(anyLong())).willReturn(Optional.ofNullable(null));
userService.getUserById(user.getId());
}
Since this #Test(expected = UserNotFoundException.class) is outdated in JUnit5, I can't find anywhere what's the way in handling exception like this one in JUnit5.

Do like this
Assertions.assertThrows(UserNotFoundException.class, () -> {
userService.getUserById(1234);
});

You should use Assertions.assertThrows().
The assertThrows() method asserts that execution of the supplied executable block throws an exception of the expected type. It takes the following parameters.
static <T extends Throwable>T assertThrows(Class<T> expectedType, Executable executable)
static <T extends Throwable>T assertThrows(Class<T> expectedType, Executable executable, String message)
static <T extends Throwable>T assertThrows(Class<T> expectedType, Executable executable, Supplier<String> messageSupplier)

Related

How to verify method, which has transactional lines, in unit test for Spring Boot Service?

i Have following UserServiceImpl Service Class:
#Service #Transactional
public class UserServiceImpl implements UserDetailsService {
...
public void addRoleToUser(String username, String name) {
User user = this.userRepository.findByUsername(username);
Role role = this.roleRepository.findByName(name);
user.getRoles().add(role);
}
}
and it's corresponding test class:
public class UserServiceImplTest {
#Mock private UserRepository userRepository;
#Mock private RoleRepository roleRepository;
private AutoCloseable autoCloseable;
private UserServiceImpl userServiceImpl;
#BeforeEach
void setUp(){
autoCloseable = MockitoAnnotations.openMocks(this);
userServiceImpl = new UserServiceImpl(userRepository, roleRepository);
}
#AfterEach
void tearDown() throws Exception{
autoCloseable.close();
}
#Test
void testAddRoleToUser() {
... // user and role def. here
userServiceImpl.addRoleToUser(username, roleName);
// what should be here?
}
}
But how can I verify that user.getRoles().add(role); is invoked in unit test? Is using mockito, and ArgumentCaptor enough?
Thank you.
Since userRepository is a mock, you probably define a behaviour for the findByUsername call and return an object there. The object is created in the test method (in the "given" block), so you have access to it after calling the method (in the "then" block). You can simply verify if the object contains a given role like so (AssertJ ussage assumed):
assertThat(user.getRoles())
.contains(role);
roleRepository is also a mock, so the role object is probably also available in the test. If it was not the case, information about the role could be extracted from the asserted object(s) and verified (for example a name as a String).

#Autowired not wiring my repository - null pointer exception

#SpringBootApplication
public class ImportCSV {
#Autowired
private static PersonRepository personRepository; //does not seem to work
public static void main(String args[]) throws IOException {
SpringApplication.run(ImportCSV.class, args);
Person person = new Person();
// Add a bunch of setters for person
personRepository.save(person); //personRepository is null
}
}
public interface PersonRepository extends JpaRepository<Person, Long>{
}
#Data
#Entity
#Table(name = "Persons")
#NoArgsConstructor
public class Person {
/*Declare relevant fields here*/
}
Please see above code. 'personRepository' is null. How do I fix this? What am I doing wrong -- how does Autowire work?
Spring does not inject into static fields. So you have to remove the static keyword from the personRepository field. The compiler will complain then, that you cannot access a non-static field from a static method. To solve this, implement the ApplicationRunner interface, which defines a run method. In that method, you can run all the code, that should be run after the Spring Boot application is started, e.g. for initializing any data in the database etc.
That would be the proper Spring Boot way to execute code after the application is started.
For even more cleaner code then, you can create a separate class, that implements the ApplicationRunner interface and move the code there.
Example:
#SpringBootApplication
public class ImportCSV implements ApplicationRunner {
#Autowired
private PersonRepository personRepository; //does not seem to work
public static void main(String args[]) throws IOException {
SpringApplication.run(ImportCSV.class, args);
}
#Override
public void run(ApplicationArguments args) throws Exception {

Person person = new Person();
personRepository.save(person);
}
}

Spring batch + repository Testing

This is my reader that work in job and step i'u using a repository to get users(public interface UserRepository extends JpaRepository<User,Long>):
#Slf4j
public class Reader implements ItemReader<User> {
#Autowired
UserRepository userRepository;
private Iterator<User>userIterator;
#BeforeStep
public void before(StepExecution execution){
userIterator=userRepository.findAll().iterator();
}
#Override
public User read() {
if (userIterator != null && userIterator.hasNext()) {
User user=userIterator.next();
log.info("User-->"+user.toString());
return user;
} else {
return null;
}
}
}
This is my test class:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {SpringBatchApplication.class, BatchTestConfiguration.class})
public class SpringBatchApplicationTest {
#Autowired
private JobLauncherTestUtils testUtils;
#Autowired
private BatchConfig config;
#Test
public void testEntireJob() throws Exception {
final JobExecution result = testUtils.getJobLauncher().run(config.processJob(), testUtils.getUniqueJobParameters());
Assert.assertNotNull(result);
Assert.assertEquals(BatchStatus.COMPLETED, result.getStatus());
}
#Test
public void testSpecificStep() {
Assert.assertEquals(BatchStatus.COMPLETED, testUtils.launchStep("orderStep1").getStatus());
}
}
When i`m running my test i got a :
Caused by: java.lang.IllegalStateException: Cannot determine embedded database for tests. If you want an embedded database please put a supported one on the classpath.
What do i need to add to make determine of my database. Do i need to place application properties somewhere or something else?
There is how my test situate in project enter image description here

Static field injection in Spring Unit test

I am using JUnit4 in my application where I tried to test a UserService, the current test case is simple:
Create a user named 'admin' and save it to the database.
The other test case will rely on this user.
So I use the BeforeClass to insert the record, however I have to use the UserService to save the user, but spring does not support to inject static fields.
When I tried to create the UserService manually, I found that I have to fill the dependencies myself, I wonder if there is an alternative? Or there is something wrong with how I use JUnit?
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:spring/application-config.xml"})
public class UserServiceTest {
#Rule
public final ExpectedException exception = ExpectedException.none();
#Autowired
private UserService userService;
//#Autowired // sprint does not suport this
private static UserService us;
private static User u;
#BeforeClass
public static void before() {
us = new UserServiceImpl(); // this UserService instance can not be used, since I have to fill the dependencies manually
us.clear();
u = new User();
u.setUsername("admin");
u.setPassword("pass");
us.save(u);
}
#AfterClass
public static void after() {
userService.delete(u.getId());
}
#Test
public void testQuery() {
List<User> list = userService.find();
assertEquals(1, list.size());
}
#Test
public void testChangePassword() {
userService.changePassword(u.getId(), u.getPassword(), "newpass");
assertEquals("newpass", userService.findOne(u.getId()).getPassword());
exception.expect(ResourceNotFoundException.class);
userService.changePassword("1", "32", "ss");
exception.expect(ResourceNotFoundException.class);
userService.changePassword(u.getId(), "32", "ss");
}
}

Spring JUnit testing with #Autowired annotation

I´m having issues with my test cases after having introduced #Autowired in one of the classes under test.
My testcase now looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"/applicationContext.xml", "/spring-security.xml"})
public class StudentRepositoryTest extends AbstractDatabaseTestCase {
private StudentRepository studentRepository;
private CompanyRepository companyRepository;
private Student testStudent;
private Company testCompany;
#Before
public void setUp() {
studentRepository = new StudentRepository();
studentRepository.setJdbcTemplate(getJdbcTemplate());
testStudent = Utils.testStudentNoApplication();
}
#Test
....
}
StudentRepository now looks like this:
#Service
public class StudentRepository extends AbstractRepository<Student> {
...
private PasswordEncoder passwordEncoder;
private MailService mailService;
public StudentRepository() {
// TODO Auto-generated constructor stub
}
#Autowired
public StudentRepository(MailService mailService, PasswordEncoder passwordEncoder) {
this.mailService = mailService;
this.passwordEncoder = passwordEncoder;
}
Obviously this test case won´t work anymore.
But what changes do I need to make to the testcase for the #Autowired annotation to be picked up by the test case?
EDIT:
I´ve now updated my setUp() to this (I need the password encoder to avoid null password):
#Before
public void setUp() {
//studentRepository = new StudentRepository();
studentRepository = new StudentRepository(mock(MailService.class), ctx.getAutowireCapableBeanFactory().createBean(ShaPasswordEncoder.class));
studentRepository.setJdbcTemplate(getJdbcTemplate());
testStudent = Utils.testStudentNoApplication();
}
My testcase is now running OK, but my testsuite failes with a NullPointerException.
I´m guessing the ApplicationContext is not being Autowired when running the testsuite for some reason?
If you don't want to declare your StudentRepository in one of XML files referenced by #ContextConfiguration and autowire it into the test, you can try to use AutowireCapableBeanFactory as follows:
...
public class StudentRepositoryTest extends AbstractDatabaseTestCase {
...
#Autowired ApplicationContext ctx;
#Before
public void setUp() {
studentRepository = ctx.getAutowireCapableBeanFactory()
.createBean(StudentRepository.class);
...
}
...
}

Resources