All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dbflute.utflute.lastaflute.WebContainerTestCase Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2014-2024 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.dbflute.utflute.lastaflute;

import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;

import org.dbflute.helper.function.IndependentProcessor;
import org.dbflute.utflute.lastaflute.mock.MockResopnseBeanValidator;
import org.dbflute.utflute.lastaflute.mock.MockRuntimeFactory;
import org.dbflute.utflute.lastaflute.mock.TestingHtmlData;
import org.dbflute.utflute.lastaflute.mock.TestingJsonData;
import org.dbflute.utflute.lastaflute.validation.TestingValidationErrorAfter;
import org.dbflute.utflute.mocklet.MockletHttpServletRequest;
import org.dbflute.utflute.mocklet.MockletHttpServletRequestImpl;
import org.dbflute.utflute.mocklet.MockletHttpServletResponse;
import org.dbflute.utflute.mocklet.MockletHttpServletResponseImpl;
import org.dbflute.utflute.mocklet.MockletHttpSession;
import org.dbflute.utflute.mocklet.MockletServletConfig;
import org.lastaflute.core.direction.FwAssistantDirector;
import org.lastaflute.core.magic.ThreadCacheContext;
import org.lastaflute.core.message.MessageManager;
import org.lastaflute.di.core.ExternalContext;
import org.lastaflute.di.core.LaContainer;
import org.lastaflute.di.core.factory.SingletonLaContainerFactory;
import org.lastaflute.meta.DocumentGenerator;
import org.lastaflute.meta.SwaggerGenerator;
import org.lastaflute.meta.agent.yourswagger.YourSwaggerSyncAgent;
import org.lastaflute.meta.agent.yourswagger.YourSwaggerSyncOption;
import org.lastaflute.meta.exception.YourSwaggerDiffException;
import org.lastaflute.meta.swagger.web.LaActionSwaggerable;
import org.lastaflute.web.LastaWebKey;
import org.lastaflute.web.response.ActionResponse;
import org.lastaflute.web.response.HtmlResponse;
import org.lastaflute.web.response.JsonResponse;
import org.lastaflute.web.ruts.process.ActionRuntime;
import org.lastaflute.web.servlet.request.RequestManager;
import org.lastaflute.web.token.DoubleSubmitManager;
import org.lastaflute.web.token.DoubleSubmitTokenMap;
import org.lastaflute.web.validation.exception.ValidationErrorException;

import jakarta.annotation.Resource;
import jakarta.servlet.ServletContext;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;

