I have a Java Spring method I want to test out that look something like this:
#RunWithPermission(values={Permission.Manage, Permissions.ManageAll}, any=true)
#RequestMapping(method = RequestMethod.POST)
public Object createReport(#RequestBody ReportParam definition) throws Exception {
...
return reportManager.createReport(definition);
}
Now my function will only run if the user has Permission.Manage or Permission.ManageAll but will not run if the user has permission like Permission.View. I want to write a unit test that will test this out so I tried something like this:
#Autowired
#InjectMocks
private RestReportController controller;
...
#Test()
#RunWithPermission(values={Permissions.View}, any=true)
public void testCreateReportPermissions() throws Exception {
ReportParam param = getReportParam("report_param.json");
param.setLabelIds(Arrays.asList(labelService.getHomeId()));
controller.createReport(param);
}
EDIT: Attempt 2: I've also tried to create some sort of fake user session to see if I can "set" the permissions during the test but that doesn't seem to work either.
#Test()
public void testCreateReportPermissions() throws Exception{
ReportParam param = getReportParam("report_param.json");
param.setLabelIds(Arrays.asList(labelService.getHomeId()));
sessionManager.clearUserSession();
userSession = TestUtils.createFakeSession("testuser001", "1000", Permissions.View);
sessionManager.setUserSession(userSession);
controller.createReport(param);
}
However, as seen by my test, it creates the Report and the unit test finishes. However, is there a way to have it throw some sort of error or exception since I annotated the method with a permission with Permission.View? If anyone could help, that would be great. Thanks!
Related
I'm new to testing methods with JUnit. I'm currently working on a Springboot application, which I want to test properly now. However, I don't understand what needs to be done in order to test a method that has a model as an argument.
The method I want to test:
public void updateCheckoutIncome(Model model, MonetaryAmount amount) {
checkoutIncome = checkoutIncome.add(amount);
model.addAttribute("checkoutIncome", checkoutIncome);
}
I then went ahead and wrote this test file that should test the class above:
public class AccountingManagementTest {
#SpringBootTest
#AutoConfigureMockMvc
public class AccountingControllerTest {
#Autowired
MockMvc mvc;
#Autowired
AccountingManagement accountingManagement;
#Test
#WithMockUser(roles = "BOSS,EMPLOYEE")
void updateCheckoutIncomeTest() throws Exception {
MonetaryAmount checkoutIncome = Money.of(10.00, EURO);
Model cart = new ModelAttribute("cart");
accountingManagement.updateCheckoutIncome(cart, Money.of(19.99, EURO));
Assertions.assertEquals(Money.of(29.99, EURO), checkoutIncome);
}
}
}
My question is: How do I test create the "model", that I need to pass along to the method call in the test? I could find a method creating a new "ModelAndView" element, but my method requires a "Model" element. Can anyone guide me what I'm missing?
To explain the model a little bit: This defines a view element in springboot. If you're having a model "cart" for example, this contains information which will be passed over to an HTML file (for example).
I think in this case you should use ArgumentCaptor which will capture the argument(model) you passed to your method (updateCheckoutIncome), then you can verify the values of attributes of your model.
for more details check out this tutorial:
https://www.baeldung.com/mockito-argumentcaptor
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 am new in Junit tests and I have a question about it. Here you can see the method findById in my service class:
#Service
public class DefaultQuarterService implements QuarterService {
private final QuarterRepository quarterRepository;
public DefaultQuarterService(QuarterRepository quarterRepository) {
this.quarterRepository = quarterRepository;
}
#Override
public QuarterEntity findById(int id) {
return quarterRepository.findById(id)
.orElseThrow(() -> new EntityNotFoundException(String.format("Quarter does not exist for id = %s!", id)));
}
}
And here is my QuarterRepository:
#Repository
public interface QuarterRepository extends CrudRepository<QuarterEntity, Integer> {
}
And here is my Junit implementation for this method:
#MockBean
private QuarterRepository quarterRepository;
#Test
public void throwExceptionWhenQuarterIdNotFound() {
int id = anyInt();
when(quarterRepository.findById(id))
.thenReturn(Optional.empty());
assertThatAnExceptionWasThrown(String.format("Quarter does not exist for id = %s!", id));
}
public void assertThatAnExceptionWasThrown(
String errorMsg
) {
expectException.expect(RuntimeException.class);
expectException.expectMessage(errorMsg);
}
Unfortunately test doesn't pass. Here the error in terminal:
java.lang.AssertionError: Expected test to throw (an instance of
java.lang.RuntimeException and exception with message a string
containing "Quarter does not exist for id = 0!")
Maybe it is so simple but I can not see what I am missing. I would be so happy if you can direct me. Thanks a lot!
As you mock your Repository it will return with Optional.empty() correctly, I think you should call your service's (which is Autowired) findById method. It will throw the exception actually.
First issue
In the assertThatAnExceptionWasThrown method you expect RuntimeException BUT in the service class you throw EntityNotFoundException, So I guess you should expect EntityNotFoundException in your test case.
Second issue
After this part of the code.
when(quarterRepository.findById(id))
.thenReturn(Optional.empty());
Why didn't you call your service method (findById)?
When you are returning the empty value, you should verify your condition with the service method you want to test it.
It should be something like this.
assertThatThrownBy(() -> defaultQuarterService.findById(id))
.isInstanceOf(ApiRequestException.class)
.hasMessageContaining("PUT_YOUR_EXCEPTION_MESSAGE_HERE");
This is a good sample for unit-test in the spring boot. You can check it out. Link
Try the above solutions and let me know it has been fixed or not. Good luck
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.
abort()-Method:
public void abort() {
LOG.info("some-text");
warning("some-text");
}
warning()-Method:
public void warning(String message) {
FacesContext.getCurrentInstance()
.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_WARN, "INFO:", message));
}
I want to write a Test-Case for abort wich is just verifying that nothing has changed and a second Test-Case which is verifying that warning() is working.
I´m knowing this little two methods doesn´t need a Unit-Test but I want to know if it´s possible. UI-Test for showing the p:message is working well but I want to check the Caption, Typ and Message by Unittest before because it´s running much faster.
working solution with just using JUnit 4.11
Separate the content from warning() in a own class like this:
#Named
#RequestScoped
public class Resources {
#Produces
public FacesContext produceFacesContext() {
return FacesContext.getCurrentInstance();
}
}
Next you need to define an ArgumentCaptor which can catch the FacesMessage for your JUnit-Test. I´ve created it as a clss member which will be initialized in the #before section and get the null value in #teardown.
private ArgumentCaptor<FacesMessage> facesMessageCaptor;
#Before
public void setUp() {facesMessageCaptor = ArgumentCaptor
.forClass(FacesMessage.class);
}
#After
public void tearDown() { facesMessageCaptor = null; }
Now you just need to add two #Mocks to your test-class
#Mock
Resources resourcesMock;
#Mock
FacesContext facesContextMock;
So you did it! Write the test like this:
Mockito.doReturn(facesContextMock).when(resourcesMock).produceFacesContext();
// Execute Method
cut.warning("SOME_DETAIL_TEXT");
// Verify interactions with the Resources and Faces and maybe others...
verify(resourcesMock).produceFacesContext();
verify(facesContextMock).addMessage(Mockito.anyString() ,
facesMessageCaptor.capture());
verifyNoMoreInteractions(...., resourcesMock, facesContextMock);
// write assert (i´ve used hamcrast-corematchers - more readable)
actualMessage = (FacesMessage) facesMessageCaptor.getValue();
assertThat(FacesMessage.SEVERITY_WARN, is(equalTo(actualMessage.getSeverity())));
assertThat(MY_TITLE, is(equalTo(actualMessage.getSummary())));
assertThat("DETAIL_TEXT", is(equalTo(actualMessage.getDetail())));