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);
Related
I have a filter in my application
#Component
#Order(2)
public class RequestResponseLoggingFilter implements Filter {
#Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
// SET VALUE OF OBJECT
}
// other methods
}
I have a Restcall which uses a class.
#RequestMapping
Class Test{
#PostMapping("/test")
public void postEntry(#Valid #RequestBody Testing testing){
}
}
Class Testing{
#NotNull(message="ERROR")
String id;
....
}
I get the id in my filter and I would like to set the id of Testing class in my Filter. Is this possible?
You can use MockHttpServletRequest something like this
#Test
public void testAddEventWithWebAuthenticationDetails() {
HttpSession session = new MockHttpSession(null, "test-session-id");
MockHttpServletRequest request = new MockHttpServletRequest();
request.setSession(session);
request.setRemoteAddr("1.2.3.4");
WebAuthenticationDetails details = new WebAuthenticationDetails(request);
Map<String, Object> data = new HashMap<>();
data.put("test-key", details);
AuditEvent event = new AuditEvent("test-user", "test-type", data);
customAuditEventRepository.add(event);
List<PersistentAuditEvent> persistentAuditEvents = persistenceAuditEventRepository.findAll();
assertThat(persistentAuditEvents).hasSize(1);
PersistentAuditEvent persistentAuditEvent = persistentAuditEvents.get(0);
assertThat(persistentAuditEvent.getData().get("remoteAddress")).isEqualTo("1.2.3.4");
assertThat(persistentAuditEvent.getData().get("sessionId")).isEqualTo("test-session-id");
}
More examples here
or
if you want to do it filter way
few Points Before that
Request body can be read only once.
If you read the body in a filter, the target servlet will not be able to re-read it and this will also cause IllegalStateException.
You will need ServletRequestWrapper or its child: HttpServletRequestWrapper so that you can read HTTP request body and then the servlet can still read it later.
Workflow will be
The only way would be for you to consume the entire input stream yourself in the filter.
Take what you want from it, and then create a new InputStream for the content you read.
Put that InputStream in to a ServletRequestWrapper (or HttpServletRequestWrapper).
// Sample Wrapper class where you can read body and modify body content
public class SampleHttpServletRequest
extends HttpServletRequestWrapper {
private ByteArrayOutputStream cachedBytes;
public SampleHttpServletRequest(HttpServletRequest request) {
super(request);
}
#Override
public ServletInputStream getInputStream() throws IOException {
if (cachedBytes == null)
cacheInputStream();
return new CachedServletInputStream();
}
#Override
public BufferedReader getReader() throws IOException{
return new BufferedReader(new InputStreamReader(getInputStream()));
}
private void cacheInputStream() throws IOException {
cachedBytes = new ByteArrayOutputStream();
IOUtils.copy(super.getInputStream(), cachedBytes);
}
public class CachedServletInputStream extends ServletInputStream {
private ByteArrayInputStream input;
public CachedServletInputStream() {
input = new ByteArrayInputStream(cachedBytes.toByteArray());
}
#Override
public int read() throws IOException {
return input.read();
}
}
}
Filter class
public class MyFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
/* wrap the request in order to read the inputstream multiple times */
MultiReadHttpServletRequest multiReadRequest = new MultiReadHttpServletRequest((HttpServletRequest) request);
doMyThing(multiReadRequest.getInputStream());
chain.doFilter(multiReadRequest, response);
}
}
Refer these post for more detail
Http Servlet request lose params from POST body after read it once
HttpServletRequestWrapper, example implementation for setReadListener / isFinished / isReady?
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 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"));
this is my first time that I work on a J2EE project and I have the following problem:
I have the following servlet that implement the HttpServlet interface.
public class Salwf extends HttpServlet {
private String msg = StringUtils.EMPTY;
public void init(ServletConfig config) throws ServletException {
super.init(config);
StandardConfigurationFactory standardConfigurationFactory = new StandardConfigurationFactory();
try {
standardConfigurationFactory.init();
} catch (ConfigurationException e) {
throw new ServletException(e);
}
ConfigurationFactory.setInstance(standardConfigurationFactory);
}
public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
LoggerMDC.setup(req, res);
Logger logger = (Logger) Logger.getStdLogger(Monitoraggio.class); // do not declare 'logger' as static field in order to work with MDC
String service = req.getParameter("serv");
char serviceId = Utility.getServizio(req.getParameter("serv"));
// The collection that have to be shown in a table inside the JSP view:
SalDettaglio[] salDettaglio = this.getSalDettaglioList();
gotoPage(ConfigurationFactory.getPropertiesPages().getProperty("pagina_salwf"), req, res);
//String paginaDaLanciare = lanciaServizio(serviceId, req, res);
}
/*
private String lanciaServizio(char num_servizio, HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
Logger logger = (Logger) Logger.getStdLogger(Monitoraggio.class); // do not declare 'logger' as static field in order to work with MDC
HttpSession session;
}
*/
private void gotoPage(String address, HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
Logger logger = (Logger) Logger.getStdLogger(Monitoraggio.class); // do not declare 'logger' as static field in order to work with MDC
logger.debug("gotoPage() | address: " + address);
ServletContext ctx = getServletConfig().getServletContext();
RequestDispatcher dispatcher = ctx.getRequestDispatcher(address);
dispatcher.forward(req, res);
}
private SalDettaglio[] getSalDettaglioList(){
SalDettaglio[] salDettaglioArrayResult;
List<SalDettaglio> salDettaglioList = new ArrayList<SalDettaglio>();
List<RM> rmList = new ArrayList<RM>();
RM rm1 = new RM("codiceRM1", "Andrea", "01/01/1014", "acqRiserva1", "consegnaFinale1", "descrizioneRM1", BigDecimal.valueOf(1000));
RM rm2 = new RM("codiceRM2", "Andrea", "01/01/1014", "acqRiserva2", "consegnaFinale2", "descrizioneRM2", BigDecimal.valueOf(1000));
rmList.add(rm1);
rmList.add(rm2);
RM[] rmArray = (RM[]) rmList.toArray();
SalDettaglio salDettaglio1 = new SalDettaglio("codice1", "stato1", "01/01/2014", "01/06/2014", "Andrea",
"Andrea", "Andrea", BigDecimal.valueOf(1000), "fornitore1",
"rmConRiserva1", "errore1", null, rmArray);
SalDettaglio salDettaglio2 = new SalDettaglio("codice2", "stato2", "01/01/2014", "01/06/2014", "Andrea",
"Andrea", "Andrea", BigDecimal.valueOf(1000), "fornitore2",
"rmConRiserva2", "errore2", null, rmArray);
salDettaglioList.add(salDettaglio1);
salDettaglioList.add(salDettaglio2);
salDettaglioArrayResult = (SalDettaglio[]) salDettaglioList.toArray();
return salDettaglioArrayResult;
}
}
As you can see into the service() method I have this array definition:
SalDettaglio[] salDettaglio
Now I have to put it into session so that I can use it into a JSP view.
How can I do it?
HttpSession session = req.getSession();
session.setAttribute("salDettaglio", salDettaglio);
This puts salDettaglio in the session and you can acces it with the attribute identifier salDettaglio keep in mind that it stores it as a plain Object so you'll have to cast in in the JSP if you are using sciplets.
Also see this question
Beware that Session's getAttribute / setAttribute methods are synchronized, but what you store with them is your responsibility. That means if you change a mutable field in the stored object that is not guaranteed to be seen by another thread. Since servlets are called by different threads from a thread-pool this can be a problem. To avoid this use either immutable objects or make your objects thread-safe.
create one session object and put your array into that session object and using sendRedirect() you can fwd your session through it..!!
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
}