/**
 * The base class of test cases for web environment with DI container. 
* You can use tests of LastaFlute components e.g. action, assist, logic, job. * *

Standard application structure:

*
 * WebContainerTestCase
 *  |-Unit[App]TestCase
 *      |-[Your]ActionTest
 * 
* *

You can test like this:

*
 * public void test_yourMethod() {
 *     // ## Arrange ##
 *     YourAction action = new YourAction();
 *     inject(action);
 * 
 *     // ## Act ##
 *     action.submit();
 * 
 *     // ## Assert ##
 *     assertTrue(action...);
 * }
 * 
* @author jflute * @since 0.5.1 (2015/03/22 Sunday) */ public abstract class WebContainerTestCase extends LastaFluteTestCase { // =================================================================================== // Attribute // ========= // ----------------------------------------------------- // Request Mock // ------------ /** The mock request of the test case execution. (NullAllowed: when no web mock or beginning or ending) */ private MockletHttpServletRequest _xmockRequest; /** The mock response of the test case execution. (NullAllowed: when no web mock or beginning or ending) */ private MockletHttpServletResponse _xmockResponse; // ----------------------------------------------------- // LastaFlute Component // -------------------- @Resource private FwAssistantDirector _assistantDirector; @Resource private MessageManager _messageManager; @Resource private RequestManager _requestManager; @Resource private DoubleSubmitManager _doubleSubmitManager; // =================================================================================== // Settings // ======== // ----------------------------------------------------- // Prepare Container // ----------------- @Override protected void xprepareTestCaseContainer() { super.xprepareTestCaseContainer(); if (!isSuppressRequestMock()) { xdoPrepareRequestMockContext(); } } /** * Does it suppress web-request mock? e.g. HttpServletRequest, HttpSession * @return The determination, true or false. */ protected boolean isSuppressRequestMock() { return false; } protected void xdoPrepareRequestMockContext() { // the servletConfig has been already created when container initialization final MockletServletConfig servletConfig = xgetCachedServletConfig(); if (servletConfig != null) { // basically true, just in case (e.g. might be overridden) xregisterRequestMockContext(servletConfig); } } protected void xregisterRequestMockContext(MockletServletConfig servletConfig) { // like S2ContainerFilter final LaContainer container = SingletonLaContainerFactory.getContainer(); final ExternalContext externalContext = container.getExternalContext(); final MockletHttpServletRequest request = createMockletHttpServletRequest(servletConfig.getServletContext()); final MockletHttpServletResponse response = createMockletHttpServletResponse(request); externalContext.setRequest(request); externalContext.setResponse(response); xkeepMockRequestInstance(request, response); // for web mock handling methods } protected MockletHttpServletRequest createMockletHttpServletRequest(ServletContext servletContext) { return new MockletHttpServletRequestImpl(servletContext, prepareMockServletPath()); } protected MockletHttpServletResponse createMockletHttpServletResponse(HttpServletRequest request) { return new MockletHttpServletResponseImpl(request); } protected String prepareMockServletPath() { // you can override return "/utservlet"; } protected void xkeepMockRequestInstance(MockletHttpServletRequest request, MockletHttpServletResponse response) { _xmockRequest = request; _xmockResponse = response; } @Override protected boolean maybeContainerResourceOverridden() { return super.maybeContainerResourceOverridden() || xisMethodOverridden("prepareMockServletPath"); } // ----------------------------------------------------- // Destroy Container // ----------------- @Override protected void xdestroyTestCaseContainer() { xclearRequestMockContext(); super.xdestroyTestCaseContainer(); } protected void xclearRequestMockContext() { final LaContainer container = SingletonLaContainerFactory.getContainer(); final ExternalContext externalContext = container.getExternalContext(); if (externalContext != null) { // just in case externalContext.setRequest(null); externalContext.setResponse(null); } xreleaseMockRequestInstance(); } protected void xreleaseMockRequestInstance() { _xmockRequest = null; _xmockResponse = null; } // =================================================================================== // Request Mock // ============ // ----------------------------------------------------- // LastaFlute // ---------- protected ActionRuntime getMockHtmlRuntime() { // MockAction@sea() return new MockRuntimeFactory().createHtmlRuntime(); } protected ActionRuntime getMockJsonRuntime() { // MockAction@land() return new MockRuntimeFactory().createJsonRuntime(); } // ----------------------------------------------------- // Request // ------- protected MockletHttpServletRequest getMockRequest() { return _xmockRequest; } protected void addMockRequestHeader(String name, String value) { final MockletHttpServletRequest request = getMockRequest(); if (request != null) { request.addHeader(name, value); } } @SuppressWarnings("unchecked") protected ATTRIBUTE getMockRequestParameter(String name) { final MockletHttpServletRequest request = getMockRequest(); return request != null ? (ATTRIBUTE) request.getParameter(name) : null; } protected void addMockRequestParameter(String name, String value) { final MockletHttpServletRequest request = getMockRequest(); if (request != null) { request.addParameter(name, value); } } @SuppressWarnings("unchecked") protected ATTRIBUTE getMockRequestAttribute(String name) { final MockletHttpServletRequest request = getMockRequest(); return request != null ? (ATTRIBUTE) request.getAttribute(name) : null; } protected void setMockRequestAttribute(String name, Object value) { final MockletHttpServletRequest request = getMockRequest(); if (request != null) { request.setAttribute(name, value); } } // ----------------------------------------------------- // Response // -------- protected MockletHttpServletResponse getMockResponse() { return _xmockResponse; } protected Cookie[] getMockResponseCookies() { final MockletHttpServletResponse response = getMockResponse(); return response != null ? response.getCookies() : null; } protected int getMockResponseStatus() { final MockletHttpServletResponse response = getMockResponse(); return response != null ? response.getStatus() : 0; } protected String getMockResponseString() { final MockletHttpServletResponse response = getMockResponse(); return response != null ? response.getResponseString() : null; } // ----------------------------------------------------- // Session // ------- /** * @return The instance of mock session. (NotNull: if no session, new-created) */ protected MockletHttpSession getMockSession() { return _xmockRequest != null ? (MockletHttpSession) _xmockRequest.getSession(true) : null; } protected void invalidateMockSession() { final MockletHttpSession session = getMockSession(); if (session != null) { session.invalidate(); } } @SuppressWarnings("unchecked") protected ATTRIBUTE getMockSessionAttribute(String name) { final MockletHttpSession session = getMockSession(); return session != null ? (ATTRIBUTE) session.getAttribute(name) : null; } protected void setMockSessionAttribute(String name, Object value) { final MockletHttpSession session = getMockSession(); if (session != null) { session.setAttribute(name, value); } } // =================================================================================== // Response Validation // =================== /** * Validate HTML data, evaluating HTML bean's validator annotations. *
     * // ## Act ##
     * HtmlResponse response = action.index(form);
     *
     * // ## Assert ##
     * TestingHtmlData htmlData = validateHtmlData(response);
     * htmlData.requiredList("beans", ProductBean.class).forEach(bean -> {
     *     assertEquals("...", bean.productName);
     * });
     * 
* @param response The HTML response to be validated. (NotNull) * @return The HTML data for testing. (NotNull) */ protected TestingHtmlData validateHtmlData(HtmlResponse response) { return new MockResopnseBeanValidator(_requestManager).validateHtmlData(response); } /** * Validate JSON data, evaluating JSON result's validator annotations. *
     * // ## Act ##
     * JsonResponse<ProductRowResult> response = action.index(form);
     *
     * // ## Assert ##
     * TestingJsonData<ProductRowResult> jsonData = validateJsonData(response);
     * ProductRowResult result = jsonData.getJsonResult();
     * ...
     * 
* @param The type of JSON bean. * @param response The HTML response to be validated. (NotNull) * @return The HTML data for testing. (NotNull) */ protected TestingJsonData validateJsonData(JsonResponse response) { return new MockResopnseBeanValidator(_requestManager).validateJsonData(response); } // =================================================================================== // Validation Error // ================ /** * Assert validation error of action. *
     * // ## Arrange ##
     * SignupAction action = new SignupAction();
     * inject(action);
     * SignupForm form = new SignupForm();
    
     * // ## Act ##
     * assertValidationError(() -> action.index(form)).handle(data -> {
     *     // ## Assert ##
     *     data.requiredMessageOf("sea", Required.class);
     * });
     * 
* @param noArgInLambda The callback for calling methods that should throw the validation error exception. (NotNull) * @return The after object that has handler of expected cause for chain call. (NotNull) */ protected TestingValidationErrorAfter assertValidationError(IndependentProcessor noArgInLambda) { final Set causeSet = new HashSet(); assertException(ValidationErrorException.class, () -> noArgInLambda.process()).handle(cause -> causeSet.add(cause)); return new TestingValidationErrorAfter(causeSet.iterator().next(), _messageManager, _requestManager); } /** * Evaluate validation error hook for action response. *
     * // if HTML response
     * assertException(ValidationErrorException.class, () -> action.update(form)).handle(cause -> {
     *     HtmlResponse response = hookValidationError(cause);
     *     TestingHtmlData htmlData = validateHtmlData(response);
     *     ...
     * });
     * 
* @param The type of action response, e.g. HtmlResponse, JsonResponse. * @param cause The exception of validation error. (NotNull) * @return The action response from validation error hook. * @deprecated use assertValidationError() */ @SuppressWarnings("unchecked") protected RESPONSE hookValidationError(ValidationErrorException cause) { return (RESPONSE) cause.getErrorHook().hook(); } // =================================================================================== // Token Assertion // =============== /** * Assert double submit token is saved in action execution. *
     * // ## Act ##
     * HtmlResponse response = action.index(memberId); // calls saveToken()
     *
     * // ## Assert ##
     * assertTokenSaved(action.getClass());
     * 
* @param groupType The group type to get double submit token, basically action type. (NotNull) */ protected void assertTokenSaved(Class groupType) { // for action that calls saveToken() final DoubleSubmitTokenMap tokenMap = _doubleSubmitManager.getSessionTokenMap().get(); final boolean condition = tokenMap.get(groupType).isPresent(); assertTrue("Not found the transaction token saved in session, so call saveToken(): tokenMap=" + tokenMap, condition); } /** * Mock double submit token is requested in the test process. *
     * // ## Arrange ##
     * MemberEditAction action = new MemberEditAction();
     * inject(action);
     * mockTokenRequested(action.getClass());
     * ...
     *
     * // ## Act ##
     * HtmlResponse response = action.update(form); // calls verifyToken()
     *
     * // ## Assert ##
     * assertTokenVerified();
     * 
* @param groupType The group type to get double submit token, basically action type. (NotNull) */ protected void mockTokenRequested(Class groupType) { // for action that calls verityToken() final String savedToken = _doubleSubmitManager.saveToken(groupType); getMockRequest().setParameter(LastaWebKey.TRANSACTION_TOKEN_KEY, savedToken); } /** * Mock double submit token is requested as double submit in the test process. *
     * // ## Arrange ##
     * MemberEditAction action = new MemberEditAction();
     * inject(action);
     * mockTokenRequestedAsDoubleSubmit(action.getClass());
     * ...
     *
     * // ## Act ##
     * // ## Assert ##
     * assertException(DoubleSubmittedRequestException.class, () -> action.update(form));
     * 
* @param groupType The group type to get double submit token, basically action type. (NotNull) */ protected void mockTokenRequestedAsDoubleSubmit(Class groupType) { // for action that calls verityToken() final String savedToken = _doubleSubmitManager.saveToken(groupType); getMockRequest().setParameter(LastaWebKey.TRANSACTION_TOKEN_KEY, savedToken); _doubleSubmitManager.verifyToken(groupType, () -> { // means first request done throw new IllegalStateException("no way"); }); } /** * Assert double submit token is verified in action execution. *
     * // ## Arrange ##
     * MemberEditAction action = new MemberEditAction();
     * inject(action);
     * mockTokenRequested(action.getClass());
     * ...
     *
     * // ## Act ##
     * HtmlResponse response = action.update(form); // calls verityToken()
     *
     * // ## Assert ##
     * assertTokenVerified();
     * 
*/ protected void assertTokenVerified() { // for action that calls verityToken() final boolean condition = _doubleSubmitManager.isFirstSubmittedRequest(); assertTrue("Not found the transaction token verification, so call verifyToken().", condition); } // =================================================================================== // Mock HTML Validation // ==================== /** * Mock HTML validate() call for thread cache adjustment.
* For example, in LastaRemoteApi, client error translation of HTML validation error needs this. *
     * // in yourDefaultRule()
     * rule.translateClientError(resource -> {
     *     ...
     *     return resource.asHtmlValidationError(messages);
     * });
     * 
     * ...
     * 
     * // in unit test
     * mockHtmlValidateCall();
     * assertValidationError(() -> bhv.requestProductList(param));
     * 
*/ protected void mockHtmlValidateCall() { // for e.g. remote api unit test ThreadCacheContext.registerValidatorErrorHook(() -> ActionResponse.undefined()); // dummy } // =================================================================================== // LastaDoc // ======== /** * Save meta data for rich LastaDoc. *
     * 1. call this method in your unit test
     * 2. execute FreeGen task (manage.sh 12) of DBFlute
     * 3. see auto-generated LastaDoc
     * 
*/ protected void saveLastaDocMeta() { createDocumentGenerator().saveLastaDocMeta(); } /** * Create document generator for rich LastaDoc. * @return The new-created document generator. (NotNull) */ protected DocumentGenerator createDocumentGenerator() { return new DocumentGenerator(); } // =================================================================================== // Swagger // ======= /** * Save meta data to use rich swagger in war deployment.
*
     * public void test_swaggerJson() {
     *     saveSwaggerMeta(new SwaggerAction());
     * }
     * 
* @param swaggerable The new-created swagger-able action to get swagger JSON. (NotNull) */ protected void saveSwaggerMeta(LaActionSwaggerable swaggerable) { assertNotNull(swaggerable); inject(swaggerable); createSwaggerGenerator().saveSwaggerMeta(swaggerable); } /** * Create swagger generator for rich swagger. * @return The new-created swagger generator. (NotNull) */ protected SwaggerGenerator createSwaggerGenerator() { return new SwaggerGenerator(); } /** * Verify that your swagger.json is synchronized with source codes.
* Basically for manual-made swagger.json driven development. * @param locationPath The location path to your swagger.json, can be resource path and filesystem path. (NotNull) * @param opLambda The callback for your swagger-sync option. (NotNull) * @throws YourSwaggerDiffException When your swagger.json has differences with source codes. */ protected void verifyYourSwaggerSync(String locationPath, Consumer opLambda) { createYourSwaggerSyncAgent().verifyYourSwaggerSync(locationPath, opLambda); } /** * Create agent for your swagger.json synchronization. * @return The new-created agent. (NotNull) */ protected YourSwaggerSyncAgent createYourSwaggerSyncAgent() { return new YourSwaggerSyncAgent(); } // =================================================================================== // Accessor // ======== protected MockletHttpServletRequest xgetMockRequest() { return _xmockRequest; } protected void xsetMockRequest(MockletHttpServletRequest xmockRequest) { _xmockRequest = xmockRequest; } protected MockletHttpServletResponse xgetMockResponse() { return _xmockResponse; } protected void xsetMockResponse(MockletHttpServletResponse xmockResponse) { _xmockResponse = xmockResponse; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy