NullPointerException when Spring SecurityContextHolder is called - java

I have been trying to write a test case for the following line of code but I keep getting java.lang.NullPointerException, I have tried to follow/replicate what others have suggested here Unit testing with Spring Security but I have had no luck. Can someone please help me better identify or give me a hint what I need to do. (I'm using mockito for this)
Code:
if (SecurityContextHolder.getContext().getAuthentication().getPrincipal().equals(user)) {
continue;
}
Test case:
#Test
public void testExpireAllSession() throws Exception {
SecurityContext securityContext = Mockito.mock(SecurityContext.class);
Mockito.when(securityContext.getAuthentication().getPrincipal().equals(any(Object.class))).thenReturn(false);
SecurityContextHolder.setContext(securityContext);
controller.theMEthodUnderTest();
}
..

There are 2 problems with your test :
You must mock each "level" of method calls, you should mock :
SecurityContext.getAuthentication()
Authentication.getPrincipal()
Principal.equals()
But, you can't mock .equals(), see Mockito FAQ - limitations and Mockito - Issue 61.
You have to design your code/test differently. For example, pass a 'user' principal to your method arguments, and make Authentication.getPrincipal() return another one (they will be different, thus making the equals return false) :
Code
public void theMethod(Principal user) {
...
if (SecurityContextHolder.getContext().getAuthentication().getPrincipal().equals(user)) {
continue;
}
...
}
Test
#Test public void testController() {
SecurityContext securityContext = Mockito.mock(SecurityContext.class);
Authentication authentication = Mockito.mock(Authentication.class);
Principal principal1 = Mockito.mock(Principal.class);
Principal principal2 = Mockito.mock(Principal.class);
Mockito.when(authentication.getPrincipal()).thenReturn(principal1);
Mockito.when(securityContext.getAuthentication()).thenReturn(authentication);
SecurityContextHolder.setContext(securityContext);
new Controller().theMethod(principal2);
}

Few important things I did to make this work and hope this helps others as well.
Used the #InjectMocks :
#InjectMocks
private static YourMainController controller;
Mocked the dependencies which would get added to the main mock above:
#Mock
SecurityContext securityContextMocked;
#Mock
Authentication authenticationMocked;
#Mock
Principal principal1;
Modified the test to look like this and that made it work nicely.
#Test
public void testExpireAllSession() throws Exception {
List mySessions = new ArrayList<>();
Object principal="";
Date aDate = new Date();

SessionInformation sessionInformation = new SessionInformation(principal,”100000”,aDate);
mySessions.add(sessionInformation);
allUsers.add("Mike");
when(authenticationMocked.getPrincipal()).thenReturn(principal1);
when(securityContextMocked.getAuthentication()).thenReturn(authenticationMocked);
SecurityContextHolder.setContext(securityContextMocked);
when(sessionRegistryMocked.getAllSessions(allUsers,false)).thenReturn(sessions);
when(sessionRegistryMocked.getAllPrincipals()).thenReturn(allUsers);
controller.expireAllSession();
verify(sessionRegistryMocked).getAllPrincipals();
 

}

Related

Mockito mock returning Null value from repository

