getting started with MockCentral
The fundamental component of the MockCentral architecture is the
MockCentralServer application, which is packaged as a jar
file and should be placed on the classpath of the test case classes
needing access to the mock objects defined in the configuration
files. The server is initialized using an InputStream,
a File or path string that is used to load and process
the xml data. Once initialized, it can be called to obtain the mock
objects (and the generated proxies) for method invocations,
creating new objects and for verifications. It can also be used to
automatically bind proxies in the NamingContext under
JNDI names.
MockCentralServer Fixture instances are used to
group collections of mock objects together. Fixtures are a useful
organizational tool and also allow an entire group of proxies to be
bound at the same time by calling the server's
bindFixtureProxies(String) method. Fixtures can also
import other fixtures, inheriting their mock object
definitions.
The configuration files can be written manually, but testing using MockCentral really comes into its own when the files are created using a MockCentral Editor application, which allows for extremely rapid "point and click" mock object editing. With a MockCentral editor, developers don't have to write any mock object setup code at all and defining complex method expectation behaviour and relationships between mock objects is easy. MockCentral editors are built on top of the mockcentral-editor-core library, which defines interfaces and supplies logic and utility classes for concrete editors. Currently, org.mockcentral offers an Eclipse plug-in version which is ready to go when simply copied into your installation's "plugins" directory. Some of the features of the editor are:
- default implementations of any or all mock object interface or java.lang.Object methods can be added to mock objects with a button-click.
- JNDI bindings can be added to proxies by simply entering a JNDI name in the appropriate field editor for a mock object definition.
- Using editor controls, fixtures, mock object definitions and individual method expectation definitions can be copied and pasted throughout different parts of the configuration file or between files, making it easy to quickly build mock object libraries.
- any defined mock objects can be assigned as parameter or return values for any others within the enclosing fixture (circularity is tested for).
what does the MockCentralServer do?
The MockCentralServer builds mock objects from data stored in mockcentral-config.xml files, which can be of any name but must conform to the mockcentral-config DTD. The server uses the apache.commons Digester to parse the file or stream supplied to it during initialization and generates instances of various classes. Unless you are writing the configuration files by hand, you don't need to know the full details about this process, but it is described here for those interested in how the server builds mock objects and how the program works generally. The above-mentioned classes (in order of increasingly finer granularity) are:
- Fixture: a class that stores a group of mock objects and their proxies, is identified by a unique name, optionally has a description and import declarations as well as a list of mock object definitions. Fixtures may also define simple (non-Mock) objects which can be bound into the JNDI naming context to facilitate your various tests.
- MockObjectSetup: a class that models a single mock object, is identified by a name unique within the enclosing Fixture, declares a mocked interface class and optionally a JNDI name as well as a list of method expectation definitions.
- MethodExpectationBean: a class which models a mock object method expectation, specifying the method name, the type of mock object method -- expectAndReturn, matchAndThrow, etc --, an optional list of parameter definitions or an "anyParams" declaration, and an optional expected result value.
- SignaturePartBean: a class supporting a
MethodExpectationBean that models a parameter or expected result
for a mock object method expectation, defining a "valueType" as
well as "valueClassName" and "value" definitions as appropriate
(more on this below). If the value type is "collection" or "array"
the model can contain a collection of other SignaturePartBean
instances created from any nested
<collectionEntry>xml Elements, which will be used to build the collection or array contents. If the value type is "instance" or "throwable" the model can contain a collection of other SignaturePartBean instances created from any nested <constuctorArg> xml Elements, which will be used to build the instance or throwable constructor arguments.
Once all the data is loaded, validation takes place and the mocks objects are built, proxies are created and the server is ready to do its work. You can now fetch, bind and verify the mock objects with simple calls to the MockCentralServer. For example:
// assign mock dialog factory
MyApplication.setDialogFactory (IDialogFactory) server.getProxy("dialogFactory");
// invoke method to open a new IFooDialog
currentObject.showFooDialog(args);
// assert "open" method was called on the mock IFooDialog referenced in the mock factory
server.verifyMock("fooDialog");
method expection value types
When defining mock object method expectations, you can specify method parameters and expected results of 11 different "value type" categories. Depending on the category, you can further assign additional data used to build the signature part, specifying a class name field and/or value field to build the object you need. Again, detailed knowledge of how the server processes the various value types is not required to use MockCentral, but a brief discussion here can help you get started using the application quickly. The various types you can select for method expectation parameters and return types (as well as simple, non-Mock JNDI-bound objects for fixtures), are listed below. Note that the editor application provides easy-to-use editing controls and programmable defaults for all of these types.
- primitive: a boolean or int value. The method signature
element is constructed from the corresponding
<valueClassName>and<value>xml Elements (note that with the auto-unboxing available since java version 1.5 this value type is no longer necessary to use, and any numeric type or boolean can be specified using the "object" type described below). - object: a wrapped primitive (Boolean, Byte, Char, Short,
Integer, Long, Float or Double) or a String. The method signature
part is constructed from the corresponding
<valueClassName>and<value>xml Elements. - ref: a reference to another mock object defined within
the fixture. The
<value>xml Element is used to look up the proxy to use as the method signature part. - instance: a new instance of any Class on the current
classpath. The
<valueClassName>xml Element is used to invokeClass.forName().newInstance()to create the method signature part, using any supplied nested<constructorArg>xml elements to build parameters for the method invocation. - throwable: a Throwable result for the method - when the
mock object method type is "matchAndThrow" or "expectAndThrow".
This value type is applicable only to expected results. The
<valueClassName>xml Element is used to invokeClass.forName().newInstance()to create the method result, using any supplied nested<constructorArg>xml elements to build parameters for the method invocation. - collection: a List or Set containing a number of other
objects, refs, etc. All additional value definitions defined in
nested
<collectionEntry>elements are processed and added to the Collection to be used as the method signature part. - array: an Array containing a number of other objects,
refs, etc. All additional value definitions defined in nested
<collectionEntry>elements are processed and added to the Array to be used as the method signature part. - enum: an enum value. The
<valueClassName>and<value>xml Elements are used to obtain the specified object. - constant: a constant value declared in a class or
interface. The
<valueClassName>and<value>xml Elements are used to obtain the specified object. - variable: a key used to look up a user-defined object
from a
HashMapoptionally supplied the server'sinitmethod: the object returned from the map is used for the method signature part. This can be useful if your code under test accesses runtime data such as the current date or an object obtained from an external service. Of course, you can also use this "value type" to assign any Object you've created to the method signature part: if it can be stored in the Map, the server can use it to build your mock objects. - null: a null value.
-
method types and verifications
The
<mockMethod>xml element in a method expectation definition indicates which type of mock object method should be assigned, i.e.,matchAndReturn,expectAndThrow, etc. There are four valid values for this element: "match-return", "match-throw", "expect-return" and "expect-throw". If one of the "throw" values is used, the expected result must be defined as a "throwable" value type and specify aThrowableclass. If one of the "expect" values is used, the mock object can be verfied for method invocations within your test code by calling the server'sverifyMockmethod. Again, if you are building your configuration files using the MockCentralEditor you can use point and click actions to set the method type and need not worry about the xml data.a very basic example
A typical strategy for using MockCentral in test cases would be:
1) get the Singleton instance and call the
MockCentralServer.initmethod in aTestCaseConstructor,suitemethod,setUpmethod or individual test method as follows:MockCentralServer server = MockCentralServer.getInstance(); public void setUp() throws Exception { InputStream is = getClass().getResourceAsStream("my_config_file.xml"); server.init(is); //additional setup code below... }2) write your test method; binding proxies, calling code that will make use of the mock objects you've defined, making assertions and verifying method calls:
public void testFoo() { server.bindFixtureProxies("fooFixture"); InitialContext context = new InitialContext(); FooHome fooHome = (FooHome) context.lookup("java:comp/env/ejb/Foo"); assertEquals(fooHome.getFooTotal(), 10); server.verifyMock("fooFixture", "mockFooHome"); }the xml code within the configuration file would look something like this:
<?xml version="1.0" encoding="UTF-8"?> <mockcentral-config> <fixture> <fixtureName>fooFixture</fixtureName> <fixtureDescription>fixture for Foo test cases</fixtureDescription> <mockObjectSetups> <mockObjectSetup> <mockObjectName>mockFooHome</mockObjectName> <className>com.xyz.FooHome</className> <jndiName>java:comp/env/ejb/Foo</jndiName> <expectations> <expectation> <methodName>getFooTotal</methodName> <mockMethod>expect-return</mockMethod> <expectedResult> <valueType>primitive</valueType><valueClassName>int</valueClassName><value>10</value> </expectedResult> </expectation> </expectations> </mockObjectSetup> </mockObjectSetups> </fixture> </mockcentral-config>more examples
click here to see additional, more detailed (and slightly more realistic) examples. Naturally, MockCentral is also used extensively to test itself, and two very good places to look for examples are the src/test directories of the MockCentralEditor-Core and MockCentralEditor Eclipse plug-in distributions.
