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 |