My Photo

August 2007

Sun Mon Tue Wed Thu Fri Sat
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  

« April 2005 | Main | September 2005 »

May 28, 2005

Virtual Mocking... with jMock

Testing is one of the areas for applying aspects that I have been excited about for the longest time. On some of the first consulting projects where I used AOP, I used a very simple form of "virtual mock objects," e.g.,

public class TestAccount {
...

    pointcut inFailureTest() : cflow(execution(* TestAccount.test*Failure());

    before() : call(* Statement+.*(..) throws SQLException) && inFailureTest() {
        throw new SQLException("account access failed");
    }
}

This was the same kind of testing that Nicholas Lesiecki wrote about in "Test flexibly with AspectJ and mock objects." Subsequently, when I was working on the aTrack project, I created a small framework to simplify this kind of testing for virtual mock objects. First I and subsequently Nick also presented about this approach and framework several times (e.g., see the slides).

Around the same time I created the virtual mocking framework in the aTrack library, Chad Woolley created his own virtual mocking framework, and Monk and Hall wrote about a simple framework. The biggest difference between the aTrack approach and the other two was that aTrack used inter-type declarations to allow defining mock behavior on the actual collaborating objects (and monitoring behavior of tested objects), e.g., Customer.setThrowable(new RuntimeException()). This also meant that when using it, tools like today's AJDT can statically determine a fairly limited scope for virtual mocking (at least unless you use it to replace many classes in your system), whereas the other approaches tended to advise every join point in the system with a runtime check. In future, I expect commercial AOP will be better able to statically determine where pointcuts can match.

Part of this story, though, is the parallel development of mock objects as a technology. Mocks are still fairly new, and most of the people who use them practice agile methodologies. Whenever I presented "Testing With Virtual Mock Objects," I found that people who actual had tried to use mocks and those who used TDD were excited. Many others were excited by ideas that were really based on the idea of mocks.

One important development in mock objects has been the jMock project. It uses dynamic proxies and features a concise and readable syntax for writing mock tests.

As I've thought about the existing implementations of virtual mocking I realized that they all suffered from trying to reinvent a good, clean API for mocks. And with jMock being a big improvement on past APIs, it would be a lot better to extend jMock to support virtual mocks...

My thinking is that there are really five interesting types of mocking:

  1. direct collaborators with public interfaces (classic mocks)
  2. direct collaborators with non-OO interfaces (singletons, static methods, private methods, etc.)
  3. indirect collaborators (unit tests of closely coupled classes and integration tests)
  4. aspects
  5. monitoring the object under test

I think the right unit of abstraction for a virtual mock is ... the pointcut and not a type. (Aside: this is related to my contention that it is better to define and configure loggers by pointcut rather than type). With this in mind, I wrote some proof of concept code that successfully extended jMock with the concept of a virtual mock in two different ways. I've been calling this ajMock...

