|
From: <Vin...@ge...> - 2003-05-07 15:06:20
|
I am getting a clearer picture of what we need to do.
Where to go
-----------
We can achieve our goals with a combination of the following:
1. We build our framework around the dynamic mock framework (in
mockobjects-java cvs under src/core/com/mockobjects/dynamic and
src/core/com/mockobjects/constraints). It already implements all the
concepts of mocks we need. The implementation we want to change is the Mock
class itself, which relies on dynamic proxies to do the interception.
Here's an example of usage taken from the mockobjects mailing list:
public void testDoGetOldStyle() throws ServletException, IOException {
Mock mockTimer = new Mock(Timer.class);
Mock mockHttpServletResponse = new OrderedMock(HttpServletResponse.class,
"response");
Mock mockHttpServletRequest = new Mock(HttpServletRequest.class);
mockHttpServletRequest.matchAndReturn( "getParameter",
C.args(C.eq("browser-identifier")), "MSIE-5.0" );
mockHttpServletRequest.expectAndReturn( "getParameter", "subject", "Mail
Subject" );
mockHttpServletRequest.expectAndReturn( "getParameter",
C.args(C.eq("body")), "Mail Body" );
mockTimer.expectAndReturn("getTime", 10000);
mockTimer.expectAndReturn("getTime", 20000);
final PrintWriter contentWriter = new PrintWriter(new StringWriter());
mockHttpServletResponse.expect( "setContentType", "text/html");
mockHttpServletResponse.expectAndReturn( "getWriter", C.args(),
contentWriter );
// proposed arbitrary odering implementation
// CallMatch m1 = mockHttpServletResponse.expect( "setContentType",
"text/html");
// CallMatch m2 = mockHttpServletResponse.expectAndReturn( "getWriter",
contentWriter );
// m1.expectBefore(m2);
SimpleServlet aServlet = new SimpleServlet((Timer)mockTimer.proxy());
aServlet.doGet((HttpServletRequest) mockHttpServletRequest.proxy(),
(HttpServletResponse) mockHttpServletResponse.proxy());
mockHttpServletRequest.verify();
mockHttpServletResponse.verify();
mockTimer.verify();
}
The nice thing about it is that we never have to actually write any mock
class anymore. They are all dynamically generated.
2. We use aspects to intercept calls in the Mock class instead of dynamic
proxies. In pseudo code, would look like:
// We intercept call for setContentType on HttpServletResponse
public class ExampleAdvice extends MethodAdvice {
private final ExpectationValue m_value = ...
public ExampleAdvice () {
super();
}
public void setExpectedValue(String value) {
....
}
public Object execute(final JoinPoint joinPoint) throws Throwable {
MethodJoinPoint jp = (MethodJoinPoint)joinPoint;
// Intercept call to setContentType
String actual = jp.getParameters[0];
m_value.setActual( actual );
// forget about original invocation
}
}
Then we add the method advice to the method setContentType of
HttpServletResponse.
Jonas just mailed me that he has already implemented some dynamic aspects
configuration. The way it worked before was through configuration files.
This implies you have to know in advance, what aspects you want to add,
which usually is just fine. However, in our case, it depends on what the
test decides. So we need an API to add aspects to classes.
3. AspectWerkz relies on JMangler, so it can't modify classes from rt.jar.
We discussed that before, and I believe there is no easy way to modify
arbitrary classes from that jar, since if a class has already been loaded,
we're doomed. I don't want to modify the VM and be VM dependent (sick!). I
guess we can get around this by modifying the class under test using BCEL or
maybe serp.
Suppose we have a class that reads a file and parses it to generate an
Object. It performs manipulation of file content and if the file contains
error, it should generate exceptions. It's legacy code and we can't (or
don't want to) change it.
public class MyClass {
private final File m_file;
public MyClass(String path) {
m_file = new File(path);
}
public void Object parse() {
...
}
}
One way to test it would be to replace the reference to File with a
reference to MockFile. MockFile is a copy of File we generate dynamically.
It must be binary compatible with File. We then add aspects to MockFile as
described in 2. When we use MyClass in the test, it will use our mock class
instead of File.
How to get there
----------------
I can think of the following things to do:
1. Evolve AspectWerkz to be 100% runtime configurable without config files.
2. Play with BCEL/serp to implement 3.
Then we wrap up everything and implement Mock class from dynamic mocks using
1+2.
I will start looking into 1. Who volunteers for 2?
-- Vincent
|