I'm trying to test my service implementation with JUNIT5. I don't quite understand whats going wrong but my UserRepository doesn't seem to be returning a value.
I have tested to see if the UserRepository has been used with:
verify(repository, times(1)); And the response was "Wanted but not invoked...Actually, there were zero interactions with this mock"
Here is the Test Class:
#ExtendWith(MockitoExtension.class)
public class UserServiceTest {
#Mock
UserRepository repository;
#InjectMocks
UserServiceImpl userServiceImpl;
UserModel inputUserModel;
#BeforeEach
public void setUp() throws Exception {
inputUserModel = new UserModel();
inputUserModel.setEmail("testemail#gmail.com");
inputUserModel.setFirstName("john");
inputUserModel.setLastName("doe");
inputUserModel.setPassword("test");
inputUserModel.setMatchPassword("test");
User inputUser = User.builder()
.email(inputUserModel.getEmail())
.firstName(inputUserModel.getFirstName())
.lastName(inputUserModel.getLastName())
.password(inputUserModel.getPassword())
.timeCreated(Timestamp.valueOf(LocalDateTime.now(ZoneId.systemDefault()))).userID(1)
.build();
User outputUser = User.builder().
email(inputUserModel.getFirstName()).
password(inputUserModel.getLastName()).
lastName(inputUserModel.getFirstName())
.firstName(inputUserModel.getFirstName())
.timeCreated(Timestamp.valueOf(LocalDateTime.now(ZoneId.systemDefault()))).userID(1).build();
Mockito.when(repository.save(inputUser)).thenReturn(outputUser);
}
#Test
public void whenSaveUser_ThenUserHasID(){
Assertions.assertEquals(1, userServiceImpl.saveUser(inputUserModel).getUserID());
}
}
The error that I am getting:
org.mockito.exceptions.misusing.PotentialStubbingProblem:
Strict stubbing argument mismatch. Please check:
- this invocation of 'save' method:
repository.save(
User(userID=0, email=testemail#gmail.com, timeCreated=2022-12-14 03:18:24.0435578, password=test, firstName=john, lastName=doe)
);
-> at com.jschwery.securitydemo.service.Implementations.UserServiceImpl.saveUser(UserServiceImpl.java:37)
- has following stubbing(s) with different arguments:
1. repository.save(
User(userID=1, email=testemail#gmail.com, timeCreated=2022-12-14 03:18:24.0235563, password=test, firstName=john, lastName=doe)
);
My Service Class that I'm creating the test for:
public class UserServiceImpl implements UserService {
UserRepository userRepository;
#Autowired
public UserServiceImpl(UserRepository repository){
this.userRepository = repository;
}
#Override
public User saveUser(UserModel userModel) {
if(!Objects.equals(userModel.getPassword(), userModel.getMatchPassword())){
throw new UserException("Passwords do not match");
}
User user = User.builder().
email(userModel.getEmail()).
firstName(userModel.getFirstName()).
lastName(userModel.getLastName()).
password(userModel.getPassword()).
timeCreated(Timestamp.valueOf(LocalDateTime.now(ZoneId.systemDefault()))).build();
User returnedUser = userRepository.save(user);
System.out.println(returnedUser.getEmail());
System.out.println("userID" + returnedUser.getUserID());
return returnedUser;
}
}
Thanks for reading! :)
I guess that your User class has an equals/hashCode defined either on the userID field or on all fields. When you mock your repository in your setUp method, you define what the method should do when it is called with the exact object that you specified, comparing the given object to the object specified in the mock by calling the equals/hashCode method.
You define the User object in your setUp method to have the userID 1 but in your UserServiceImpl the userID is not ever set (as it is generated by the persistence layer). Therefore the equals/hashCode check to determine if Mockito should execute the stub logic will never be called because the object passed by the UserServiceImpl will never be equals to the one that you defined in your mock.
There are several ways how you can solve this.
1) Integration test
My advice would be to convert your Unit-Test to a #SpringBootTest. There is no value in testing mocked behaviour. If this test was an integration test, you would test that the repository inserts the User into the database and generates an ID with your assertion.
2) Unit test
If you want to mock the repository, you should use an any() matcher for the input argument and then do what the repository would do, namely set the id:
when(repository.save(any(User.class))).thenAnswer(invocation -> {
final User entity = invocation.getArgument(0);
ReflectionTestUtils.setField(entity, "userID", RandomUtils.nextLong());
return entity;
});
I would then assert, that the userID is not null, as the userID will be randomly generated as it would in production:
#ExtendWith(MockitoExtension.class)
public class UserServiceTest {
#Mock
private UserRepository repository;
#InjectMocks
private UserServiceImpl userServiceImpl;
#Test
void saveUser_userHasID(){
// Arrange
final UserModel inputUserModel = new UserModel();
inputUserModel.setEmail("testemail#gmail.com");
inputUserModel.setFirstName("john");
inputUserModel.setLastName("doe");
inputUserModel.setPassword("test");
inputUserModel.setMatchPassword("test");
when(repository.save(any(User.class))).thenAnswer(invocation -> {
final User entity = invocation.getArgument(0);
ReflectionTestUtils.setField(entity, "userID", RandomUtils.nextLong());
return entity;
});
// Act
final User user = userServiceImpl.saveUser(inputUserModel);
// Assert
assertThat(user.getUserID()).isNotNull();
}
}
I highly recommend to use AssertJ for your assertions as it offers fluent asssertions and way more assertion methods than JUnit.
tl;dr
Spring data can't generate a userID through a simple unit test with a mock, you will need a spring context running in an Integration test.
Details
There are two fields leading to the error you are having : UserID and timeCreated.
For the timeCreated field : in your test you create a inputUser
object that you use for your mock. the problem is that this object
inputUser is NOT the one used by your method under test
saveUser(UserModel userModel) you could clearly see the that the
timestamp differs in the erreor between the two objects.
For the UserID field : again you give your expect your mock to use
the inputUser object. this object has userID = 1. but in the real
method saveUser(UserModel userModel) this field is not set
explicitly so the default value will be set to 0 you could see that
also in the error.
Now since your test case whenSaveUser_ThenUserHasID is meant to verify that a saved record has an ID, you can't do it with a mock. The ID is generated by the Spring Data you are using and yo make it run, you should use and Integration test to run your spring context in your test in order for your spring data to be able to generate an ID for your user, here is a previous question that will be helpful for you to do so. With this, you will neither need the inputUser in your test nor the mock. (You can use #DataJpaTest from JUnit 5 or #SpringBootTest...)

