Package org.easetech.easytest.runner

Source Code of org.easetech.easytest.runner.InternalParameterizedStatement

package org.easetech.easytest.runner;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.easetech.easytest.exceptions.ParamAssertionError;
import org.easetech.easytest.internal.EasyAssignments;
import org.easetech.easytest.loader.Loader;
import org.easetech.easytest.reports.data.TestMethodDuration;
import org.easetech.easytest.reports.data.TestResultBean;
import org.easetech.easytest.util.CommonUtils;
import org.junit.Assert;
import org.junit.experimental.theories.PotentialAssignment;
import org.junit.experimental.theories.internal.Assignments;
import org.junit.internal.AssumptionViolatedException;
import org.junit.internal.runners.model.EachTestNotifier;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement;
import org.junit.runners.model.TestClass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* An internal class that holds the logic of running a given Test method. This class contains the common code for both
* {@link DataDrivenTestRunner} and SpringTestRunner that is present in the easytest-spring module.
*
* @author Anuj Kumar
*
*/
public class InternalParameterizedStatement extends Statement {
   
    private final Statement originalStatement;

    /**
     * An instance of logger associated with the test framework.
     */
    protected final Logger LOG = LoggerFactory.getLogger(InternalParameterizedStatement.class);


    /**
     * an instance of {@link FrameworkMethod} identifying the method to be tested.
     */
    private EasyFrameworkMethod fTestMethod;

    /**
     * A List of {@link Assignments}. Each member in the list corresponds to a single set of test data to be passed to
     * the test method. For eg. If the user has specified the test data in the CSV file as:<br>
     * <br>
     * <B>testGetItems,LibraryId,itemType,searchText</B> <br>
     * ,4,journal,batman <br>
     * ,1,ebook,potter <br>
     * where: <li>testGetItems is the name of the method</li> <li>
     * LibraryId,itemType,searchText are the names of the parameters that the test method expects</li> and <li>
     * ,4,journal,batman</li> <li>,1,ebook,potter</li> are the actual test data <br>
     * then this list will consists of TWO {@link Assignments} instances with values: <li>[[{LibraryId=4,
     * itemType=journal, searchText=batman}]]</li> AND <li>[[{LibraryId=1, itemType=ebook, searchText=potter}]]
     *
     */
    private List<EasyAssignments> listOfAssignments;

    /**
     * List of Invalid parameters
     */
    private List<AssumptionViolatedException> fInvalidParameters = new ArrayList<AssumptionViolatedException>();

    /**
     * An instance of {@link TestClass} identifying the class under test
     */
    private TestClass fTestClass;

    /**
     * The actual instance of the test class. This is extremely handy in cases where we want to reflectively set
     * instance fields on a test class.
     */
    private Object testInstance;

    public InternalParameterizedStatement(Statement originalStatement, EasyFrameworkMethod fTestMethod,
       
        TestClass testClass, Object testInstance) {
        this.fTestMethod = fTestMethod;
        this.listOfAssignments = new ArrayList<EasyAssignments>();
        this.fTestClass = testClass;
        this.testInstance = testInstance;
        this.originalStatement = originalStatement;

    }

    private TestClass getTestClass() {
        return fTestClass;
    }

    public void evaluate() throws Throwable {
        if(originalStatement != null) {
            originalStatement.evaluate();
        }
        runWithAssignment(EasyAssignments.allUnassigned(fTestMethod.getMethod(), getTestClass()));
       
       
    }

    /**
     * This method encapsulates the actual change in behavior from the traditional JUnit Theories way of populating and
     * supplying the test data to the test method. This method creates a list of {@link Assignments} identified by
     * {@link #listOfAssignments} and then calls {@link #runWithCompleteAssignment(EasyAssignments)} for each
     * {@link Assignments} element in the {@link #listOfAssignments}
     *
     * @param parameterAssignment an instance of {@link Assignments} identifying the parameters that needs to be
     *            supplied test data
     * @throws Throwable if any exception occurs.
     */
    protected void runWithAssignment(EasyAssignments parameterAssignment) throws Throwable {
        while (!parameterAssignment.isComplete()) {
            List<PotentialAssignment> potentialAssignments = parameterAssignment.potentialsForNextUnassigned(fTestMethod);
            boolean isFirstSetOfArguments = listOfAssignments.isEmpty();
            for (int i = 0; i < potentialAssignments.size(); i++) {
                if (isFirstSetOfArguments) {
                    EasyAssignments assignments = EasyAssignments
                        .allUnassigned(fTestMethod.getMethod(), getTestClass());
                    listOfAssignments.add(assignments.assignNext(potentialAssignments.get(i)));
                } else {
                    EasyAssignments assignments = listOfAssignments.get(i);
                    try {
                        listOfAssignments.set(i, assignments.assignNext(potentialAssignments.get(i)));
                    } catch (IndexOutOfBoundsException e) {
                        listOfAssignments.add(assignments.assignNext(potentialAssignments.get(i)));
                    }
                }

            }
            parameterAssignment = parameterAssignment.assignNext(null);
        }
        if (listOfAssignments.isEmpty()) {
            LOG.debug("The list of Assignments is null. It normally happens when the user has not supplied any parameters to the test.");
            LOG.debug(" Creating an instance of Assignments object with all its value unassigned.");
            listOfAssignments.add(EasyAssignments.allUnassigned(fTestMethod.getMethod(), getTestClass()));
        }
        for (EasyAssignments assignments : listOfAssignments) {
            runWithCompleteAssignment(assignments);
        }
    }