  • Dynamic
    class TestInitialization extends VirtualMockObjectTestCase {
    ...
        public void testInitialization() {
            VirtualMock mockStart = virtualDynamicMock("call(* startUp()) && target("+ApplicationLifecycleAware.class.getName()+")");
            mockStart.expects(once()).will(doProceed());
            ...

    Static
    class TestInitialization extends VirtualMockObjectTestCase {
    ...
        public void testInitialization() {
            VirtualMock mockStart = virtualMock(StartInitialization.aspectOf());
            mockStart.expects(once()).will(doProceed());
            ...
        }

      private static aspect StartInitialization extends VirtualMockAspect {
            public pointcut mockPoint() : call(* startUp()) && target(ApplicationLifecycleAware);
    ...

It was relatively straightforward to integrate these extensions into jMock (albeit incompletely and as more of a test than a complete framework). One really nice thing is that you can use the normal jMock APIs and should be able to mix and match proxy and even CGLIB proxy mocks with virtual mocks. There are a few areas where jMock assumes that mocks have proxies, and where virtual mocks can do differently.

I've been using this code to test my performance monitoring code and was pleased that it has helped me (mostly with integration tests). I started off with the dynamic approach, extending the new AspectJ 5 reflection library. However, I found that tools issues made this unpalatable. The small issue was the annoyance of runtime checking of pointcuts and having no tools information about them. I adopted the idiom of using Type.class.getName() when writing the mock strings to get compile-time checking rather than using runtime checks to help a little with that. The big issue that led me to change to the static approach was the impact on tools support everywhere else. The dynamic version had to advise almost every join point in the system with dynamic tests for whether to mock it. This resulted in a painful experience when in stepping a debugger, it added too much clutter to gutter tips, cross-reference views, etc. for advice (since everything was advised), and even made stack traces harder to read. The static version seems like a pretty reasonable compromise.

I think dynamic virtual mocks are a great candidate for load-time weaving: you could apply them to a limited scope for a given test case and not impact tools support when editing or debugging outside the scope. The part I haven't yet designed is how to effectively integrate load-time weaving into Eclipse's jUnit launcher.

These type of virtual mocks can be a great complement to, e.g., jMock.

  • It makes it a snap to mock things that are traditionally hard to mock (static, final, or private methods). (case 2 above: non-OO interfaces)
  • It also makes it a snap to track behavior of objects under test, without in any way mocking them. (case 5 above)
  • Pointcuts are great for expressing systematic mock behavior where jMock method patterns names aren't robust enough (I see their matchers as just a baby step towards the power of pointcuts). E.g., what if I want to specify a null return value for any method returning an Object or subclass, and false for any returning a boolean? What if I want to throw SQLExceptions from any database call? (adding to the value that jMock provides in case 1)
  • Pointcuts also make it really natural to integration test an isolated subsystem. Often you want to plug in dummy behavior that gets invoked a few calls down. With ajMock, you can specify this easily. In integration tests, it is sometimes a big win to use an object's own behavior as a starting point, and just to change it a bit to make your test case work (rather than dummying out all the interactions).
  • I think this approach can be extended to achieve many of the goals of, e.g., aUnit, but in a way that lets you integrate with other mocks. I think you'd need a different kind of virtual mock that let you generate (stub) pointcuts, rather than just matching them in the normal flow. However, the jMock-style API seems to be a useful one to allow testing pointcuts meaningfully, through representative examples. This is a rich area for further exploration. (case 4 above)

I'd be interested in thoughts from others about this? For jMock users, where would you see this as a useful complement. Obviously I need to flesh out the design and explain more of it. I also think there's a lot of room to extend the simple virtual mocks with common idioms (e.g., plugging in a regular jMock of a class that is invoked indirectly, mocking an assembly of objects).

May 05, 2005

Load Time Weaving with AspectJ 5

I've been working with the new AspectJ 5 Load Time Weaving implementation that Alex and Jonas have created, and am really excited at the opportunity this technology brings. I have tried it with the Sun Java 1.5 VM and the JRockIt 1.5 VM too (*).  I'm working on creating application monitoring tools, so it's really natural to weave into the whole VM that a container is running in. Using LTW, we are already able to track the operations of any deployed application inside Tomcat 5.5.x, which is very cool!

I have used build-time weaving with third party libraries in past and found it very painful because you needed to either find any libraries that these libraries used (e.g., to weave into Spring.jar you need all the various O/R tools, JMX, and a bevy of obscure cache management libraries). With load-time weaving, you only weave the classes that are loaded, so you only need the libraries that are referenced by these. That's a big improvement.

LTW is still "pre-alpha", so naturally I faced a few gotchas (I was working off a branch in CVS and they just merged it into head in the last couple of days). Thanks to Alex for helping me with these and to Andy Clement for bearing with my questions about mixing 1.5.0 M2 output with the fairly old branch code!

One of the things I'd like to change is the need to specify all the aspects that are woven in an aop.xml file. Alex and I traded thoughts on this and my idea of an aspectpath like mechanism on bugzilla (where else)? I would really like to have some way to generate the list of aspects, rather than having to maintain it by hand, hopefully stored separately (in something like META-INF/aspects.mf).

I also ran into an interesting problem when I was weaving into a MySQL JDBC driver's debug version:

error at com\mysql\jdbc\NonRegisteringDriver.java::0 class 'com.mysql.jdbc.NonRegisteringDriver' is already woven and has not been built with -Xreweavable

That's right. This error means that the code was already woven with AspectJ! The MYSQL debug JDBC driver includes a tracing aspect: it's great to see this kind of increased use & popularity of AspectJ. For the curious, you can see the source code for this aspect in the source distribution of the Mysql Connector/J. In this case, the normal, non-debug, library doesn't use AspectJ so it's not a major problem.  There is already consensus that reweavable should be the default mode in future. I even question why there's an option to not allow reweaving. (**)

Now the next challenges I see on this front are:

  • Shipping 1.5.0
  • Optimizing the weaving performance, especially for simple cases. I'd really like to see AspectJ's load-time weaving fairly close in performance to hand-written bytecode manipulation. Not necessarily the same, but at least close enough.
  • Support for older VMs (which I know Alex and Jonas are working on)

Ron

(*) I found JRockIt to be about twice as fast, but to not be quite as good at debugging my aspects inside Eclipse. Apparently BEA has a patch for Eclipse debugging but is only providing it to paying customers...

(**) Amusingly, I only discovered this because I had an error in the first zip distribution I downloaded so I could only extract the debug jar file.

May 04, 2005

AOP and the Analysts

Things have been going very well for Aspect-Oriented Programming in the last year. There has been a continuing increase in interest and effective use across the board. With the recent AspectJ5 announcement, both IBM and BEA are sponsoring the same, leading open source implementation. And both Spring and JBoss continue to deliver valuable frameworks using AOP.

As AOP has gained use, the industry analysts have started noticing. The Gartner Group has tracked Aspect-Oriented Software Development as an emerging technology for the past two years, with positive assessments as they project it moving towards mainstream adoption.

Last month, Richard Monson-Haefal, a senior analyst at the Burton Group wrote a carefully researched in-depth analysis "Ghosts in the Machine: Aspect-Oriented Programming" that concluded

Aspect-oriented programming (AOP) provides significant advantages in
terms of modularization. However, AOP has not reached mainstream
adoption because it is difficult to learn, and historically, it lacked
decent tool support. Most damaging was its lack of visibility: AOP
code changed the behavior of an application, but its functionality
wasn't seen.  It was as if there were ghosts in the machine. Although
AOP is still difficult to learn, tool support and visibility are no
longer a problem. Integrated development environments (IDEs) have
advanced to the point where an organization can now safely use, and
benefit from, the powerful modularization capabilities offered by AOP.

Shortly thereafter, Carl Zetie of Forrester wrote a 5 page report "AOP Considered Harmful" that is quite negative. I have heard that this cited only a single workshop paper from graduate students. If this is true, it is surprising to me that an analyst would write an extremely negative assessment of something without seeking input from the leading proponents (or opponents!), and understanding the intended uses. It is interesting to speculate if there are other motives involved, e.g., it appears that the anti-AOP camp inside Sun are in control and, of course, being contrarian can be useful as an analyst.

Regardless, this is just the most visible example of that the AOP backlash has arrived. At this stage, I find most of the opponents of AOP are those who dislike it on first encounter and don't seek to understand it well, not those who tried and found it lacking. In a way, that's the best news possible. A backlash is inevitable. But negative reactions primarly from those who aren't experienced is far better. It suggests to me that the message of go carefully and adopt incrementally has worked. In addition, AOP advocates tend to be realistic in our assessment, rather than offering a silver bullet. This has resulted in far less disillusionment from real users. I'd suggest that this is one of the big benefits of open source rather than commerically-led technologies. It has also given AOP more time to mature before the backlash began.