Package org.jmock

Source Code of org.jmock.Mockery$MockObject

package org.jmock;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.hamcrest.Description;
import org.hamcrest.SelfDescribing;
import org.jmock.api.Expectation;
import org.jmock.api.ExpectationError;
import org.jmock.api.ExpectationErrorTranslator;
import org.jmock.api.Imposteriser;
import org.jmock.api.Invocation;
import org.jmock.api.Invokable;
import org.jmock.api.MockObjectNamingScheme;
import org.jmock.internal.CaptureControl;
import org.jmock.internal.ExpectationBuilder;
import org.jmock.internal.ExpectationCapture;
import org.jmock.internal.InvocationDispatcher;
import org.jmock.internal.InvocationDiverter;
import org.jmock.internal.InvocationToExpectationTranslator;
import org.jmock.internal.NamedSequence;
import org.jmock.internal.ObjectMethodExpectationBouncer;
import org.jmock.internal.ProxiedObjectIdentity;
import org.jmock.internal.ReturnDefaultValueAction;
import org.jmock.lib.CamelCaseNamingScheme;
import org.jmock.lib.IdentityExpectationErrorTranslator;
import org.jmock.lib.JavaReflectionImposteriser;


/**
* A Mockery represents the context, or neighbourhood, of the object(s) under test. 
*
* The neighbouring objects in that context are mocked out. The test specifies the
* expected interactions between the object(s) under test and its  neighbours and
* the Mockery checks those expectations while the test is running.
*
* @author npryce
* @author smgf
* @author named by Ivan Moore.
*/
public class Mockery implements SelfDescribing {
    private Set<String> mockNames = new HashSet<String>();
    private Imposteriser imposteriser = JavaReflectionImposteriser.INSTANCE;
    private ExpectationErrorTranslator expectationErrorTranslator = IdentityExpectationErrorTranslator.INSTANCE;
    private MockObjectNamingScheme namingScheme = CamelCaseNamingScheme.INSTANCE;
   
    private ReturnDefaultValueAction defaultAction = new ReturnDefaultValueAction(imposteriser);
   
    private InvocationDispatcher dispatcher = new InvocationDispatcher();
    private Throwable firstError = null;
   
    private List<Invocation> actualInvocations = new ArrayList<Invocation>();
   
   
    /*
     * Policies
     */
   
    /**
     * Sets the result returned for the given type when no return value has been explicitly
     * specified in the expectation.
     *
     * @param type
     *    The type for which to return <var>result</var>.
     * @param result
     *    The value to return when a method of return type <var>type</var>
     *    is invoked for which an explicit return value has has not been specified.
     */
    public void setDefaultResultForType(Class<?> type, Object result) {
        defaultAction.addResult(type, result);
    }
   
    /**
     * Changes the imposteriser used to adapt mock objects to the mocked type.
     *
     * The default imposteriser allows a test to mock interfaces but not
     * classes, so you'll have to plug a different imposteriser into the
     * Mockery if you want to mock classes.
     */
    public void setImposteriser(Imposteriser imposteriser) {
        this.imposteriser = imposteriser;
        this.defaultAction.setImposteriser(imposteriser);
    }
   
    /**
     * Changes the naming scheme used to generate names for mock objects that
     * have not been explicitly named in the test.
     *
     * The default naming scheme names mock objects by lower-casing the first
     * letter of the class name, so a mock object of type BananaSplit will be
     * called "bananaSplit" if it is not explicitly named in the test.
     */
    public void setNamingScheme(MockObjectNamingScheme namingScheme) {
        this.namingScheme = namingScheme;
    }
   
    /**
     * Changes the expectation error translator used to translate expectation
     * errors into errors that report test failures.
     *
     * By default, expectation errors are not translated and are thrown as
     * errors of type {@link ExpectationError}.  Plug in a new expectation error
     * translator if you want your favourite test framework to report expectation
     * failures using its own error type.
     */
    public void setExpectationErrorTranslator(ExpectationErrorTranslator expectationErrorTranslator) {
        this.expectationErrorTranslator = expectationErrorTranslator;
    }
   
    /*
     * API
     */
   
    /**
     * Creates a mock object of type <var>typeToMock</var> and generates a name for it.
     */
    public <T> T mock(Class<T> typeToMock) {
    return mock(typeToMock, namingScheme.defaultNameFor(typeToMock));
  }
   