Java writing Spring Boot Unit Tests

I was wondering if I wrote this test in a proper way to mock a real situation. Can you please provide any feedback?
I'm using Mockito in a Spring Boot environment. I'm new to Mockito and other mocking techniques, but I want to know if I'm on the right path or not.
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest
class FileServiceImplTest {
#Mock
private UserPrincipal userPrincipal;
#Mock
private User user;
#Mock
private FileService fileService;
#BeforeEach
void initialize() {
user = new User("testUser", "test#email.com", "testPassword");
user.setId(1L);
user.setRoles(List.of(Role.User));
userPrincipal = UserPrincipal.create(user);
}
#Test
void usedMockUpsShouldNotBeNull() {
assertAll("All mock instaces of classes should not be null",
() -> assertNotNull(fileService),
() -> assertNotNull(userPrincipal),
() -> assertNotNull(user)
);
}
#Test
void collectionOfFilesShouldChangeAfterNewFileIsAdded_Mockito() throws ExecutionException, InterruptedException {
Image image = createImageFile();
List<? super File> files = createFilesCollection();
doReturn(files).when(fileService).getAll(userPrincipal);
int initialSize = fileService.getAll(userPrincipal).size();
fileService.save(image);
doReturn(files.add(image)).when(fileService).save(image);
int newSize = fileService.getAll(userPrincipal).size();
assertNotEquals(newSize, initialSize);
Mockito.verify(fileService, atLeast(2)).getAll(userPrincipal);
Mockito.verify(fileService).save(image);
}
}
I am sorry that you are in the wrong path.
If FileService is just an interface, you do not need to test it.
If FileService is an implementation, you should create an actual instance to test it but not a mock.
The mock is only useful for the direct dependencies of the class that you are testing (i.e FileSerivice) such that you can easily stub the result when calling methods on these dependencies and verify if the class you are testing interacts with them correctly. (i.e. call the correct methods with the correct parameters etc.)
As I cannot see FileSerivice 's source codes , but if UserPrincipal and User are not its dependencies , it does not make sense to mock them.
Also as mentioned by other in the comment, as you are not doing the integration testing with some Spring framework stuff, you should simply rewrite your test as a plain Mockito test which is simpler and run faster :
#ExtendWith(MockitoExtension.class)
public class FileServiceImplTest {
}

How to Mock HttpSession and pass it as arguemnt to a method which works with session?

