I'm writing unit tests for my application and one of the steps I do inside my Service is get the currently authenticated user from SpringSecurityContext.
I know that if I want to mock Spring Security Authentication I can use #WithMockUser but it's not working for me as it is always returning null when the tested method reaches the getAuthentication() method call...
I've already search many SO questions and many blogs post but none of them has a solution.
I annotate my Test class with #ExtendWith(MockitoExtension.class)
I'd like to avoid having to write 4 lines to mock a single method call
// mock Authentication
// mock Principal
// when SpringContextHolder.getContext().getAuthentication -> return Authentication
// when getPrincipal() -> return Principal
edit:
Test class
#ExtendWith(MockitoExtension.class)
public class SegmetnServiceTest {
#InjectMocks
private SegmentService service;
#Test
void testWithMockUser() {
//given
UpdateSegmentReq request = new UpdateSegmentReq();
String name = "TEST"
request.setName(name)
//when
SegmentNode segment = service.updateSegment(request);
//then
assertEquals(segment.getName(), name)
}
}
Service class
public class SegmentService {
private SegmentRepository repository;
SegmentNode updateSegment(String code){
SegmentNode segment = repository.findByCode(code);
String userId = (String) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
segment.updatedBy(userId);
return segment;
}
}
The problem is, even if I annotate my Test method with #WithMockUser, when it reaches the service method getAuthentication is null and getPrincipal throws NPE.
You should be using #ContextConfiguration to load Spring Security test configuration into the Application Context(assuming you also have spring-testas a dependency)See for clarification.
You can use the annotation #SpringJUnitConfig to combine the required annotations -- #ExtendWith(SpringExtension.class) and #ContextConfiguration -- to setup spring test in your tests.
Related
I'm trying to mock the return value for a method using the when call from mockito. However, I'm new to this and I may perhaps be misunderstanding how mockito works, since the call is failing inside the method mocked when that calls another method. I thought regardless of how that method is implemented, I should be getting the return value I'm asking for? Or do I need to mock also the internals for that method? I feel that shouldn't be it.
public boolean verifyState(HttpServletRequest request, String s) {
String stateToken = getCookieByName(request, STATE_TOKEN);
String authToken = getCookieByName(request, AUTHN);
boolean isValidState = true;
if (isValidState) {
try {
log.info(getEdUserId(stateToken, authToken));
return true;
} catch (Exception e) {
ExceptionLogger.logDetailedError("CookieSessionUtils.verifyState", e);
return false;
}
} else {
return false;
}
}
public String getEdUserId(String stateToken, String authToken) throws Exception {
String edUserId;
Map<String, Object> jwtClaims;
jwtClaims = StateUtils.checkJWT(stateToken, this.stateSharedSecret); // Failing here not generating a proper jwt token
log.info("State Claims: " + jwtClaims);
edUserId = sifAuthorizationService.getEdUserIdFromAuthJWT(authToken);
return edUserId;
}
My test:
#ActiveProfiles(resolver = MyActiveProfileResolver.class)
#WebMvcTest(value = CookieSessionUtils.class, includeFilters = {
#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {ApiOriginFilter.class, ValidationFilter.class})})
class CookieSessionUtilsTest {
#Autowired
private CookieSessionUtils cookieSessionUtils; // Service class
#Mock
private CookieSessionUtils cookieSessionUtilsMocked; // Both the method under test and the one mocked are under the same class, so trying these two annotations together.
#Mock
private HttpServletRequest request;
#BeforeEach
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testVerifyState1() throws Exception {
//...Some mocks for getCookieName
UUID uuid = UUID.randomUUID();
when(cookieSessionUtils.getEdUserId(anyString(), anyString()).thenReturn(eq(String.valueOf(uuid))); // When this line runs it fails on verifyState method
assertTrue(cookieSessionUtils.verifyState(request, ""));
}
UPDATE
Attempt using anyString() instead of eq().
Thank you.
Your test is broken in a few places.
Setting expectations on a real object
You should call Mockito.when on mocks and spies, not on System under test. Mockito normally reports it with a clear error message, but you throw a NPE from getEdUserId, so this is reported instead. The NPE stems from the fact that both eq and anyString return null, which is passed to the real method.
Invalid use of matchers
As #StefanD explained in his answer eq("anyString()") is not matching any string. It matches only one string "anyString()"
Returning a mather instead of real object
thenReturn(eq(String.valueOf(uuid)))
This is illegal position for a matcher.
Mixing Mockito and Spring annotations in a WebMvcTest
This is a common error. Mockito does not inject beans to the spring context.
From the code provided it is unclear what CookieSessionUtils is (Controller? ControllerAdvice?) and what is the correct way to test it.
Update
It seems that you are trying to replace some methods under test. A way to do it is to use a Spy.
See https://towardsdatascience.com/mocking-a-method-in-the-same-test-class-using-mockito-b8f997916109
The test written in this style:
#ExtendWith(MockitoExtension.class)
class CookieSessionUtilsTest {
#Mock
private HttpServletRequest request;
#Mock
private SifAuthorizationService sifAuthorizationService;
#Spy
#InjectMocks
private CookieSessionUtils cookieSessionUtils;
#Test
public void testVerifyState1() throws Exception {
Cookie cookie1 = new Cookie("stateToken", "stateToken");
Cookie cookie2 = new Cookie("Authn", "Authn");
when(request.getCookies()).thenReturn(new Cookie[]{cookie1, cookie2});
UUID uuid = UUID.randomUUID();
doReturn(String.valueOf(uuid)).when(cookieSessionUtils).getEdUserId(anyString(), anyString());
assertTrue(cookieSessionUtils.verifyState(request, ""));
}
}
An alternative way is to call the real method, but to mock all collaborators: StateUtils and sifAuthorizationService. I would probably go with this one, if you want to test public getEdUserId.
Test written when mocking collaborators:
#ExtendWith(MockitoExtension.class)
class CookieSessionUtilsTest {
#Mock
private HttpServletRequest request;
#Mock
private SifAuthorizationService sifAuthorizationService;
#InjectMocks
private CookieSessionUtils cookieSessionUtils;
#Test
public void testVerifyState1() throws Exception {
Cookie cookie1 = new Cookie("stateToken", "stateToken");
Cookie cookie2 = new Cookie("Authn", "Authn");
when(request.getCookies()).thenReturn(new Cookie[]{cookie1, cookie2});
UUID uuid = UUID.randomUUID();
when(sifAuthorizationService.getEdUserIdFromAuthJWT(cookie2.getValue())).thenReturn(String.valueOf(uuid));
assertTrue(cookieSessionUtils.verifyState(request, ""));
}
}
I took the assumption that StateUtils.checkJWT does not need to be mocked
The points above are still valid and need to be resolved in either case.
Remarks
As the system under test is currently a Service, I suggest to drop WebMvcTest and test it with plain mockito instead.
Should SUT be a service? It is more typical to handle auth code in filters.
note usage of doReturn when stubbing a method on a spy.
You use mocks in more places than needed. For example Cookie is trivial to construct, there is no point in using a mock
The error is here:
when(cookieSessionUtils.getEdUserId(eq("anyString()"), eq("anyString()"))).thenReturn(eq(String.valueOf(uuid)));
It should read like
when(cookieSessionUtils.getEdUserId(anyString()), anyString()).thenReturn(uuid);
Please refer to the Mockito documentation of Argument matchers.
Because the argument matchers looking for the string "anyString()" they never match the actual parameters the method call is providing and so there is never returned the uuid you expecting.
I have a microservice setup with Spring boot and OAuth 2 with JWT.
I have additional fields in my JWT token.
Most of my services call a static method that has a thread local of the additional fields in the token.
How can I write unit tests for such services?
Even if I tried to inject a mock user it doesn't work and I couldn't find a way of sending the JWT because I am no testing the controllers.
Code:
SecurityUtils static Calss (also check the package for other relevant JWT handler) .
Example on a method that will call the static class (Line 79).
Method:
public CommonResponse saveUpdateProfile(ProfileRequest request) {
String authUsername = SecurityUtils.getUsername();
Optional<ProfileEntity> optionalProfile = findProfileByUsername(authUsername);
ProfileEntity profile;
if (optionalProfile.isPresent()) {
profile = optionalProfile.get();
} else {
profile = ProfileEntity.builder()
.username(authUsername)
.build();
}
profile.setFirstName(request.getFirstName());
profile.setLastName(request.getLastName());
ProfileEntity savedProfile = profileRepository.save(profile);
if (savedProfile == null) {
throw new RuntimeException("Failed to save user in database");
}
return CommonResponse.ok(savedProfile);
}
I appreciate all the help.
Ok, so that's a common problem when using static methods. You can't easily override them, e.g. in tests. I think what I would do is to turn your SecurityUtils class into a service and make it implement an interface. Then inject this interface into any other service that needs to use it, instead of calling static methods. Then you can easily provide another implementation to your tests.
So you would have something like that:
interface SecurityUtils {
String getUsername();
...
}
#Service
class MySecurityUtils immplements SecurityUtils {
private JwtToken getJwtToken() {
return MySecurityContextHolder.getContext().getJwtToken();
}
public String getUsername() {
return getJwtToken().getUsername();
}
...
}
Then in the unit test you can just inject a mock implementation of SecurityUtils to any class you're testing.
This question already has answers here:
Mockito - difference between doReturn() and when()
(5 answers)
Closed 2 years ago.
I'm pretty new to Junit and I'm trying to test isApproved method in Junit. I'm am mocking isDateValidOfData method using when and thenReturn using
Mockito.when(isDateValidOfData(anyLong(), anyString()).thenReturn(true);
This is getting an indexOutOfBounds exception. Here the serviceClass is being called on argument matchers so returning nothing in the list of data. I just want to know is there a way to mock the data and test it using Mockito and Junit.
Using spy to get the object of the same class to call the method.
MyClass {
//Service class calls the repository to fetch data.
#Autowired
ServiceClass serviceClass;
public boolean isApproved(Long id, String code) {
// Validation is done on the arguments
//if id is of a particular type then return true by default
//if code is in a list already present then continue with the below code or else return true by default.
return isDateValidOfData(Long id, String code);
}
public boolean isDateValidOfData(Long id, String code) {
List<Data> data = serviceObject.getData(id, code);
LocalDateTime eDate = data.get(0).getEDate();
LocalDateTime rDate = data.get(0).getRDate();
// check if eDate and rDate is greater than current date, if yes return true or return false
}
}
#RunWith(SpringRunner.class)
TestClass {
#InjectMocks
MyClass myClass;
#Test
public void isApprovedTest() {
MyClass myClass1 = Mockito.spy(myClass);
Mockito.when(myClass1.isDateValidOfData(anyLong(), anyString())).thenReturn(true);
Assert.assertTrue(myClass1.isApproved(1234L, "1234");
}
}
Since you are using #InjectMocks you should use #RunWith(MockitoJUnitRunner.class) on class level an everything will work fine.
If you want to use #RunWith(SpringRunner.class) then use #MockBean and #SpyBean to write test.
EDIT:
You can see more #RunWith(SpringRunner.class) vs #RunWith(MockitoJUnitRunner.class) here
Additional, you can check this Why we use Mockitojunitrunner class in our junit test?
and #RunWith(SpringRunner.class) with Spring Boot
Also, please check this one Mockito - difference between doReturn() and when()
Hello i try to use mockito, to verify user password if invalid i want to verify it show error message.
But i got this following error :
Wanted but not invoked:
loginView.showPasswordError();
-> at android.fanjavaid.com.tdd_android_login.LoginActivityTest.invalidPassword_notLoggedIn_showPasswordError(LoginActivityTest.java:84)
However, there was exactly 1 interaction with this mock:
loginView.showEmailError();
-> at android.fanjavaid.com.tdd_android_login.presenter.LoginPresenterImpl.validateInput(LoginPresenterImpl.java:23)
Here is my test method :
#Test
public void invalidEmail_notLoggedIn_showEmailError() {
LoginPresenter presenter = new LoginPresenterImpl(loginView, validator);
presenter.validateInput(user);
verify(loginView).showEmailError();
}
#Test
public void invalidPassword_notLoggedIn_showPasswordError() {
when(user.getEmailAddress()).thenReturn("fanjavaid#gmail.com");
LoginPresenter presenter = new LoginPresenterImpl(loginView, validator);
presenter.validateInput(user);
verify(loginView).showPasswordError();
}
I already mock the email user in invalidPassword_notLoggedIn_showPasswordError() with valid input, but i still get that error message.
Here is my Presenter implementation :
#Override
public void validateInput(User user) {
if (!validator.validateEmail(user.getEmailAddress())) {
view.showEmailError();
} else if (validator.validatePassword(user.getPassword())) {
view.showPasswordError();
}
}
What i am missing for?
Thank you
✔ ANSWER
After several minutes explore again, i found something interesting.
I forgot to add mock to one class.
Below i mock some classes moreless like this :
#RunWith(MockitoJUnitRunner.class)
public class LoginActivityTest {
#Mock User user;
#Mock LoginView loginView;
#Mock MyValidator validator;
LoginPresenter presenter;
#Before
public void beforeTest() {
presenter = new LoginPresenterImpl(loginView, validator);
}
...
You can see that i mock validator class.
I got the error because in invalidPassword_notLoggedIn_showPasswordError() method i didn't add mock value for email validation.
// Mock validation
when(validator.validateEmail(user.getEmailAddress())).thenReturn(true);
If i don't mock it, it will ask about showEmailError() but we just verify showPasswordError()
This cause my implementation using condition to check one by one, started from check email is valid or not, then password valid or not.
If email doesn't exists and return value from validator doesn't exists the error will occured.
So i need to mock email address as valid and mock validator to return true(valid email).
That's my explanation and hope can help anyone who try mockito.
When I am testing the Mocked external call, I am not seeing the mocked value of report instead it is Null and my testing is failing. I can see the Mocked value (of report) in the Test Class but not in BusinessServiceImpl class and Application(Method Return) is not modified as I expected.
My Expectation: When I mock the External call in Impl class, mocked value should be available there and rest everything else happen as if real method was called to complete the Unit Testing.
Implementation code:
package com.core.business.service.dp.fulfillment;
import com.core.business.service.dp.payment.PaymentBusinessService;
public class BusinessServiceImpl implements BusinessService { // Actual Impl Class
private PaymentBusinessService paymentBusinessService = PluginSystem.INSTANCE.getPluginInjector().getInstance(PaymentBusinessService.class);
#Transactional( rollbackOn = Throwable.class)
public Application applicationValidation (final Deal deal) throws BasePersistenceException {
Application application = (Application) ApplicationDTOFactory.eINSTANCE.createApplication();
//External Call we want to Mock
String report = paymentBusinessService.checkForCreditCardReport(deal.getId());
if (report != null) {
application.settingSomething(true); //report is Null and hence not reaching here
}
return application;
}
}
The test code:
#Test(enabled = true)// Test Class
public void testReCalculatePrepaids() throws Exception {
PaymentBusinessService paymentBusinessService = mock(PaymentBusinessService.class);
//Mocking External Call
when(paymentBusinessService.checkForCreditCardReport(this.deal.getId())).thenReturn(new String ("Decline by only Me"));
String report = paymentBusinessService.checkForCreditCardReport(this.deal.getId());
// Mocked value of report available here
//Calling Impl Class whose one external call is mocked
//Application is not modified as expected since report is Null in Impl class
Application sc = BusinessService.applicationValidation(this.deal);
}
The main purpose of Mockito is to isolate the tests. As in, when you are testing your BusinessServiceImpl you should mock all its dependencies.
This is exactly what you are trying to do with your example above. Now for the mocking to work, the mocked object has to be injected into the class you are trying to test, in this case the BusinessServiceImpl.
One way of doing this is by passing the dependecy by the contructor of the class, dependency injection. Or you could look at how it can be done with Spring and ReflectionTestUtils.
I got it done and I am successfully able to get the Mocked value without touching the BusinessServiceImpl class at all. Steps I followed are:
1. #Mock PaymentBusinessService paymentBusinessService = mock(PaymentBusinessService.class);
2. #InjectMocks private PaymentBusinessService paymentBusinessService = PluginSystem.INSTANCE.getPluginInjector().getInstance(PaymentBusinessService.class);
And then simply ran the above test and I could see the value of report as "Decline by only Me" in the BusinessServiceImpl and my test case passed