I have a JPARepository like this
#Repository
public interface RestaurantRepo extends JpaRepository<Restaurant, String> {
Optional<Restaurant> findByName(String name);
Optional<List<Restaurant>> findAllByMerchantId(String merchantId);
}
I then have a controller where I am initializing the Repository like this
#Autowired
RestaurantRepo restaurantRepo;
Then I have one method where I am using the Repository like this
#PreAuthorize("hasRole('ROLE_SUPER_ADMIN')")
#GetMapping("/disable")
public ResponseEntity<CustomResponse> disableUser(String username, boolean disable) {
Restaurant restaurant = restaurantRepo.findById(username).get();
restaurant.setDisabled(disable);
CustomResponse er = new CustomResponse();
er.setStatus("Successful");
if (disable) {
er.setMessage("Restaurant is disabled");
} else {
er.setMessage("Restaurant is enabled");
}
restaurantRepo.save(restaurant);
return new ResponseEntity<>(er, new HttpHeaders(), HttpStatus.OK);
}
Whenever I hit this api, it works fine.
Then I have another method like this
#PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN','ROLE_RESTAURANT')")
#GetMapping("/get")
private ResponseEntity<Restaurant> getRestaurant(Principal principal) {
String username = principal.getName();
System.out.println("username "+username); //prints the valid username
System.out.println("RestRepo "+restaurantRepo); //prints null
Restaurant restaurant;
Optional<Restaurant> opt = restaurantRepo.findById(username); //prints Exception here
if (opt.isPresent()) {
restaurant = opt.get();
return new ResponseEntity<>(restaurant, HttpStatus.OK);
} else {
restaurant = new Restaurant();
return new ResponseEntity<>(restaurant, HttpStatus.NOT_FOUND);
}
}
Here I get a NullPointerException in this line Optional<Restaurant> opt = restaurantRepo.findById(username);
The username is a valid username, and so when I print the restaurantRepo I get null.
I am confused as to why the variable is not null in one method while it is null in another method. Any help would be appreciated. Thanks
Im not sure. But try to change the access modifier in the second method to public.
Please refer this thread
Also use constructor injection instead of #Autowired annotation.
Related
I've followed an open Course on Spring web. Written some code to list all orders from a database and return them through a rest api. This works perfectly. Now I'm writing some code to give the ID of the order in the request, find 0 or 1 orders and return them. However, when there is no Order find with the given ID, a nullpointerexception is given. I can't find out what is causing this. I'm assuming the .orElse(null) statement. Please advise
Controller:
#RequestMapping("api/V1/order")
#RestController
public class OrderController {
private final OrderService orderService;
#Autowired
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
#GetMapping(path = "{id}")
public Order getOrderById(#PathVariable("id") int id) {
return orderService.getOrderById(id)
.orElse(null);
}
}
Service:
#Service
public class OrderService {
private final OrderDao orderDao;
#Autowired
public OrderService(#Qualifier("oracle") OrderDao orderDao) {
this.orderDao = orderDao;
}
public Optional<Order> getOrderById(int orderNumber) {
return orderDao.selectOrderById(orderNumber);
}
}
Dao:
#Override
public Optional<Order> selectOrderById(int searchedOrderNumber) {
final String sql = "SELECT \"order\", sender, receiver, patient, orderdate, duedate, paymentref, status, netprice from \"ORDER\" where \"order\" = ?";
Order order = jdbcTemplate.queryForObject(sql, new Object[] {searchedOrderNumber}, (resultSet, i) -> {
int orderNumber = resultSet.getInt( "\"order\"");
String sender = resultSet.getString("sender");
String receiver = resultSet.getString("receiver");
String patient = resultSet.getString("patient");
String orderDate = resultSet.getString("orderdate");
String dueDate = resultSet.getString("duedate");
String paymentRef = resultSet.getString("paymentref");
String status = resultSet.getString("status");
int netPrice = resultSet.getInt("netprice");
return new Order(orderNumber,sender,receiver,patient,orderDate,dueDate,paymentRef,status,netPrice);
});
return Optional.ofNullable(order);
}
For the Jdbcexception, use general query instead of the queryForObject, or use try/catch to convert the Jdbc related exception, else Spring itself will handle these internally using ExceptionTranslater, ExceptionHandler etc.
To handle optional case in controllers, just throw an exception there, for example PostController.java#L63
And handle it in the PostExceptionHandler.
Editing based on comment about stack trace
For your error please check - Jdbctemplate query for string: EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0
To solve problem associated with orderService.getOrderById(id) returning null you can return ResponseEntity.ResponseEntity gives you more flexibility in terms of status code and header. If you can change your code to return ResponseEntitythen you can do something like
#GetMapping(path = "{id}")
public ResponseEntity<?> getOrderById(#PathVariable("id") int id) {
return orderService
.getOrderById(id)
.map(order -> new ResponseEntity<>(order.getId(), HttpStatus.OK))
.orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
You can even write generic Exception handler using #ControllerAdvice and throw OrderNotFoundException as .orElse(throw new OrderNotFoundException);. Check more information here.
My question is : what repository will return when object not found in
junit tests.
I have test like this :
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest
public class CouponServiceTestSuite {
private final static String HOME_TEAM = "home team";
private final static String AWAY_TEAM = "away team";
#Autowired
private CouponService couponService;
#MockBean
private CouponRepository couponRepository;
#MockBean
private MatchRepository matchRepository;
#Test
public void shouldThrowException() {
//Given
//When
//Then
Assertions.assertThrows(BetWinnerException.class, () -> couponService.getCoupon(-6L));
I want to mock this like :
Mockito.when(couponRepository.findById(ArgumentMatchers.anyLong()).thenReturn(null);
My service class :
#Slf4j
#RequiredArgsConstructor
#Service
public class CouponService {
private final CouponRepository couponRepository;
private final MatchRepository matchRepository;
private final CouponMapper couponMapper;
public List<CouponDto> getCoupons() {
log.debug("Getting all coupons");
List<Coupon> couponList = couponRepository.findAll();
List<CouponDto> couponDtoList = couponMapper.mapToCouponDtoList(couponList);
log.debug("Return all coupons: {}", couponDtoList);
return couponDtoList;
}
public CouponDto getCoupon(Long couponId) {
log.debug("Getting coupon by id: {}", couponId);
Coupon coupon = couponRepository.findById(couponId).orElseThrow(()
-> new BetWinnerException(BetWinnerException.ERR_COUPON_NOT_FOUND_EXCEPTION));
CouponDto couponDto = couponMapper.mapToCouponDto(coupon);
log.debug("Return coupon: {}", couponDto);
return couponDto;
}
public CouponDto createCoupon() {
log.debug("Creating new coupon");
Coupon coupon = couponRepository.save(new Coupon());
CouponDto couponDto = couponMapper.mapToCouponDto(coupon);
log.debug("Return created coupon: {}", couponDto);
return couponDto;
}
public CouponDto addMatch(Long couponId, Long matchId) {
log.debug("Add match to the coupon: {}{}", matchId, couponId);
Coupon coupon = couponRepository.findById(couponId).orElseThrow(()
-> new BetWinnerException(BetWinnerException.ERR_COUPON_NOT_FOUND_EXCEPTION));
Match match = matchRepository.findById(matchId).orElseThrow(()
-> new BetWinnerException(BetWinnerException.ERR_MATCH_NOT_FOUND_EXCEPTION));
coupon.getMatchList().add(match);
Coupon updatedCoupon = couponRepository.save(coupon);
CouponDto couponDto = couponMapper.mapToCouponDto(updatedCoupon);
log.debug("Return coupon with added match: {}", couponDto);
return couponDto;
}
public boolean deleteCoupon(Long couponId) {
log.debug("Deleting coupon id: {}", couponId);
couponRepository.deleteById(couponId);
if (couponRepository.existsById(couponId)) {
log.debug("Coupon not deleted id: {}", couponId);
return false;
} else {
log.debug("Coupon deleted id: {}", couponId);
return true;
}
}
}
I thought that it returns null but when i do like this it returns NullPointerException. My service returns BetWinnerException when object is not found.
So what it will return ? How should i create this test ?
Test like this works properly but i dont want to use id = -6. I just want to mock it somehow.
You are mocking couponRepository but using couponService and as there is no code shown how that is initialised in your test, it is hard to tell, where the problem is.
Now that you updated your question, the answer is quite obvious:
in the service the code expects couponRepository.findById() to return an Optional so that it can throw the exception if that is empty
mocked beans are 'normal' Mockito mocks that try to return a useful result; for collection this is an empty collection, for objects this is null - usually
TIL that Mockito 2 will support what you expect: https://www.baeldung.com/mockito-2-java-8#return-default-values-for-optional-and-stream
this article also shows how to make Mockito 1 return the empty Optional
you tried to make it return null but you actually need Optional.empty()
Or did I misunderstand how you actually test it and what your problem is?
Do you get a NullPointerException in the service as I understood?
Or do you already use Mockito 2 and have another issue?
Please post your entire test class
Add #InjectMocks annotation above the #Autowired in the service you declared in your test and see if it solves the problem, however I don't think it will work considering you have your Repo declared as FINAL in your Service. If it`s not FINAL and is #Autowired, the InjectMocks would work fine for your mock. But if you really need this as FINAL, try this:
https://www.baeldung.com/mockito-final
Need to create default user profile when my Web Application [Spring MVC] starts:
--using postConstruct
-- Create default user on start up
-- do not create same user in next run
-- Default user like admin with some other information like First Name, Last Name,DOB etc. and it should be stored in database.
Of-course I have to follow proper mvc pattern like controller, model, service, repository, spring-configuration,spring-security configuration.
Please do not post code in spring-boot or JSON.
Any help will be highly appreciated and thanks in advance.
The question have simple solution after lot of research I have concluded that using #PostConstruct anything can be create at initializing phase of the application.
Hare is What I have found:
#Component
public class DbInit {
#Autowired
private UserRepository userRepository;
#PostConstruct
private void postConstruct() {
User admin = new User("admin", "admin password");
User normalUser = new User("user", "user password");
userRepository.save(admin, normalUser);
}
}
A reference from : https://www.baeldung.com/spring-postconstruct-predestroy
Thanks to this site a grate place to learn.
Create a class call SecurityUtil like below.
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import java.util.HashSet;
import java.util.Set;
public class SecurityUtil {
public static User getFirstTimeUser() {
User defaultUser = new User();
defaultUser.setActive(true);
defaultUser.setUserId("admin");
defaultUser.setFirstName("First Time");
defaultUser.setLastName("Admin");
defaultUser.setId(0L);
defaultUser.setPassword(new Md5PasswordEncoder().encodePassword("admin", Constants.SALT));
//Set<Role> roles = new HashSet<Role>();
Role role = new Role();
role.setId(0L);
role.setRoleName("ROLE_ADMIN");
//roles.add(role);
defaultUser.setRole(role);
return defaultUser;
}
Then call this method from Userservice class which is implemented from Spring's UserDetails.
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserFilter filter = new UserFilter();
filter.setUserId(username);
List<User> users = userDAO.find(filter);
if (users != null && users.size() == 1) {
return users.get(0);
} else {
if (userDAO.count() == 0) {
return getFirstTimeUser();
} else {
throw new UsernameNotFoundException("Username Not Found Exception");
}
}
}
I've the following model:
public class Users {
public static PlayJongo jongo = Play.current().injector().instanceOf(PlayJongo.class);
public static MongoCollection users() {
return jongo.getCollection("DB.users");
}
..
..
public static Users authenticate(String email, String password) {
Users user = users().findOne("{email: #, removed: false}", email).as(Users.class);
if (user != null) {
if (HomeController.checkPassword(password, user.password)) {
return user;
}
}
return null;
}
..
I use that in my controllers as:
public Result authenticate() {
DynamicForm requestData = Form.form().bindFromRequest();
String email = requestData.get("email").trim();
String password = requestData.get("password").trim();
Users user = Users.authenticate(email, password);
if (user == null) {
flash("danger", "Incorrect email or password.");
return redirect(routes.HomeController.login());
}
session("email", user.getEmail());
session("role", user.getRole());
session("fullname", user.getLastname() + " " + user.getFirstname());
session("id", user.getId().toString());
return redirect(routes.HomeController.index());
}
I tried a lot of combination to use injection with play-jongo without result. E.g.
#Inject
public PlayJongo jongo;
public MongoCollection users() {
return jongo.getCollection("DocBox.users");
}
I enter in a loop of static/non-static referenced context errors. If I remove all static declaration, I'm unable to call Users.method. If I try to inject Users to a controller
public class HomeController extends Controller {
#Inject
public Users users;
.
.
and try to call a Users method:
Users user = users.authenticate(email, password);
I receive a org.jongo.marshall.MarshallingException.
My brain is definitively goes overheating, someone can explain me how to use Injection with play-jongo?
I solve the problem. Now I've a UsersRepository that contains the methods that operate on the mongo collection (authenticate, addUser, et al.). And a Users object that only contains the actual data fields (firstname, lastname, email, etc.).
After that I can inject UsersRepository into my controller and use that one instance everywhere.
Thanks to Greg Methvin, Tech Lead - Play Framework
Suppose I have the following service object
public class UserService {
#Autowired
private UserDao dao;
public void addUser(String username, String password) {
if (username.length() < 8 ) {
username = username + "random" ; // add some random string
}
User user = new User(username, password);
dao.save(user);
}
}
I want to test the behaviour of the method "addUser" when username length is less 8 and when the username is more than 8 char. How do approach in unit test UserService.addUser(...), and verify it? I am aware using assert(), but the value "password" is not available outside the addUser(...) method.
I use JUnit and Mockito.
I came up a solution, after some re-visit the problem again after some months.
The idea is to observed the object user that is being passed to UserDao. We can inspect the value of the username by doing this, hence the unit test code:
#RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {
#Mock
private UserDao dao;
#InjectMock
private UserService service;
#Test
public void testAddingUserWithLessThan8CharUsername () {
final String username = "some";
final String password = "user";
doAnswer(new Answer<Object>() {
#Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
Object[] args = invocationOnMock.getArguments();
User toBeSaved = (User) args[0];
Assert.assertEquals(username + "random", toBeSaved.getPassword());
return null;
}
}).when(userDao).save(Matchers.any(User.class));
service.addUser(username, password);
}
}
Guillaume actually had the closest answer, but he answered using jMock. However, he gave me the idea on how to accomplish this, so I think he deserves some credit too.
You are testing side-effects, but fortunately, everything you need is passed to the dao.save(). First, create a UserDao (either with or without Mockito), then you can use ReflectionTestUtils to set the dao in the UserService, then you can test the values which are passed to dao.save().
Something like:
private class TestUserDao extends UserDao {
private User savedUser;
public void save(User user) {
this.savedUser = user;
}
}
#Test public void testMethod() {
UserService userService = new UserService();
TestUserDao userDao = new TestUserDao();
ReflectionTestUtils.setField(userService, "dao", userDao);
userService.addUser("foo", "bar");
assertEquals("foo", userDao.savedUser.username.substring(0, 3));
assertEquals("bar", userDao.savedUser.password);
}
Or you can user Mockito to mock out the Dao if you want.
Use a mocking framework. The example below uses JMock2, but it would be similar with EasyMock, Mockito, etc.
Also, you need to extract the username generation to something like UsernameGenmerator to be able to mock it. You need another specific test for the username generator.
private final Mockery mockery = new Mockery();
private final UserDao mockDao = mockery.mock(UserDao.class);
private final UsernameGenerator mockUserNameGenerator = mockery.mock(UsernameGenerator.class);
#Test
public void addUserUsesDaoToSaveUser() {
final String username = "something";
final String generatedUsername = "siomething else";
final String password = "a password";
mockery.checking(new Expectations() {{
oneOf(mockUsernameGenerator).generateUsername(username);
will(returnValue(generatedUsername));
oneOf(mockDao).save(new User(generatedUsername, password)); // assumes your User class has a "natueral" equals/hashcode
}});
UserService userService = new UserService();
userService.addUser(username, password);
}
And for UsernameGenerator you need test on length of the returned username:
#Test
public void leavesUsernameUnchangedIfMoreThanEightChars() {
final String username = "123456789";
final UsernameGenerator usernameGenerator = new UsernameGenerator();
assertEquals(username, userGenerator.generateUsername(username));
}
#Test
public void addsCharactersToUsernameIfLessThanEightChars() {
final String username = "1234567";
final UsernameGenerator usernameGenerator = new UsernameGenerator();
assertEquals(8, userGenerator.generateUsername(username).length());
}
Of course, depending on your "random" method, you may want to test its specific behaviour too. Apart from that, the above provide sifficient coverage for your code.
It would all depend on how your DAO's save method is implemented.
If you are actually storing to a hard-coded repository, then you will probably need to query the repository itself for the values you are intereseted in.
If you have an underlying interface which is called, then you should be able to set up a callback method and retrieve the actual value which is being saved.
I have never used Mockito so I couldn't give you exact code which does this article should address that:
Using Mockito, how do I intercept a callback object on a void method?
Consider extracting user name generation logic as dependency from UserService.
interface UserNameGenerator {
Strign generate();
}
Wire UserNameGenerator same as UserDao. And change the code to:
public class UserService {
#Autowired
private UserDao dao;
#Autowired
private UserNameGenerator nameGenerator;
public void addUser(String username, String password) {
if (username.length() < 8 ) {
username = nameGenerator.generate();
}
User user = new User(username, password);
dao.save(user);
}
}
Next create the default implementation of UserNameGenerator and move name generating logic there.
Now you can easily check behavior by mocking UserNameGenerator and UserDao.
To check use case when username is length is less than 8
String username = "123";
String password = "pass";
String generatedName = "random";
// stub generator
when(nameGenerator.generate()).thenReture(generatedName);
// call the method
userService.addUser(username, password);
// verify that generator was called
verify(nameGenerator).generate();
verify(userDao).save(new User(generatedName, password));
To check use case when username is length is greater than 8
String username = "123456789";
String password = "pass";
String generatedName = "random";
// call the method
userService.addUser(username, password);
// verify that generator was never called
verify(nameGenerator, never()).generate();
verify(userDao).save(new User(username, password));
Easiest way is to extract the part where you have the user name correction logic
if (username.length() < 8 ) {
username = username + "random" ; // add some random string
}
into a method and test the return value of that method.
public string GetValidUsername(string userName){
if (username.length() < 8 ) {
return username + "random" ; // add some random string
}
return username;
}
with this you can pass different types of username and test the behavior of your code.