I am using Spring framework 2.4.4 and Intellij Ultimate 2020
I would like to test this method tryGetUser on which I am passing HttpSession, but I can't resolve the problem, because when I am mocking it and setAttribute(); at the //Arrange part of the method it always ends with currentUserUsername to be null.
public User tryGetUser(HttpSession session) {
String currentUserUsername = (String) session.getAttribute("currentUserUsername");
***// Here this local variable currentUsername is always null;***
if (currentUserUsername == null) {
throw new AuthorizationException("No logged in user.");
}
try {
return userService.getByUsername(currentUserUsername);
} catch (EntityNotFoundException e) {
throw new AuthorizationException("No logged in user.");
}
}
Here you can see how I am trying to Mock it, but actually, the session is remaining empty or I don't know, but when the service method starts to execute the attribute is not there.
#ExtendWith(MockitoExtension.class)
public class LoginServiceMvcTests {
#Mock
UserRepository mockUserRepository;
#Mock
UserService mockUserService;
#InjectMocks
MockHttpSession mockHttpSession;
#InjectMocks
LoginServiceMvc userService;
private User junkie;
private User organizer;
#BeforeEach
public void setup() {
junkie = Helpers.createJunkie();
organizer = Helpers.createOrganizer();
}
#Test
public void tryGetUser_Should_GetUserFromSession_When_UserIsLogged() {
// Arrange
mockHttpSession.setAttribute("luboslav", junkie);
Mockito.when(mockUserService.getByUsername("luboslav"))
.thenReturn(junkie);
// Act
userService.tryGetUser(mockHttpSession);
// Assert
Mockito.verify(mockUserService, Mockito.times(1))
.getByUsername("luboslav");
}
}
From Jon skeet answer Mockito doesn't work that way, so you need to actually mock the call instead of setting attribute
Mockito.when(mockHttpSession. getAttribute("currentUserUsername"))
.thenReturn("name");
And also HttpSession should be annotated with #Mock not #InjectMocks
#Mock
HttpSession httpSession;
Use MockHttpSession, which is an actual real object (not just a Mockito interface mock) that is intended for exactly this type of testing. It's basically an empty/blank container, so you don't have to mock out all the standard behavior, just fill it with whatever attributes you want.

How can I properly mock a Principal object in Spring?