    /**
     * Run the test data with complete Assignments
     *
     * @param complete the {@link Assignments}
     * @throws InstantiationException if an error occurs while instantiating the method
     * @throws IllegalAccessException if an error occurs due to illegal access to the test method
     * @throws InvocationTargetException if an error occurs because the method is not invokable
     * @throws NoSuchMethodException if an error occurs because no such method with the given name exists.
     * @throws Throwable any other error
     */
    protected void runWithCompleteAssignment(final EasyAssignments complete) throws InstantiationException,
        IllegalAccessException, InvocationTargetException, NoSuchMethodException, Throwable {
       
        methodCompletesWithParameters(fTestMethod, complete, testInstance);
    }

    /**
     * This method is responsible for actually executing the test method as well as capturing the test data returned by
     * the test method. The algorithm to capture the output data is as follows:
     * <ol>
     * After the method has been invoked explosively, the returned value is checked. If there is a return value:
     * <li>We get the name of the method that is currently executing,
     * <li>We find the exact place in the test input data for which this method was executed,
     * <li>We put the returned result in the map of input test data. The entry in the map has the key :
     * {@link Loader#ACTUAL_RESULT} and the value is the returned value by the test method.
     * <li>If expected result{@link Loader#EXPECTED_RESULT} exist in user input data then we compare it with actual
     * result and put the test status either passed/failed. The entry in the map has the key :
     * {@link Loader#TEST_STATUS} and the value is the either PASSED or FAILED.
     *
     * We finally write the test data to the file.
     *
     * @param method an instance of {@link FrameworkMethod} that needs to be executed
     * @param complete an instance of {@link Assignments} that contains the input test data values
     * @param freshInstance a fresh instance of the class for which the method needs to be invoked.
     * @throws Throwable
     */
    private void methodCompletesWithParameters(final EasyFrameworkMethod method, final EasyAssignments complete,
        final Object freshInstance) throws Throwable{

        final RunNotifier testRunNotifier = new RunNotifier();
        final TestRunDurationListener testRunDurationListener = new TestRunDurationListener();
        testRunNotifier.addListener(testRunDurationListener);
        final EachTestNotifier eachRunNotifier = new EachTestNotifier(testRunNotifier, null);
       
        String currentMethodName = method.getMethod().getName();
        TestResultBean testResult = method.getTestResult();
        Map<String, Object> writableRow = method.getTestData();
        Object returnObj = null;
        try {
            final Object[] values = complete.getMethodArguments(true);

            testResult.setInput(method.getTestData());
            // invoke test method
            eachRunNotifier.fireTestStarted();
            LOG.debug("Calling method {} with values {}", method.getName(), values);
            returnObj = method.invokeExplosively(freshInstance, values);
            eachRunNotifier.fireTestFinished();
           
            TestMethodDuration testItemDurationBean = new TestMethodDuration(currentMethodName,
                testRunDurationListener.getStartInNano(), testRunDurationListener.getEndInNano());
            testResult.addTestItemDurationBean(testItemDurationBean);
            testResult.setOutput((returnObj == null) ? "void" : returnObj);
            testResult.setPassed(Boolean.TRUE);
           
            if (writableRow != null) {
                if (returnObj != null) {
                    LOG.debug("Data returned by method {} is {} :", method.getName(), returnObj);
                    writableRow.put(Loader.ACTUAL_RESULT, returnObj);
                    Object expectedResult = writableRow.get(Loader.EXPECTED_RESULT);
                    // if expected result exist in user input test data,
                    // then compare that with actual output result
                    // and write the status back to writable map data.
                    if (expectedResult != null) {
                        LOG.debug("Expected result exists");
                        if (expectedResult.toString().equals(returnObj.toString())) {
                            writableRow.put(Loader.TEST_STATUS, Loader.TEST_PASSED);
                        } else {
                            writableRow.put(Loader.TEST_STATUS, Loader.TEST_FAILED);

                        }
                    }

                }
                LOG.debug("testItemDurationBean:" + testItemDurationBean);
                if (testItemDurationBean != null) {
                    Double testDuration = CommonUtils.getRounded(testItemDurationBean.getRoundedMsDifference()
                        .doubleValue(), 3);
                    LOG.debug("testItemDurationBean.getRoundedMsDifference():" + testDuration);
                    writableRow.put(Loader.DURATION, testDuration);
                }
            }
        } catch (AssumptionViolatedException e) {
            eachRunNotifier.addFailedAssumption(e);
            handleAssumptionViolation(e);
        } catch (Throwable e) {

            if (e instanceof AssertionError) { // Assertion error
                testResult.setPassed(Boolean.FALSE);
                testResult.setResult(e.getMessage());                      

            } else { // Exception
                testResult.setException(Boolean.TRUE);
                testResult.setExceptionResult(e.toString());

            }
            eachRunNotifier.addFailure(e);
            throw e;
        } finally {
            eachRunNotifier.fireTestFinished();
        }
        //The test should fail in case the Actual Result returned by the test method did
        //not match the Expected result specified for the method in the test data file.
        if (writableRow != null && writableRow.get(Loader.TEST_STATUS) != null
            && writableRow.get(Loader.TEST_STATUS).equals(Loader.TEST_FAILED)) {
            Assert.fail("Actual Result returned by the method : [" + returnObj
                + "] did not match the expected result : [" + writableRow.get(Loader.EXPECTED_RESULT) + "]");
        }

    }

    protected void handleAssumptionViolation(AssumptionViolatedException e) {
        fInvalidParameters.add(e);
    }

   

   
}
TOP

Related Classes of org.easetech.easytest.runner.InternalParameterizedStatement

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.