Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I have this method which I want to test using mockito
public boolean verifyState(HttpServletRequest request) {
String stateToken = getCookieByName(request, STATE_TOKEN);
String authToken = getCookieByName(request, AUTHN);
boolean isValidState = !stateToken.isEmpty() && !authToken.isEmpty();
if (isValidState) {
return true;
}
else {
return false;
}
}
It does two calls to getCookieName(), which has this implementation.
public String getCookieByName(HttpServletRequest request, String cookieName) {
try {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals(cookieName)) {
return cookie.getValue();
}
}
}
} catch (Exception e) {
ExceptionLogger.logDetailedError("CookieSessionUtils.getCookieByName", e);
log.error("Error on Cookie " + e.getMessage());
}
return "";
}
I then have this for my tests:
#WebMvcTest(value = CookieSessionUtils.class, includeFilters =
{#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {ApiOriginFilter.class})})
class CookieSessionUtilsTest {
#Autowired
private CookieSessionUtils cookieSessionUtils;
#Mock
private HttpServletRequest request;
#BeforeEach
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testVerifyState() {
Cookie mockCookie1 = Mockito.mock(Cookie.class);
Cookie mockCookie2 = Mockito.mock(Cookie.class);
when(mockCookie1.getName()).thenReturn("stateToken");
when(mockCookie1.getValue()).thenReturn("stateToken");
when(mockCookie2.getName()).thenReturn("authn");
when(mockCookie2.getValue()).thenReturn("authn");
when(request.getCookies()).thenReturn(new Cookie[]{mockCookie1, mockCookie2});
when(cookieSessionUtils.getCookieByName(request, "stateToken")).thenReturn("stateToken");
when(cookieSessionUtils.getCookieByName(request, "authn")).thenReturn("authn");
assertTrue(cookieSessionUtils.verifyState(request));
}
However, it's always failing returning false falling into the return "" for the getCookieByName() method which seems to be triggered multiple times and having the value for getName() and getValue() overwritten by the other cookie, so it fails on (cookie.getName().equals(cookieName)). Not sure what I'm doing wrong.
Thank you.
No need to be so complicated. For mocking the servlet stuff, you can simply use the mock implementation provided by spring-test. It is more convenient to use than Mockito.
You can simply write your test case as :
#Test
public void testVerifyState() {
MockHttpServletRequest request = MockMvcRequestBuilders.get("/dummy")
.cookie(new MockCookie("stateToken", "stateToken"))
.cookie(new MockCookie("authn", "authn"))
.buildRequest(new MockServletContext());
assertTrue(cookieSessionUtils.verifyState(request));
}
Also , if the CookieSessionUtils that you are testing is just an utility class which does not have other spring bean dependencies, you can further simplify your test to just a plain JUnit test rather than a #WebMvcTest.
Based on #kenChan's answer, both of the below methods work. The issue was actually something very subtle and trivial. The value for the cookie was "authn" but its name is "Authn", so there wasn't a match between them due to capitalization.
private static final String STATE_TOKEN = "stateToken";
private static final String AUTHN = "Authn";
#Test
public void testVerifyState1() {
Cookie mockCookie1 = Mockito.mock(Cookie.class);
Cookie mockCookie2 = Mockito.mock(Cookie.class);
when(mockCookie1.getName()).thenReturn("stateToken");
when(mockCookie1.getValue()).thenReturn("stateToken");
when(mockCookie2.getName()).thenReturn("authn");
when(mockCookie2.getValue()).thenReturn("Authn");
when(request.getCookies()).thenReturn(new Cookie[]{mockCookie1, mockCookie2});
when(cookieSessionUtils.getCookieByName(request, "stateToken")).thenReturn("stateToken");
when(cookieSessionUtils.getCookieByName(request, "Authn")).thenReturn("Authn");
assertTrue(cookieSessionUtils.verifyState(request, ""));
}
#Test
public void testVerifyState() {
MockHttpServletRequest request = MockMvcRequestBuilders.get("/dummy")
.cookie(new MockCookie("stateToken", "stateToken"))
.cookie(new MockCookie("Authn", "authn"))
.buildRequest(new MockServletContext());
assertTrue(cookieSessionUtils.verifyState(request, ""));
}
Related
Hello I'm new to Java and Springboot. I'm currently working with an API where before making a POST request, I would need to generate a Bearer token. In order to generate a Bearer token, I would need to pass in my basic auth credentials to the "/oauth/token" endpoint. My application is having trouble passing my basic auth credentials since by the time I hit the "/v1/some-endpoint", I'm denied authorization because the Bearer token is null.
Here's my initial solution thinking I could check the url in the interceptor, then executing the following line but after debugging, it doesn't seem to be hitting that line.
Is there something I'm missing or not implementing correctly? Am I not implementing the Basic Auth endpoint correctly? Let me know if you need more information. Thanks
#Profile("!offline")
#FeignClient(
value = "someClient",
url = "${someProperty.url}",
configuration = SomeClient.SomeClientConfig.class)
public interface someClient {
#PostMapping("/v1/some-endpoint")
void redeemSomething(someRequestBody data);
#PostMapping("/oauth/token")
static BasicAuthResponse getBasicAuthToken() {
return new BasicAuthResponse();
}
#AllArgsConstructor
class SomeClientConfig extends BaseClientConfig {
private final SomeProperties properties;
private final SomeAuthTokenSupplier tokenSupplier = new SomeAuthTokenSupplier();
#Bean
#Override
public CloseableHttpClient apacheClient() {
return apacheClientFactory(properties.getUseProxy());
}
#Bean
public RequestInterceptor someAuthInterceptor() {
return template -> {
if(template.url().equals("/oauth/token")) {
String authToken = Base64Utils.encodeToString((properties.getCredentials().getUser() + ":" + properties.getCredentials().getUser()).getBytes(Charset.forName("UTF-8")));
template.header("Authorization", authToken);
}
template.header("Authorization", String.format("Bearer %s", tokenSupplier.getToken()));
};
}
private class SomeAuthTokenSupplier {
private volatile String token;
private volatile long retrievedOn = -1L;
String getToken() {
if (updateTokenRequired()) {
synchronized (this) {
if (updateTokenRequired()) {
BasicAuthResponse tokenResponse = getBasicAuthToken();
token = tokenResponse.getAccess_token(); // new token from some api should be assigned here
retrievedOn = Instant.now().toEpochMilli();
}
}
}
return token;
}
private boolean updateTokenRequired() {
return token == null || LocalDateTime.now().minusHours(8L).isAfter(LocalDateTime.ofInstant(Instant.ofEpochMilli(retrievedOn), ZoneId.systemDefault()));
}
}
#Override
public Retryer retryer() {
return new ClientRetry(250L, 2, 3) {
#Override
public void continueOrPropagate(RetryableException e) {
if (e.status() == 401 || e.status() == 403) {
tokenSupplier.token = null;
}
super.continueOrPropagate(e);
}
};
}
}
}
It worth using standard Spring Security OAuth2 Client feature instead in order to support authorization in Feign clients
See docs and code samples: https://docs.spring.io/spring-security/site/docs/current/reference/html5/#oauth2client
UPD
See another code sample: https://github.com/int128/feign-oauth2-example
If several service endpoints require different authentication, then it's worth having several Feign clients, each with own configuration
I do want to write some functional tests for our project. Techstack: Play Framework 1.5, Java 16, Junit 3.
I found following documentation:
test - 1.5.x
security - 1.5.x
So the Controller looks something like this.
#AllowFeature(Feature.SEARCH_BOX)
public static void search(String term) {
//implementation omitted
TablePage<MyAwesomeType> page = search(term);
render(..., page, term);
}
And my test looks like this
public class SearchTest extends FunctionalTest {
#Test
public void search_withResults() {
String term = "ABC";
Http.Response response = GET("/foo/search?term=" + term);
assertStatus(302, response);
assertThat(renderArgs("page"), is(notNullValue()));
TablePage<MyAwesomeType> page = (TablePage<MyAwesomeType>) renderArgs("page");
assertTrue(page.getTotalRecords() >= 1);
}
}
However, the TablePage<MyAwesomeType> page is null when it really should not be, and i am unable to step into the controller method with the debugger. So it looks like the controller method search(...) is not called at all.
The response Code is 302 - Found but I think this might be play suggestion it found the path /foo/search
My guess is that i need to setup some UserContext or send a authenticityToken along with the request. So play can check the required feature #AllowFeature(Feature.A_SEARCH_BOX).
Does anybody know how I would setup such a functional test?
Any help is appreciated. Thanks!
I was able to figure this out.
I need to log into the application and then the play FunctionalTest.class takes care of the cookie.
Add #NoAuthenticityto the login method
#NoAuthenticity // <-- This enables execution without authenticityToken
public static void login(#Required String username, #Required String password) {
...
}
Post a request to login before the test.
#Test
public void search_withResults() {
// 1. login
Map<String, String> credentials = Map.of("username", "MyUsername", "password", "MyPassword");
POST("/login", credentials);
// Note: session info / authenticityToken is stored in a cookie
// FunctionalTest.class makes sure to use this cookie for subsequent requests
// This request now works like a charm
String term = "ABC";
Http.Response response = GET("/foo/search?term=" + term);
assertStatus(302, response);
assertThat(renderArgs("page"), is(notNullValue()));
TablePage<MyAwesomeType> page = (TablePage<MyAwesomeType>) renderArgs("page");
assertTrue(page.getTotalRecords() >= 1);
}
Note: One can use the JUnit #Before Annotation to simplify the test class.
#Before
public void login(){
Map<String, String> credentials = Map.of("username", "MyUsername", "password", "MyPassword");
POST("/login", credentials);
}
#Test
public void search_withResults() {
String term = "ABC";
Http.Response response = GET("/foo/search?term=" + term);
assertStatus(302, response);
assertThat(renderArgs("page"), is(notNullValue()));
TablePage<MyAwesomeType> page = (TablePage<MyAwesomeType>) renderArgs("page");
assertTrue(page.getTotalRecords() >= 1);
}
#Test
public void anotherTest() { ... }
#Test
public void yetAnotherTest() { ... }
I am writing a Spring boot application and I have a question regarding my controller class. I read the documentation and saw that the controller is a singleton by deafult, so I have decided to save my user info in a DB and save it by session. I use the spring boot sesison managment JDBC. My question is though, if a method is also singelton. I know it seems like a foolish question but I am starting to second guess myself. I am confused if the code I have would fail if multiple users logged in with a session. Also, can I not have any variables in my methods, or should I move the logic I have here somewhere else. I really don't know.
Java Controller Method:
#RequestMapping(value = "/edit")
public ModelAndView editTab(#RequestParam(value = "currentAppcode", required = false, defaultValue = "Appcode") String currentAppcode,
#RequestParam(value = "currentAcro", required = false, defaultValue = "Arco") String currentAcro,
#RequestParam(value = "currentAppname", required = false, defaultValue = "Appname") String currentAppname,
HttpSession session) {
ModelAndView modelAndView = new ModelAndView();
private List<Some_Object> allQueryConfig = new ArrayList<Some_Object>();
session.setAttribute("SELECTED_APP",queryService.findDtoById(currentCode));
session.setAttribute("ALL_SERVERS", queryService.serverWork() );
allSystems.clear();
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String currentPrincipalName = authentication.getName();
try {
OPTApplication selected_app = (OPTApplication) session.getAttribute("SELECTED_APP");
rc.setProxy();
if (rc.validateUser(currentPrincipalName, selected_app.getSys_id()) != 0 || queryService.isAuditor(currentPrincipalName)) {
session.setAttribute("GP_SELECTED", apiRunner.runGPData(selected_app.getNumber()));
allQueryConfig = queryService.getNonDistinctServerInfo();
modelAndView.setViewName("create");
for (AppSelectorTier current : allQueryConfig) {
allSystems.add(current.getSystem());
}
} else {
modelAndView.setViewName("forbidden");
}
} catch (Exception e) {
e.printStackTrace();
modelAndView.setViewName("test");
}
modelAndView.addObject("currentAppName", "Application: " + currentAppname);
modelAndView.addObject("ary4", queryService.getServerInfo());
modelAndView.addObject("adid", currentPrincipalName);
modelAndView.addObject("hasAccess", false);
return modelAndView;
}
Another example:
public #ResponseBody List<SelectorDTO> getGPdataTaddm(#RequestBody TestApp mTestApp, HttpServletRequest request, HttpSession session)
throws SQLException {
GroupPatternDTO gp_selected = (GroupPatternDTO) session.getAttribute("GP_SELECTED");
session.setAttribute("QUERY_CONFIG_DATA", new QueryConfigData());
QueryConfigData query_config_data = (QueryConfigData) session.getAttribute("QUERY_CONFIG_DATA");
List<SelectorDTO> inProgressInfo = queryService.getInfoFromInProgress(gp_selected.getCode());
if (inProgressInfo.size() != 0) {
gp_selected.setSels(inProgressInfo);
} else {
for (SelectorDTO current : gp_selected.getSels()) {
String objectPassed = current.getDescription().replaceAll("[^a-zA-Z0-9]", "");
System.out.println("First match: ----------------------------------------------------------------------"
+ objectPassed);
if (queryService.getTierTypeFromObj(objectPassed).size() != 0) {
AppSelectorTier selectorObj = queryService.getTierTypeFromObj(taddmObjectPassed).get(0);
query_config_data.setName(selectorObj.getName());
query_config_data.setSystem(selectorObj.getSystem());
query_config_data.setMqlList(selectorObj.getMqllisting());
query_config_data.setTag1(selectorObj.getTagname_one());
query_config_data.setTag2(selectorObj.getTagname_two());
query_config_data.setMqlSelector(selectorObj.getMqllisting());
query_config_data.setParseInstruc(selectorObj.getParseinstruction());
query_config_data.setTaddmObject(selectorObj.getTaddmobj());
}
}
}
session.setAttribute("GP_SELECTED", gp_selected);
session.setAttribute("QUERY_CONFIG_DATA", query_config_data);
return gp_selected.getSels();
}
I am unsure if this is a good pracice or if for my usecase this is fine?
In my spring boot project, one of my Service depends on external service like Amazon. I am writing the integration testing of the Controller classes. So, I want to mock the method in the AmazonService class(as it depends on third party API). The method is void with a single Long argument and can throw a custom application-specific exceptions.
The method is as follows:-
class AmazonService{
public void deleteMultipleObjects(Long enterpriseId) {
String key = formApplicationLogokey(enterpriseId,null);
List<S3ObjectSummary> objects = getAllObjectSummaryByFolder(key);
List<DeleteObjectsRequest.KeyVersion> keys = new ArrayList<>();
objects.stream().forEach(object->keys.add(new DeleteObjectsRequest.KeyVersion(object.getKey())));
try{
DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(this.bucket).withKeys(keys);
this.s3client.deleteObjects(deleteObjectsRequest);
log.debug("All the Application logos deleted from AWS for the Enterprise id: {}",enterpriseId);
}
catch(AmazonServiceException e){
throw new AppScoreException(AppScoreErrorCode.OBJECT_NOT_DELETED_FROM_AWS);
}
}}
class Test
class Test
{
#Autowired
AmazonServiceImpl amazonService;
#Autowired
EnterpriseService enterpriseService;
#Before
public void init()
{
amazonService = Mockito.mock(AmazonServiceImpl.class);
Mockito.doNothing().when(amazonService).deleteMultipleObjects(isA(Long.class));
}
#Test
public void testDeleteEnterprise(){
setHeaders();
EnterpriseDTO enterpriseDTO = createEnterpriseEntity(null,"testDeleteEnterpriseName3",null,null,null);
String postUrl = TestUrlUtil.createURLWithPort(TestConstants.ADD_ENTERPRISE,port);
HttpEntity<EnterpriseDTO> request1 = new HttpEntity<>(enterpriseDTO,headers);
ResponseEntity<EnterpriseDTO> response1 = restTemplate.postForEntity(postUrl,request1,EnterpriseDTO.class);
assert response1 != null;
Long enterpriseId = Objects.requireNonNull(response1.getBody()).getId();
String url = TestUrlUtil.createURLWithPort(TestConstants.DELETE_ENTERPRISE,port)+File.separator+enterpriseId;
HttpEntity<EnterpriseDTO> request = new HttpEntity<>(null, headers);
ResponseEntity<Object> response = restTemplate.exchange(url,HttpMethod.DELETE,request,Object.class);
Assert.assertEquals(Constants.ENTERPRISE_DELETION_SUCCESS_MESSAGE,response.getBody());
}
}
class EnterpriseResource
class EnterpriseResource
{
#DeleteMapping("/enterprises/{enterpriseId}")
public ResponseEntity<Object> deleteEnterprise(#PathVariable Long enterpriseId) {
log.debug("REST request to delete Enterprise : {}", enterpriseId);
enterpriseService.delete(enterpriseId);
return ResponseEntity.badRequest().body(Constants.ENTERPRISE_DELETION_SUCCESS_MESSAGE);
}
}
class EnterpriseServiceImpl
class EnterpriseServiceImpl
{
#Override
public void delete(Long enterpriseId) {
log.debug("Request to delete Enterprise : {}", enterpriseId);
enterpriseRepository.deleteById(enterpriseId);
amazonService.deleteMultipleObjects(enterpriseId);
}
}
I have tried various approaches to Mock this method but it didn't work and control is going inside this method during debugging. I want to do nothing in this method during testing.
I have tried the various approaches like throw(), doNothing(), spy() etc.
Please help what is missing here?
Thanks
I'm trying for more than an hour to test this class. It went so ugly of stubbing the whole components of the method etc. I'd love some advice how to make a better test or refactor the class to make it way easier to test. I could not figure out a way yet.
Class to Test
#Slf4j
public final class HistoryRestService {
static RestTemplate restTemplate = new RestTemplate();
public static Optional<List<History>> findLatestHistories() {
String url = buildUrl();
ResponseEntity<History[]> responseEntity = null;
try {
responseEntity = restTemplate.getForEntity(url, History[].class);
} catch (ResourceAccessException e) {
log.warn("No connection to History persistence. Please check if the history persistence started up properly");
return Optional.empty();
}
History[] histories = responseEntity.getBody();
return Optional.of(Arrays.asList(histories));
}
private static String buildUrl() {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("http://");
stringBuilder.append("localhost");
stringBuilder.append(":8081");
stringBuilder.append("/history/get");
return stringBuilder.toString();
}
// For Testing
static void setRestTemplate(RestTemplate restTemplate) {
HistoryRestService.restTemplate = restTemplate;
}
}
Spock Test which fails
class HistoryRestServiceTest extends Specification {
def "test findLatestHistories"() {
given:
History mockedHistory = Mock()
HistoryRestService uut = new HistoryRestService()
History[] expected = [mockedHistory]
RestTemplate mockedRestTemplate = Stub()
ResponseEntity<History> mockedResponseEntity = Stub()
mockedResponseEntity.getBody() >> expected
mockedRestTemplate.getForEntity(_) >> mockedResponseEntity
uut.setRestTemplate(mockedRestTemplate)
when:
def actual = uut.findLatestHistories()
then:
actual.get() == expected
}
}
I'd suggest using real depedency-injection (spring/guice/cdi) instead of static variables.
Furthermore, you should think about what you want to test, is it the correct request and parsing of the network call, then write an integration test using something like mockserver or wiremock to have the whole stack. Or, if you are just concerned with the result handling, then you could move the code that interacts with RestTemplate into a separate method and use partial mocking to mock this method. I'd suggest to use the real integration test, but for the sake of an example this should work, but I didn't verify the code.
#Slf4j
public class HistoryRestService {
private final RestTemplate restTemplate;
public HistoryRestService() {
restTemplate = new RestTemplate();
}
public HistoryRestService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public Optional<List<History>> findLatestHistories() {
try {
return Optional.of(Arrays.asList(getLatestHistories(buildUrl())));
} catch (ResourceAccessException e) {
log.warn("No connection to History persistence. Please check if the history persistence started up properly");
return Optional.empty();
}
}
History[] getLatestHistories(String url) throws {
ResponseEntity<History[]> responseEntity = null;
responseEntity = restTemplate.getForEntity(url, History[].class);
return responseEntity.getBody()
}
private String buildUrl() {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("http://");
stringBuilder.append("localhost");
stringBuilder.append(":8081");
stringBuilder.append("/history/get");
return stringBuilder.toString();
}
}
class HistoryRestServiceTest extends Specification {
#Subject
HistoryRestService uut = Spy()
def "test findLatestHistories"() {
given:
History[] expected = [mockedHistory]
when:
def actual = uut.findLatestHistories()
then:
actual.get() == expected
1 * uut.getLatestHistories(_ as String) >> expected
}
def "test findLatestHistories returns empty on exceptions"() {
given:
History[] expected = [mockedHistory]
when:
def actual = uut.findLatestHistories()
then:
!actual.present
1 * uut.getLatestHistories(_ as String) >> {throw new ResourceAccessException()}
}
}