I'm trying to test a method which calls another method which makes use of a private final static variable initialized inline. I'm then trying to change the value for the variable when the tests run. I've been trying things such as spring reflection or adding a setter for that specific field but maybe I should use something as powermock, which I never used before. I'm on my own trying to implement this with no one to ask so if I could have some guidance on what is the best way to proceed, please.
UPDATE
Based on some feedback I got here, mocking the private final variable may not be what I should do for my my tests. I've opened a different issue here Mock return value for cookie.getValue() using Mockito
@Service
public class CookieSessionUtils {
private static final String VIADUCT_LOCAL_AMP = "viaductLocalAmp"; // Value to be changed when the test runs to test the "if Y" scenario.
public boolean verifyState(HttpServletRequest request, String state) {
String viaductLocalAmp = getCookieByName(request, VIADUCT_LOCAL_AMP);
if (viaductLocalAmp.equalsIgnoreCase("Y")) {
return true;
}
return false;
}
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 "";
}
These are a few of the things I've tried:
@Autowired
private CookieSessionUtils cookieSessionUtils;
@Mock
private HttpServletRequest request;
@Test
public void testVerifyState() {
Cookie mockCookie = Mockito.mock(Cookie.class);
Mockito.when(mockCookie.getName()).thenReturn("YviaductLocalAmp");
Mockito.when(request.getCookies()).thenReturn(new Cookie[]{mockCookie});
// cookieSessionUtils.setViaductLocalAmp("YviaductLocalAmp");
// setField(cookieSessionUtils, "VIADUCT_LOCAL_AMP", VIADUCT_LOCAL_AMP);
// Mockito.when(cookieSessionUtils.getCookieByName(request, "YviaductLocalAmp")).thenReturn("Y");
assertTrue(cookieSessionUtils.verifyState(httpServletRequest, "viaductLocalAmp"));
}
Thank you.
CodePudding user response:
Not sure if even Powermock will be able to help.
The recommended way is to not use inline constants which need to be changed while testing.
- If you want to change this, you need to introduce an interface in between which supplies constants. Use one implementation of it for the actual source and one for tests. Switch the implementation while testing.
If you do not want to change this, you could try the below approach with reflection
- Make the field accessible.
- Remove the final modifier
- edit the field value
I got this suggestion from this discussion
private static void setFinalStatic(Field field, Object newValue) throws Exception {
Field field = ClassWhereToMockStaticFinalVar.class.getDeclaredField("FieldName");
field.setAccessible(true);
// remove final modifier from field
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);
}
CodePudding user response:
You are trying to violate the information hiding / encapsulation principle because you are trying to "test code".
But UnitTests do not "test code".
Unit Tests verify public observable behavior, that is: return values and communication with dependencies.
The actual content of that constant is an implementation detail the Test should not care about.
So what you really should test is, that verifyState()
returns true
if the request contains a cookie with Name "viaductLocalAmp" and value "Y".