Let's see if I understand your problem correctly. You would like to define a test for M1 (testM1), a test for M2 (testM2) a test for M3 (testM3) etc. You would then like to test the combination of M1->M2->M3 by calling all of the test methods testM1, testM2, testM3 in order, but passing some arbitrary data between those methods. So testM1 gets called, and it produces some data which gets passed to testM2, which produces data which gets passed to testM3. The solution below is designed for this, but can easily be extended to include classes.
There are two ways of doing this. The easiest is to define a @Rule in your classes which gets the context stored in a static field in the Suite class and updates it. If you extend ExternalResource, you can do something like:
private Context context;
@Rule
public ExternalResource resource= new ExternalResource() {
@Override
protected void before() throws Throwable {
// get context from Suite class
context = MySuite.getContext();
};
@Override
protected void after() {
// put context into Suite class
MySuite.setContext(context);
};
};
Your methods would then use that context as normal. This would work in most cases, but creates a dependance from the test to a particular MySuite class.
The more complex option
You can do this using a custom @Suite
class with a customer Runner. My answer to How to define JUnit method rule in a suite? shows how to execute custom code before and after a particular method (in that case one with a @depcrecated annotation).
You would need to do something similar. So your Suite would create the context for the test, and before executing a particular test method, the runner retrieves the context from the suite, calls a setter in the test class. The test method uses that context, returns and the runner retrieves the context from the test class and updates the suite class. The obvious place to store the context would be the suite, since the test classes themselves are created each time. I think it's too much work to try and pass the context in as a parameter, you'd have to change a lot more code.
So in your Runner, you would have something like
public class MyRunner extends BlockJUnit4ClassRunner {
private MySuite suite;
public MyRunner(Class<?> klass, MySuite suite) throws InitializationError {
super(klass);
this.suite = suite;
}
@Override
protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
Description description= describeChild(method);
if (method.getAnnotation(Ignore.class) != null) {
notifier.fireTestIgnored(description);
} else {
// call suite.getContext()
// call setContext() in class
runLeaf(methodBlock(method), description, notifier);
// call getContext() in class
// call suite.setContext()
}
}
}
It is possible to override the beforeclass behaviour as well.