Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to pass application context, servlet context, session, request and response to TestExecutor #12

Open
dtrunk90 opened this issue Dec 11, 2015 · 1 comment

Comments

@dtrunk90
Copy link

It's currently not supported to use java config out of the box.
You need to pass the mocks created from Spring to the TestExecutor which is only possible by a custom implementation of IProcessingContextBuilder:

@WebAppConfiguration
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class MyTest {
    @Configuration
    public static class ContextConfiguration {
        // some beans
    }

    @Autowired
    private WebApplicationContext applicationContext;

    @Autowired
    private MockServletContext servletContext;

    @Autowired
    private MockHttpServletRequest request;

    @Autowired
    private MockHttpServletResponse response;

    @Autowired
    private ServletWebRequest webRequest;

    private TestExecutor executor;

    @Before
    public void setUp() {
        IProcessingContextBuilder processingContextBuilder = new CustomContextBuilder(applicationContext, servletContext, session, request, response, webRequest);

        List<IDialect> dialects = new ArrayList<IDialect>();
        dialects.add(new SpringStandardDialect());

        executor = new TestExecutor();
        executor.setProcessingContextBuilder(processingContextBuilder);
        executor.setDialects(dialects);
    }

    // some tests
}

And this is where the trouble begins: You cannot simply extend from SpringWebProcessingContextBuilder as most of the methods createApplicationContext, createMockServletContext, createMockHttpServletRequest, createMockHttpServletResponse are final and the ServletWebRequest will be initialized directly in doAdditionalVariableProcessing instead of an own method.

So I ended up in copying all the stuff of the classes which works but is really ugly:

public class CustomContextBuilder implements IProcessingContextBuilder {
    public static final Locale DEFAULT_LOCALE = Locale.ENGLISH;

    private static final String REQUEST_PARAMS_PREFIX = "param";
    private static final String REQUEST_ATTRS_PREFIX = "request";
    private static final String SESSION_ATTRS_PREFIX = "session";
    private static final String SERVLETCONTEXT_ATTRS_PREFIX = "application";

    private final WebApplicationContext applicationContext;
    private final ServletContext servletContext;
    private final HttpServletRequest request;
    private final HttpServletResponse response;
    private final ServletWebRequest webRequest;

    public JawrDialectContextBuilder(WebApplicationContext applicationContext, ServletContext servletContext, HttpServletRequest request, HttpServletResponse response, ServletWebRequest webRequest) {
        this.applicationContext = applicationContext;
        this.servletContext = servletContext;
        this.request = request;
        this.response = response;
        this.webRequest = webRequest;
    }

    @Override
    public IContext build(ITest test) {
        if (test == null) {
            return null;
        }

        ITestContext testContext = test.getContext();

        Locale locale = DEFAULT_LOCALE;
        ITestContextExpression localeExpression = testContext.getLocale();
        if (localeExpression != null) {
            Object exprResult = localeExpression.evaluate(Collections.<String, Object> emptyMap(), DEFAULT_LOCALE);
            if (exprResult != null) {
                locale = LocaleUtils.toLocale(exprResult.toString());
            }
        }

        Map<String, Object> variables = new HashMap<String, Object>();

        Map<String, Object[]> requestParameters = new LinkedHashMap<String, Object[]>();
        variables.put(REQUEST_PARAMS_PREFIX, requestParameters);

        Map<String, Object> requestAttributes = new LinkedHashMap<String, Object>();
        variables.put(REQUEST_ATTRS_PREFIX, requestAttributes);

        Map<String, Object> sessionAttributes = new LinkedHashMap<String, Object>();
        variables.put(SESSION_ATTRS_PREFIX, sessionAttributes);

        Map<String, Object> servletContextAttributes = new LinkedHashMap<String, Object>();
        variables.put(SERVLETCONTEXT_ATTRS_PREFIX, servletContextAttributes);

        for (Map.Entry<String, ITestContextExpression> entry : testContext.getVariables().entrySet()) {
            resolve(entry.getKey(), entry.getValue(), variables, locale);
        }

        for (Map.Entry<String, ITestContextExpression[]> entry : testContext.getRequestParameters().entrySet()) {
            int firstPoint = entry.getKey().indexOf('.');

            String paramName = firstPoint == -1 ? entry.getKey() : entry.getKey().substring(0, firstPoint);
            Object[] paramValues = new Object[entry.getValue().length];

            requestParameters.put(paramName, paramValues);

            String remainder = firstPoint == -1 ? "" : entry.getKey().substring(firstPoint);
            int expressionsLen = entry.getValue().length;

            for (int i = 0; i < expressionsLen; i++) {
                resolve(REQUEST_PARAMS_PREFIX + "." + paramName + "[" + i + "]" + remainder, entry.getValue()[i], variables, locale);
            }
        }

        for (Map.Entry<String, ITestContextExpression> entry : testContext.getRequestAttributes().entrySet()) {
            resolve(REQUEST_ATTRS_PREFIX + "." + entry.getKey(), entry.getValue(), variables, locale);
        }

        for (Map.Entry<String, ITestContextExpression> entry : testContext.getSessionAttributes().entrySet()) {
            resolve(SESSION_ATTRS_PREFIX + "." + entry.getKey(), entry.getValue(), variables, locale);
        }

        for (Map.Entry<String, ITestContextExpression> entry : testContext.getServletContextAttributes().entrySet()) {
            resolve(SERVLETCONTEXT_ATTRS_PREFIX + "." + entry.getKey(),entry.getValue(), variables, locale);
        }

        variables.remove(REQUEST_PARAMS_PREFIX);
        variables.remove(REQUEST_ATTRS_PREFIX);
        variables.remove(SESSION_ATTRS_PREFIX);
        variables.remove(SERVLETCONTEXT_ATTRS_PREFIX);

        doAdditionalVariableProcessing(test, request, response, servletContext, locale, variables);
        return new WebContext(request, response, servletContext, locale, variables);
    }