    /**
     * Creates a mock object of type <var>typeToMock</var> with the given name.
     */
    public <T> T mock(Class<T> typeToMock, String name) {
        if (mockNames.contains(name)) {
            throw new IllegalArgumentException("a mock with name " + name + " already exists");
        }
       
        MockObject mock = new MockObject(typeToMock, name);
        mockNames.add(name);
       
        ProxiedObjectIdentity invokable =
            new ProxiedObjectIdentity(
                new InvocationDiverter<CaptureControl>(
                    CaptureControl.class, mock, mock));
       
        return imposteriser.imposterise(invokable, typeToMock, CaptureControl.class);
    }
   
    /**
     * Returns a new sequence that is used to constrain the order in which
     * expectations can occur.
     *
     * @param name
     *     The name of the sequence.
     * @return
     *     A new sequence with the given name.
     */
    public Sequence sequence(String name) {
        return new NamedSequence(name);
    }
   
    /**
     * Returns a new state machine that is used to constrain the order in which
     * expectations can occur.
     *
     * @param name
     *     The name of the state machine.
     * @return
     *     A new state machine with the given name.
     */
    public States states(String name) {
        return dispatcher.newStateMachine(name);
    }
   
    /**
     * Specifies the expected invocations that the object under test will perform upon
     * objects in its context during the test.
     *
     * The builder is responsible for interpreting high-level, readable API calls to
     * construct an expectation.
     */
  public void checking(ExpectationBuilder expectations) {
      expectations.buildExpectations(defaultAction, dispatcher);
    }
 
    /**
     * Adds an expected invocation that the object under test will perform upon
     * objects in its context during the test.
     *
     * This method allows a test to define an expectation explicitly, bypassing the
     * high-level API, if desired.
     */
    public void addExpectation(Expectation expectation) {
        dispatcher.add(expectation);
    }
 
    /**
     * Fails the test if there are any expectations that have not been met.
     */
  public void assertIsSatisfied() {
        firstError = null;
        if (!dispatcher.isSatisfied()) {
            throw expectationErrorTranslator.translate(new ExpectationError("not all expectations were satisfied", dispatcher));
        }
  }
   
    public void describeTo(Description description) {
        description.appendDescriptionOf(dispatcher)
                   .appendText("\nwhat happened before this:");
       
        if (actualInvocations.isEmpty()) {
            description.appendText(" nothing!");
        }
        else {
            description.appendList("\n  ", "\n  ", "\n", actualInvocations);
        }
    }
   
    private Object dispatch(Invocation invocation) throws Throwable {
        if (firstError != null) {
            throw firstError;
        }
       
        try {
            Object result = dispatcher.dispatch(invocation);
            actualInvocations.add(invocation);
            return result;
        }
        catch (ExpectationError e) {
            firstError = expectationErrorTranslator.translate(fillInDetails(e));
            firstError.setStackTrace(e.getStackTrace());
            throw firstError;
        }
        catch (Throwable t) {
            actualInvocations.add(invocation);
            throw t;
        }
    }
   
    // The ExpectationError might not have the expectations field set (because of a design
    // flaw that cannot be fixed without breaking backward compatibility of client code).
    // So if it is null we create a new ExpectationError with the field set to the mockery's
    // expectations.
    private ExpectationError fillInDetails(ExpectationError e) {
        ExpectationError filledIn = new ExpectationError(e.getMessage(), this, e.invocation);
        filledIn.setStackTrace(e.getStackTrace());
        return filledIn;
    }

    private class MockObject implements Invokable, CaptureControl {
        private Class<?> mockedType;
        private String name;
       
        public MockObject(Class<?> mockedType, String name) {
            this.name = name;
            this.mockedType = mockedType;
        }
       
        @Override
        public String toString() {
            return name;
        }
       
        public Object invoke(Invocation invocation) throws Throwable {
            return dispatch(invocation);
        }

        public Object captureExpectationTo(ExpectationCapture capture) {
            return imposteriser.imposterise(
                new ObjectMethodExpectationBouncer(new InvocationToExpectationTranslator(capture, defaultAction)),
                mockedType);
        }
    }
}
TOP

Related Classes of org.jmock.Mockery$MockObject

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.