|
PluginDevelopment
How to create your own plugins
Creating your own plugins for Mycila Testing Framework is very easy. As an example is better than many words, let's start by a tutorial on how to write a JMock Plugin. The Plugin APITestPluginPlugins must implement TestPlugin, but we hardly advise you extend DefaultTestPlugin to avoid APi breaks and to avoid implementing all methods unecessary for you. Your plugin will be called at each step of a test flow:
In these plugin methods, you have access to the current Execution context and the current Test context. The test context represents the test class and the Execution context represents the method under test / execution. The table below summary the contexts you can obtain for each method. Note that you can retrieve the Test context from the Execution context by doing execution.context()
ContextThe Execution and sub class TestExecution context represents the method in test. When you develop a plugin that handles beforeTest and afterTest events, the TestExecution context enables you to set wheter the test method should be skipped, and enables you to get the exception thrown by the test method if any. With those 2 properties, you are able to change the behavior of the test framework and let some test pass even if they thrown an exception. A good example of this is the AnnotationPlugin which provides @Skip and @ExpectException annotations Introspector and FiltersThe TestContext class gives you access to an Introspector on the test instance, from where you can select fields and methods through filters and compose them. In example, in Guice plugin, to select all methods providing Guice Modules and annotated by @ModuleProvider, we can call: import static com.mycila.testing.core.introspect.Filters.*; List<Method> methods = ctx.introspector().selectMethods(excludeOverridenMethods(and(methodsReturning(Module.class), methodsAnnotatedBy(ModuleProvider.class)))) The Filters class already provides common filters. The Introspector is in a separate package and can be used on any class instance, and you can also define your own filters. In exemple, here we want to select from myObject all methods annotated by @ConfigureMycilaPlugins which have only one parameter, a PluginManager instance. Introspector introspector = new Introspector(myObject);
List<Method> methods = introspector.selectMethods(excludeOverridenMethods(and(methodsAnnotatedBy(ConfigureMycilaPlugins.class), new Filter<Method>() {
@Override
protected boolean accept(Method method) {
final Class<?>[] types = method.getParameterTypes();
return types.length == 1 && types[0].equals(PluginManager.class);
}
})));Configure PluginManager / Test your pluginYou can use @MycilaPlugins and @ConfigureMycilaPlugins to test your plugin, as described in the User API wiki page. Basically, @MycilaPlugins enables you to control which plugin descriptor to use (to isolate your plugin when testing) and this annotation enables you to configure per-test the PluginManager. If you want to define your own plugin at runtime like this example, you'll have to annotate your configuration method with @ConfigureMycilaPlugins. ExampleAn example of implementation would be: public final class MyPlugin extends DefaultTestPlugin{
@Override
public List<String> getBefore() {
return null;
}
@Override
public List<String> getAfter() {
return null;
}
@Override
public void prepareTestInstance(Context context) {
}
}We don't care about receiving beforeTest, afterTest and afterClass events. getBefore and getAfter comes from Mycila Plugin Framework. If provided, they help defined the order of execution of the plugins. In example, suppose we have a Spring plugin that checks for beans to include in the Application Context. If we execute the JMock Plugin before the Spring plugin, we will be able to create the Spring Application Context base on created mocks. The TestContext enables the plugin to access the test instance to enhance, and also sharing data with other plugins through context attributes. A plugin can also access the plugin manager and resolver, which provides the plugin execution flow. To have more information about the plugin infrastructure, please see Mycila Plugin Framework documentation. First, What do we want for our plugin ?
We would like to be able to write tests using JMock like this: public final class MyTest extends MycilaTestNGTest {
@MockContext
Mockery mockery;
@Mock
Service service;
@Test
public void test_go() {
mockery.checking(new Expectations() {{
// some expectations
}});
// execute test
mockery.assertIsSatisfied();
}
}
Let's code our plugin !First, we need to have two annotations: @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface Mock {
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface MockContext {
}Then, we prepare our plugin class: public final class MyPlugin extends DefaultTestPlugin {
@Override
public List<String> getAfter() {
return Arrays.asList("guice1", "spring");
}
@Override
public void prepareTestInstance(Context context) {
//TODO: implement
}
}The implementation is quite simple:
Field[] mocks = context.getTest().getFieldsAnnotatedWith(Mock.class);
Mockery mockery = new Mockery();
for (Field field : context.introspector().selectFields(and(fieldsAccepting(Mockery.class), fieldsAnnotatedBy(MockContext.class)))) {
context.introspector().set(field, mockery);
}
for (Field field : mocks) {
context.introspector().set(field, mockery.mock(field.getType(), field.getDeclaringClass().getName() + "." + field.getName()));
}Package and distributeNow that we have a working and testable plugin, it would be nice if it could be loaded automatically if our plugin jar is in the classpath. To detect plugins, Mycila Plugin Framework reads all property files in the classpath named: META-INF/mycila/testing/plugins.properties So we just have to include a plugins.properties files in our jar, with the following content: jmock=MyPlugin Et voila ! We've made with 3 classes (two annotation and a plugin class) and a property file, a JMock plugin for Mycila Testing, that can be automatically loaded and executed to enhance your tests. To go furtherWe highly suggest you have a look of existing plugins implementation in the source code repository. There are plenty of plugins existing and you will probably find what you need on these examples. | |||||||||||