    private void resolve(String expression, ITestContextExpression contextExpression, Map<String, Object> variables, Locale locale) {
        try {
            Object result = contextExpression.evaluate(variables, locale);

            Object parsedExpression = Ognl.parseExpression(expression);
            Ognl.setValue(parsedExpression, variables, result);
        } catch (Throwable t) {
            throw new TestEngineExecutionException("Exception while trying to evaluate expression \"" + expression + "\" on context for test \"" + TestExecutor.getThreadTestName() + "\"", t);
        }
    }

    private void doAdditionalVariableProcessing(ITest test, HttpServletRequest request, HttpServletResponse response, ServletContext servletContext, Locale locale, Map<String, Object> variables) {
        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, applicationContext);

        request.setAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE, applicationContext);

        RequestContextHolder.setRequestAttributes(webRequest);

        ConversionService conversionService = getConversionService(applicationContext);

        RequestContext requestContext = new RequestContext(request, response, servletContext, variables);
        variables.put(AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE, requestContext);

        additionalVariableProcessing(applicationContext, conversionService, variables);

        initializeBindingResults(test, conversionService, locale, variables);

    }

    private ConversionService getConversionService(ApplicationContext applicationContext) {
        if (applicationContext == null) {
            return null;
        }

        Map<String, ConversionService> conversionServices = applicationContext.getBeansOfType(ConversionService.class);
        if (conversionServices.size() == 0) {
            return null;
        }

        return (ConversionService) conversionServices.values().toArray()[0];
    }

    private void additionalVariableProcessing(ApplicationContext applicationContext, ConversionService conversionService, Map<String, Object> variables) {
        RequestContext requestContext = (RequestContext) variables.get(AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE);
        variables.put(SpringContextVariableNames.SPRING_REQUEST_CONTEXT, requestContext);

        ThymeleafEvaluationContext evaluationContext = new ThymeleafEvaluationContext(applicationContext, conversionService);

        variables.put(ThymeleafEvaluationContext.THYMELEAF_EVALUATION_CONTEXT_CONTEXT_VARIABLE_NAME, evaluationContext);
    }

    private void initializeBindingResults(ITest test, ConversionService conversionService, Locale locale, Map<String, Object> variables) {
        List<String> variableNames = new ArrayList<String>(variables.keySet());
        for (String variableName : variableNames) {
            Object bindingObject = variables.get(variableName);
            if (isBindingCandidate(variableName, bindingObject)) {
                String bindingVariableName = BindingResult.MODEL_KEY_PREFIX + variableName;
                if (!variables.containsKey(bindingVariableName)) {
                    WebDataBinder dataBinders = createBinding(test, variableName, bindingVariableName, bindingObject, conversionService, locale, variables);
                    variables.put(bindingVariableName, dataBinders.getBindingResult());
                }
            }
        }
    }

    private boolean isBindingCandidate(String variableName, Object bindingObject) {
        if (variableName.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
            return false;
        }

        return bindingObject != null && !bindingObject.getClass().isArray() && !(bindingObject instanceof Collection) && !(bindingObject instanceof Map) && !BeanUtils.isSimpleValueType(bindingObject.getClass());
    }

    private WebDataBinder createBinding(ITest test, String variableName, String bindingVariableName, Object bindingObject, ConversionService conversionService, Locale locale, Map<String, Object> variables) {
        WebDataBinder dataBinder = new WebDataBinder(bindingObject, bindingVariableName);
        dataBinder.setConversionService(conversionService);
        return dataBinder;
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants