I want to test my Servlet with different incoming URLs. I tried to use Mockito to test if specific function was called:
package servlet;
import blabla;
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration(locations = {"classpath:application-context-test.xml"})
public class MainServletTest extends TestCase{
#Autowired
private Categories categories;
private MockHttpServletRequest request = new MockHttpServletRequest();
#Mock
private HttpServletResponse response;
#Mock
private HttpSession session;
#Mock
private RequestDispatcher rd;
#Test
public void testCategories() throws ServletException, IOException {
// given
request.setServerName("localhost");//here I try to change incoming URL
request.setRequestURI("/test/categories");//here I try to change incoming URL
HttpServletRequest req = spy(request);//???
//when
new MainServlet().doGet(req, response);
//then
verify(req).setAttribute("categories", categories.getContainer());//here try to check if this method is called
}
}
Here I try to change incoming url and check if specific attribute was set for incoming request. Since req is not Mock object but MockHttpServletRequest object - this code does not work. Any ideas?
Either use a mock:
// ...
#Mock
private HttpServletRequest request;
// ...
#Test
public void testCategories() throws ServletException, IOException {
// given
when(request.getServerName()).thenReturn("localhost");
when(request.getRequestURI()).thenReturn("/test/categories")
//when
new MainServlet().doGet(req, response);
//then
verify(req).setAttribute("categories", categories.getContainer());
or
Use MockHttpServletRequest to check the attribute named categories:
assertEquals(categories.getContainer(), req.getAttributes("categories"));
Related
I am writing test case for servlets. In Test I am not able to intiallize Servlet Config variable. I am providing my code and test also
Code is :
public void service (HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
ServletConfig config = getServletConfig();
msSmtpServer = getServletConfig().getInitParameter("smptserver");
msSmtpPort = getServletConfig().getInitParameter("smtpport");
msUserName = getServletConfig().getInitParameter("username");
msPassword = getServletConfig().getInitParameter("password");
String buf = getRequestBuffer(request).toString();
and test is :
public class AddUserServletTest {
#Mock
HttpServletRequest request;
#Mock
HttpServletResponse response;
#Mock
HttpSession session;
#Mock
RequestDispatcher rd;
#Mock
ServletOutputStream servletOut;
#Mock
ServletConfig sg;
public final static String Seperator = new Character((char)1).toString();
public final static String ContentDelimeter = new Character((char)2).toString();
#BeforeClass
public void setUp() throws Exception
{
MockitoAnnotations.initMocks(this);
}
#Test
public void test() throws Exception {
DataSet ds=new DataSet();
String buffer=ds.getUserId()+Seperator+ds.getTableID()+Seperator+ds.getMemberID()+Seperator+ds.getNHID();
ByteArrayOutputStream bos = new ByteArrayOutputStream ();
ZipOutputStream out = new ZipOutputStream(bos);
out.putNextEntry(new ZipEntry("request.txt"));
out.write(buffer.getBytes("UTF-8"));
out.closeEntry();
out.close ();
bos.close();
String b64String = Base64.encodeBase64String(bos.toByteArray());
Reader inputString = new StringReader(b64String);
BufferedReader reqbuffer = new BufferedReader(inputString);
when(request.getReader()).thenReturn(reqbuffer);
when(sg.getInitParameter("smptserver")).thenReturn("abc.xyz.com");
when(sg.getInitParameter("smtpport")).thenReturn("80");
when(sg.getInitParameter("username")).thenReturn("password#xyz.com");
when(sg.getInitParameter("password")).thenReturn("abcd");
when(response.getOutputStream()).thenReturn(servletOut);
new AddUsers().service(request, response);
ArgumentCaptor<String> bufferCaptor = ArgumentCaptor.forClass(String.class);
verify(servletOut).print(bufferCaptor.capture());
String responseBody = bufferCaptor.getValue();
ZipInputStream zipIn = new ZipInputStream(new ByteArrayInputStream(Base64.decodeBase64(responseBody.toString().getBytes())));
zipIn.getNextEntry();
StringBuffer sb = new StringBuffer();
ByteArrayOutputStream out1 = new ByteArrayOutputStream();
IOUtils.copy(zipIn, out1);
sb = new StringBuffer();
sb.append(out1.toString("UTF-8"));
System.out.println("--------------- :"+sb.toString());
String[] res=sb.toString().split(Seperator);
AssertJUnit.assertEquals("Success",res[0]);
}
}
i tried to initialize but in servlet that values are null not getting values.
How to initialize ServletConfig variable in test using mockito?
The current code:
new AddUsers().service(request, response);
creates a new instance of the servlet and attempts to use it immediately so the instance has no awareness of sg.
Try this which allows the mock to be injected before using it:
AddUsers servlet = new AddUsers();
servlet.init(sg); // use the servlet life-cycle method to inject the mock
servlet.service(request, response);
Subjectively servlets are a terrible concept, at the very least because their initialisation breaks the concepts of encapsulation requiring a default constructor and delegating the initialisation to the init method instead of using constructor arguments. Nevertheless, one can work around by providing package visible constructors for test purposes. Essentially you need to be able to inject an instance of ServletConfig and you can follow the solution described by #Andres S or you can follow the following, general, not specific to servlets, pattern:
public class AddUsers implements Servlet... {
private final Supplier<ServletConfig> configSupplier = () -> some default call;
public AddUsers() {}
#VisibleForTesting // guava
AddUsers(Supplier<ServletConfig> configSupplier) {
this.configSupplier = configSupplier;
}
public void service (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletConfig config = configSupplier.get();
...
In test you just use the constructor left open for testing:
new AddUsers(() -> sg).service(request, response);
I try to test my security layer using MockMvc. I've write the following integration test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {ApplicationContextConfig.class,WebSecurityConfig.class})
#WebAppConfiguration
public class AuthenticationStatesIT {
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#Before
public void initMocks(){
this.mockMvc = MockMvcBuilders.webAppContextSetup(context)
.addFilter(new AuthenticationFilter(), "/*")
.build();
}
#Test
public void stage10_firstRequestForLoginPageShouldReturnProperPageAndAddUnauthenticatedStateToSession () throws Exception {
MvcResult mvcResult = mockMvc.perform(get("/"))
.andDo(print())
//.andExpect(cookie().exists("JSESSIONID"))
.andExpect(status().is3xxRedirection()).andReturn();
MockHttpSession session = (MockHttpSession) mvcResult.getRequest().getSession();
StatesAuthenticator authenticator = (StatesAuthenticator)session.getAttribute("authenticator");
AuthenticationState state = authenticator.getState();
Assert.assertNotNull(authenticator);
Assert.assertNotNull(state);
}
}
Everything work ok except one detail. The 'JSESSIONID' cookie is not creating. I am sure that the new session is created but the test 'andExpect(cookie().exists("JSESSIONID"))' is not passed. I am creating session as follows:
public class UnauthenticatedState implements AuthenticationState {
#Override
public void doAuthentication(StatesAuthenticator authentication,ServletRequest request,
ServletResponse response,FilterChain chain) throws IOException, ServletException {
authentication.setAuthentication(null);
HttpServletResponse httpResponse = (HttpServletResponse)response;
HttpServletRequest httpRequest = (HttpServletRequest)request;
//get the old session and invalidate if exists
HttpSession oldSession = httpRequest.getSession(false);
if (oldSession != null) {
oldSession.invalidate();
}
//generate a new session
HttpSession session = httpRequest.getSession(true);
session.setMaxInactiveInterval(300); // 5 minutes
session.setAttribute("authenticator", authentication);
authentication.setState(new AuthenticatingState());
httpResponse.sendRedirect("login");
}
}
When I run server and look for that cookie in browser everything is ok, the cookie exists. Can someone explain me why MockMvc do no set 'JSESSIONID'? Thanks for any help!
I have Servlet which add users in database. Servlet use instance of module for work with database he's alias DBJoint. And Servlet get instance of DBJoint from ServletContext.
#WebListener
public class ContextListener implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
final ServletContext servletContext =
servletContextEvent.getServletContext();
final DBJoint joint = new DBJointHandler(
"database_scripts",
"authentication_database");
servletContext.setAttribute("db", joint);
}
}
And in each Servlet when I need work with database I call ServletContext and get DBJoint by key "db".
public class AddUserServlet extends HttpServlet {
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setCharacterEncoding("UTF8");
try {
final boolean success = addUserInDatabase(req);
if (success) req.setAttribute("serverAnswer", EDIT_SUCCESS.get());
else req.setAttribute("serverAnswer", ERR_UNIQUE_L_P.get());
req.getRequestDispatcher(ANSWER.get())
.forward(req, resp);
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* Addition user in database.
*
* #return true if addition success, else false.
*/
private boolean addUserInDatabase(final HttpServletRequest req)
throws SQLException {
final User user = getUserFromRequest(req);
return getDatabaseExecutor().addUserAndGetSuccess(user);
}
/**
* Extracts user's data from HttpServletRequest.
*
* #return user from request.
*/
private User getUserFromRequest(final HttpServletRequest req) {
return new User(
req.getParameter("name"),
req.getParameter("login"),
req.getParameter("password"),
req.getParameter("email"),
req.getParameter("role")
);
}
/**
* Get executor database requests.
*/
private ScriptExecutor getDatabaseExecutor() throws SQLException {
final DBJoint db = (DBJoint) getServletContext().getAttribute("db");
return db.getDBScriptExecutor();
}
}
I need test my Servlet, and use mock object instead original object:
In this test I try verify what body doPost call addUser(User u) in executor of database script. But get NPE.
#Test
public void whenUserAddThenAddUserCall() throws ServletException, IOException, SQLException {
final AddUserServlet servlet = new AddUserServlet();
//mock http.
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class);
User user = new User("name", "login", "password", "email");
//mock database.
ScriptExecutor executor = mock(ScriptExecutor.class);
DBJoint joint = mock(DBJointHandler.class);
when(joint.getDBScriptExecutor()).thenReturn(executor);
final ServletContext context = request.getServletContext();
//In this place I get NPE but why?
context.setAttribute("db", joint);
servlet.doPost(request, response);
verify(executor).addUser(user);
}
Why I have NullPointerException? How test this class? Thank You!
you need to include these as well
ServletContext context = mock(ServletContext .class)
when(request.getServletContext()).thenReturn(context );
You are calling request.getServletContext() on the request object which is a mock.
Mocked objects do no provide real method implementations. Instead of the real getServletContext() implementation, a stubbed version of the method is called returning null.
A mock should be told what it needs to return when a method is called on it, you need to define behavior just like you did for the DBJoinHandler mock.
To solve the NPE, do something like this:
when(request.getServletContext()).thenReturn(context);
where context could be a ServletContext mock.
I am trying to write a junit for a spring controller whose signature is something like this
#RequestMapping(value = { "/addPharmcyInLookUpTable.form" }, method = { org.springframework.web.bind.annotation.RequestMethod.POST })
public String processSubmitAddPhl(#ModelAttribute PhrmcyAdmin phrmcyAdmin,
BindingResult result, SessionStatus status,
HttpServletRequest request) throws Exception {
.....
....
}
The junit for this is
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:/applicationContext.xml",
"classpath:/puela-app-config.xml" }, inheritLocations = true)
public class AddPharmacyInLookUpTableControllerTest {
public static junit.framework.Test suite() {
return new JUnit4TestAdapter(
AddPharmacyInLookUpTableControllerTest.class);
}
#InjectMocks
private AddPharmacyInLookUpTableController controller;
private static MockHttpServletRequest request;
private static MockHttpServletResponse response;
#Autowired
private HandlerMapping handlerMapping;
#Autowired
private HandlerAdapter handlerAdapter;
#BeforeClass
public static void runBeforeAllTest() throws Exception {
System.out.println("Running one time Setup");
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
}
private ModelAndView handle(final HttpServletRequest request,
final HttpServletResponse response) throws Exception {
final HandlerExecutionChain handler = handlerMapping
.getHandler(request);
Assert.assertNotNull(
"No handler found for request, check you request mapping",
handler);
final Object controller = handler.getHandler();
for (final HandlerInterceptor interceptor : handlerMapping.getHandler(
request).getInterceptors()) {
if (!interceptor.preHandle(request, response, controller)) {
return null;
}
}
return handlerAdapter.handle(request, response, controller);
}
#Test
public void processRequestAddPhl_post() throws Exception
{
PhrmcyAdmin phrmcyAdmin = new PhrmcyAdmin();
phrmcyAdmin.setPhlCalMailbox("Test");
phrmcyAdmin.setPhlMailPharmacy("FootHill");
request.setMethod("POST");
request.setRequestURI("/addPharmcyInLookUpTable.form");
// Code goes here
MockHttpSession session = new MockHttpSession();
ModelAndView mv = handle(request, response);
assertEquals(mv.getViewName(), "addPhrmcyInTable.view");
}
}
I am trying to send this model object phrmcyAdmin along with the request. Any idea how we can deal with the model object??
All of my controllers extend the following abstract class:
public abstract class AbstractController {
public HttpServletRequest request;
public HttpServletResponse response;
public ModelMap model;
}
Moreover, I implemented the following interceptor:
public class HttpRequestInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException {
if (handler instanceof AbstractController) {
AbstractController controller = (AbstractController) handler;
controller.request = request;
controller.response = response;
controller.model = new ModelMap();
}
return true;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
if (handler instanceof AbstractController && modelAndView != null) {
AbstractController controller = (AbstractController) handler;
modelAndView.addAllObjects(controller.model);
}
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
This is a solution I found to improve the factorization of my code, since you won't need to pass the request, the response and the model as method parameters within your controllers. The solution works fine, until I found this issue:
public class HomeController extends AbstractController {
#RequestMapping
public void download1() {
// use the parent attribute response
File file = new File(MY_FILE_PATH);
InputStream in = new BufferedInputStream(new FileInputStream(file));
ServletOutputStream out = response.getOutputStream();
IOUtils.copy(in, out);
response.flushBuffer();
}
#RequestMapping
public void download2(HttpServletResponse response) {
// use the response passed as parameter
File file = new File(MY_FILE_PATH);
InputStream in = new BufferedInputStream(new FileInputStream(file));
ServletOutputStream out = response.getOutputStream();
IOUtils.copy(in, out);
response.flushBuffer();
}
}
Both of the two methods above make the browser downloading a file, but the download1 one generated an empty file while the download2 generates the original file as it should. Any idea why?
Thanks to the debugger, I noticed that in the postHandle method of the interceptor, the download2 method generates a modelAndView which equals null, while the download1 one generated an instanciated one. This should mean something for the issue, but I can't find what.
How get a response instanciated when passed as a parameter of a controller's method?
Don't do this :
public abstract class AbstractController {
public HttpServletRequest request;
public HttpServletResponse response;
public ModelMap model;
}
Instance variables in controllers (which have default scope of singleton btw) is a bad idea.
Just make something like this (to a txt file):
#RequestMapping(value="/download", method=RequestMethod.GET, produces=MediaType.APPLICATION_OCTET_STREAM_VALUE)
#ResponseBody
public String download(HttpServletResponse response) throws IOException {
response.setContentType("application/force-download");
FileReader fr = new FileReader("/folder/file.extension");
return IOUtils.toString(fr); // IOUtils come from Apache Commons IO
}