First of all, I have the following endpoint method present within a class called RecipeController:
#RequestMapping(value = {"/", "/recipes"})
public String listRecipes(Model model, Principal principal){
List<Recipe> recipes;
User user = (User)((UsernamePasswordAuthenticationToken)principal).getPrincipal();
User actualUser = userService.findByUsername(user.getUsername());
if(!model.containsAttribute("recipes")){
recipes = recipeService.findAll();
model.addAttribute("nullAndNonNullUserFavoriteRecipeList",
UtilityMethods.nullAndNonNullUserFavoriteRecipeList(recipes, actualUser.getFavoritedRecipes()));
model.addAttribute("recipes", recipes);
}
if(!model.containsAttribute("recipe")){
model.addAttribute("recipe", new Recipe());
}
model.addAttribute("categories", Category.values());
model.addAttribute("username", user.getUsername());
return "recipe/index";
}
As you can see above, the method takes as a second parameter a Principal object. When running the application, the parameter points to a non-null object as expected. It contains information about the user that is currently logged in within the application.
I have created a test class for the RecipeController called RecipeControllerTest. This class contains a single method called testListRecipes.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
#WebAppConfiguration
public class RecipeControllerTest{
#Mock
private RecipeService recipeService;
#Mock
private IngredientService ingredientService;
#Mock
private StepService stepService;
#Mock
private UserService userService;
#Mock
private UsernamePasswordAuthenticationToken principal;
private RecipeController recipeController;
private MockMvc mockMvc;
#Before
public void setUp(){
MockitoAnnotations.initMocks(this);
recipeController = new RecipeController(recipeService,
ingredientService, stepService, userService);
mockMvc = MockMvcBuilders.standaloneSetup(recipeController).build();
}
#Test
public void testListRecipes() throws Exception {
User user = new User();
List<Recipe> recipes = new ArrayList<>();
Recipe recipe = new Recipe();
recipes.add(recipe);
when(principal.getPrincipal()).thenReturn(user);
when(userService.findByUsername(anyString()))
.thenReturn(user);
when(recipeService.findAll()).thenReturn(recipes);
mockMvc.perform(get("/recipes"))
.andExpect(status().isOk())
.andExpect(view().name("recipe/index"))
.andExpect(model().attributeExists("recipes"))
.andExpect(model().attributeExists("recipe"))
.andExpect(model().attributeExists("categories"))
.andExpect(model().attributeExists("username"));
verify(userService, times(1)).findByUsername(anyString());
verify(recipeService, times(1)).findAll();
}
}
As you can see in this second snippet, I tried to mock the Principal object within the test class, using the UsernamePasswordAuthenticationToken implementation.
When I run the test, I get a NullPointerException, and the stacktrace points me to the following line from the first snippet of code:
User user = (User)((UsernamePasswordAuthenticationToken)principal).getPrincipal();
The principal object passed as a parameter to the listRecipes method from is still null, even though I tried to provide a mock object.
Any suggestions ?
Create a class that implements Principal:
class PrincipalImpl implements Principal {
#Override
public String getName() {
return "XXXXXXX";
}
}
Sample test:
#Test
public void login() throws Exception {
Principal principal = new PrincipalImpl();
mockMvc.perform(get("/login").principal(principal)).andExpect(.........;
}
Spring MVC is very flexible with controller arguments, which lets you put most of the responsibility of looking up information onto the framework and focus on writing the business code. In this particular case, while you can use Principal as a method parameter, it's usually much better to use your actual principal class:
public String listRecipes(Model model, #AuthenticationPrincipal User user)
To actually set the user for a test, you need to work with Spring Security, which means adding .apply(springSecurity()) to your setup. (Complications like this, by the way, are the main reason I dislike using standaloneSetup, as it requires you to remember to duplicate your exact production setup. I recommend writing actual unit tests and/or full-stack tests.) Then annotate your test with #WithUserDetails and specify the username of the test user.
Finally, as a side note this controller pattern can be simplified significantly with Querydsl, as Spring is able to inject a Predicate that combines all of the filter attributes you're looking up by hand, and then you can pass that predicate to a Spring Data repository.
Did you try using...?
#Test
#WithMockUser(username = "my_principal")
public void testListRecipes() {
...

Mocking not able to mock method call

I am trying to mock a call in my test but I am getting a error as its calling the real method than mocking it.
This is my method
#Value("${omega.aws.nonprod-profile}")
private String nonProdProfile;
#Autowired
AwsService awsService;
public List<SecurityGroup> getAllSecurityGroups() {
AmazonEC2 ec2 = configSetter();
return awsService.getAllSecurityGroups(ec2);
}
protected AmazonEC2 configSetter() {
ProfileCredentialsProvider credentials = new ProfileCredentialsProvider(nonProdProfile);
ClientConfiguration clientCfg = new ClientConfiguration();
clientCfg.setProxyHost(this.proxyHost);
clientCfg.setProxyPort(Integer.valueOf(proxyPort));
clientCfg.setProtocol(Protocol.HTTPS);
return new AmazonEC2Client(credentials, clientCfg);
}
Here is my test class
#InjectMocks
private AwsConfigurationLocal subject;
#Mock
private AwsService awsService;
#Test
public void TestgetAllSecurityGroups() throws Exception {
ec2 = Mockito.mock(AmazonEC2Client.class);
securityGroup = new SecurityGroup();
List<SecurityGroup> result = Collections.singletonList(securityGroup);
Mockito.when(awsService.getAllSecurityGroups(ec2)).thenReturn(result);
List<SecurityGroup> actual = subject.getAllSecurityGroups();
assertThat(actual, CoreMatchers.is(equals(result)));
}
The test actually calls the protected method configSetter and fails when setting a proxy. Help me understand what I am doing wrong here.
subject.getAllSecurityGroups(); calls real configSetter() which returns real AmazonEC2 which in turn is passed on to awsService.getAllSecurityGroups(ec2);. The parameter doesn't match your mock ec2 so the default mock implementation is returned (I guess it's null) as a actual.
So the issue is: there is nothing that would prevent real implementation of configSetter() to be called.
If you annotate subject with #Spy and do
Mockito.when(subject.configSetter()).then(ec2);
it should work as expected.
That being said there's a lot of set up done only to check simple invocation being delegated. This is due intermixing of two responsibility in AwsConfigurationLocal - creation of AmazonEC2Client and providing getAllSecurityGroups(). If you move the former into separate class (let's say AmazonEC2ClientFactor) everything should fall in place.
Try using powerMockito to return the mocked instance of AmazonEC2
#RunWith(PowerMockRunner.class)
#PrepareForTest({AwsConfigurationLocal.class})//assuming this is your test class
#InjectMocks
private AwsConfigurationLocal subject=PowerMockito.spy(AwsConfigurationLocal.class);//or try Mockito.spy instead, whichever works
#Test
public void TestgetAllSecurityGroups() throws Exception {
ec2 = Mockito.mock(AmazonEC2Client.class);
PowerMockito.doReturn(ec2).when(subject).configSetter().withNoArguments();
//your code
}

Categories

Resources