From: Thomas C. <ald...@ne...> - 2001-08-15 19:18:59
|
Vincent "I like the dynamic proxy because it enables to skip the static mock implementation generation step. Even if you don't have an interface you can still extend a class." The implementation of java.lang.reflect.Proxy requires an interface - you cannot extend an implementation class with Proxy. However, you can override methods of an implementation class (in any JDK, AFAIK), with the anonymous inner class testing pattern, but you cannot add callable methods to the class' interface with an *anonymous* inner class. You can also use this latter pattern to extend and test interfaces prior to JDK 1.3 *if and only if* a compilable mock implementation exists, though this mock implementation can simply return null or do nothing for all methods as return values require (thus relatively easy to generate). "public void testSomething() { MockYYY mock = new MockYYY(); mock.setActualValueXXX("text/html"); mock.setActualValueZZZ(true); ... // set the expectations on the mock object (if need be) mock.setExpectedValueAAA("something"); ... // the method to unit test that calls XXX // and ZZZ as part of it's logic String result = myObject.someMethod(); ... // then the asserts, which can be a mix of junit asserts and // verifying against the expectations set on the mock object assertEquals("...", result); mock.verify(); }" If I understand this correctly, this is a good spot to do a side-by-side comparison of pieces of the test above with the patterns. One way, closest to that in the last version of the article draft I posted here: public void testSomething() { MockYYY mock = new MockYYY() { // Implements a verifiable interface private boolean called; String XXX() { return "text/html"); } boolean ZZZ() { return true; } void AAA(String someString) { assertEquals("something", someString); called = true; } boolean verify() { return called; } }; String result = myObject.someMethod(); assertEquals("...", result); assert(mock.verify()); } However, this is more efficient and renders the Verifiable interface unnecessary: public void testSomething() { MockYYY mock = new MockYYY() { String XXX() { return "text/html"); } boolean ZZZ() { return true; } void AAA(String someString) { assertEquals("something", something); } }; String result = myObject.someMethod(); assertEquals("...", result); } Unfortunately, this does not answer the question of, "What happens if method AAA is never called, but the call affects the state of object yyy and is required by contract?" If essential, we can use a small helper class to count calls: public class CallCount() { int required; public CallCount(int required) { this.required = required; } public void add() { required--; } public boolean check() { return required == 0; } } And use it thus: public void testSomething() { final CallCount calls = new CallCount(1); MockYYY mock = new MockYYY() { String XXX() { return "text/html"); } boolean ZZZ() { return true; } void AAA(String someString) { assertEquals("something", something); calls.add(); } }; String result = myObject.someMethod(); assertEquals("...", result); assert(calls.check()); } There still remains a slight chance, where there are two or more methods to call in the mock object and at least one of those methods is supposed to get called more than once, that the test might not catch the *incorrect* method call frequency. In my experience, this is fairly rare except for writing to output streams where a count of the number of lines or number of times printed is not generally useful. Moreover, calling the same method of another object, aside from some basic system classes, more than once in the same method is a symptom of poor method design. However, if we really must be sure, a slightly more advanced call counter could suffice. To prepare: CallCountMap calls = new CallCountMap(); calls.set("AAA", 6); calls.set("BBB", 4); To record in an anonymous class method: calls.add("AAA"); To check: assert(calls.check()); Of, if call the check method is implemented to cause an assertion failure, the additional assert is not necessary: calls.check(); Thus, without the call counters, you can make use of a Verifiable-like interface. With the call counters, you can put aside Verifiable completely while making most of your assertions directly in methods of the anonymous class. Now, there are some subtle points regarding "expectation folding" that I am waiting to hear back from Tim and Steve on to see if they have seen situations where this breaks down, but it is working for me thus far and causing some refactoring of production classes that proves useful later on. Also, if there *are* complex expectations, you can always replace the CallCountMap in the above with an expectation monitor class perhaps build on some of the expectation classes currently available in the mock objects project. "We probably need to work on a real life example" Indeed. I shall stay tuned. "The idea is simply that with the above described generic mechanism there is no need to inner classes at all." Agreed in theory with the generic mechanism you described, but it seems to me that the generic mechanism is not trivial, requiring a lot of code and a relatively large library to pull off as well as the prospect of complicated code generation and compilation. Tom |
From: Steve F. <st...@m3...> - 2001-08-15 22:51:15
|
RnJvbTogIlRob21hcyBDYWxpdmVyYSIgPGFsZGhlb3J0ZUBuZXR6ZXJvLmNvbT4NCj4gICAgIFN0 cmluZyByZXN1bHQgPSBteU9iamVjdC5zb21lTWV0aG9kKCk7DQo+ICAgICBhc3NlcnRFcXVhbHMo Ii4uLiIsIHJlc3VsdCk7DQo+ICAgICBhc3NlcnQobW9jay52ZXJpZnkoKSk7DQoNCndoeSB3b3Vs ZCB5b3UgYXNzZXJ0IHRoZSB2ZXJpZnk/IFRoZSB2ZXJpZnkgbWV0aG9kIHNob3VsZCB0aHJvdyBh biBBc3NlcnRpb25GYWlsZWRFeGNlcHRpb24gaWYgaXQgZmFpbHMuDQoNCj4gVW5mb3J0dW5hdGVs eSwgdGhpcyBkb2VzIG5vdCBhbnN3ZXIgdGhlIHF1ZXN0aW9uIG9mLCAiV2hhdCBoYXBwZW5zIGlm DQo+IG1ldGhvZCBBQUEgaXMgbmV2ZXIgY2FsbGVkLCBidXQgdGhlIGNhbGwgYWZmZWN0cyB0aGUg c3RhdGUgb2Ygb2JqZWN0IHl5eQ0KPiBhbmQgaXMgcmVxdWlyZWQgYnkgY29udHJhY3Q/IiBJZiBl c3NlbnRpYWwsIHdlIGNhbiB1c2UgYSBzbWFsbCBoZWxwZXINCj4gY2xhc3MgdG8gY291bnQgY2Fs bHM6DQo+IA0KPiBwdWJsaWMgY2xhc3MgQ2FsbENvdW50KCkgew0KPiANCj4gICAgIGludCByZXF1 aXJlZDsNCj4gICAgIA0KPiAgICAgcHVibGljIENhbGxDb3VudChpbnQgcmVxdWlyZWQpIHsNCj4g ICAgICAgICB0aGlzLnJlcXVpcmVkID0gcmVxdWlyZWQ7DQo+ICAgICB9DQo+ICAgICANCj4gICAg IHB1YmxpYyB2b2lkIGFkZCgpIHsNCj4gICAgICAgICByZXF1aXJlZC0tOw0KPiAgICAgfQ0KPiAg ICAgDQo+ICAgICBwdWJsaWMgYm9vbGVhbiBjaGVjaygpIHsNCj4gICAgICAgICByZXR1cm4gcmVx dWlyZWQgPT0gMDsNCj4gICAgIH0NCj4gDQo+IH0NCg0Kd2h5IGRvbid0IHlvdSB1c2UgYW4gRXhw ZWN0YXRpb25Db3VudGVyPyBBbHNvLCBob3cgZG9lcyB0aGUgY2hlY2sgbWV0aG9kIGRpZmZlciBm cm9tIHVzaW5nIFZlcmlmaWFibGUsIHdoaWNoIGlzIGEgY29uc2lzdGVudCBpbnRlcmZhY2Ugd2Ug dXNlIGFjcm9zcyB0aGUgbGlicmFyeSAoYW5kIGlzIHNlYXJjaGVhYmxlIGJ5IHJlZmxlY3Rpb24p Lg0KDQo+IFRodXMsIHdpdGhvdXQgdGhlIGNhbGwgY291bnRlcnMsIHlvdSBjYW4gbWFrZSB1c2Ug b2YgYQ0KPiBWZXJpZmlhYmxlLWxpa2UgaW50ZXJmYWNlLiBXaXRoIHRoZSBjYWxsIGNvdW50ZXJz LCB5b3UgY2FuIHB1dCBhc2lkZQ0KPiBWZXJpZmlhYmxlIGNvbXBsZXRlbHkgd2hpbGUgbWFraW5n IG1vc3Qgb2YgeW91ciBhc3NlcnRpb25zIGRpcmVjdGx5IGluDQo+IG1ldGhvZHMgb2YgdGhlIGFu b255bW91cyBjbGFzcy4NCg0Kc29ycnkuIEkgZG9uJ3QgdW5kZXJzdGFuZC4gVG8gY2hlY2sgZm9y IG1pc3NpbmcgdmFsdWVzIChvciBjYWxscykgeW91IGhhdmUgdG8gY2FsbCBzb21lIGtpbmQgb2Yg Y2hlY2tpbmcgbWVjaGFuaXNtIGF0IHRoZSBlbmQgb2YgdGhlIHRlc3QuIFlvdSBjYW4gZWl0aGVy IHdyaXRlIHlvdXJzZWxmIGEgY2hlY2sgbWV0aG9kIG9yIHVzZSBzb21lIG9mIG91ciBWZXJpZmlh YmxlIGluZnJhc3RydWN0dXJlLiBJIGRvbid0IHVuZGVyc3RhbmQgdGhlIGRpZmZlcmVuY2UuIEkg YWxzbyBkb24ndCBzZWUgd2h5IHRoZSBpbm5lciBjbGFzcyBpcyBtb3JlIGVmZmljaWVudC4NCg0K QXMgZmFyIGFzIEkgY2FuIHRlbGwgdGhlIGlubmVyIGNsYXNzZXMgaW1wbGVtZW50IG11Y2ggdGhl IHNhbWUgY29kZSBhcyB3ZSBkbyB3aXRoIG91ciBtb2NrIGxpYnJhcmllcywgZXhjZXB0IHRoYXQg dGhleSBtYWtlcyB0aGUgdGVzdCBtZXRob2RzIGxvbmdlci4gSSBhbHNvIGRvbid0IHF1aXRlIHNl ZSBob3cgaXQgd291bGQgc2NhbGUgdXAgdG8gbW9yZSBjb21wbGV4IG9iamVjdHMuIEFyZSB5b3Ug c2F5aW5nIHRoYXQgdGhlIHRyaWNrIGlzIHRvIGltcGxlbWVudCBpbiB0aGUgaW5uZXIgY2xhc3Mg b25seSB0aG9zZSBtZXRob2RzIHlvdSBuZWVkIGZvciB0aGUgY3VycmVudCB0ZXN0PyBJZiBzbywg dGhhdCBuZWVkIGNsYXJpZnlpbmcuDQoNClN0ZXZlDQoNCg== |
From: Thomas C. <ald...@ne...> - 2001-08-16 18:33:53
|
Steve Thank you for your feedback. "why would you assert the verify? The verify method should throw an AssertionFailedException if it fails." Agreed. "why don't you use an ExpectationCounter? Also, how does the check method differ from using Verifiable, which is a consistent interface we use across the library (and is searcheable by reflection)." Sure, let's use the ExpectationCounter with the inc() and verify() methods. I used CallCounter simply to show that the goal for the check() method is not to do any verification of parameters used in method calls or of return values, but simply to check that the *number* of method calls is correct. Most of the verification of correct parameters and such is done in the overridden methods themselves. As long as the references in the outer class are final, we can use any of the Expectation-implementing classes of com.mockobjects.* *anywhere* in the test, including in the anonymous inner class methods, and call their verify methods at the end of test as appropriate. So far, through refactoring and demeter-based method design of the production classes, my experience is that the simple counter suffices. Do you have any complex class examples where doing verifications in the inner class and checking the number of method calls via a counter is not adequate? "Are you saying that the trick is to implement in the inner class only those methods you need for the current test? If so, that need clarifying." This is precisely the point. We only override the methods we need for the current test and make our assertions within those overridden methods. We can also, as above, use an ExpectationCounter to check message calls. If this is not clear, what do you suggest to make sure it is clear? Also, remember that the current context is pre-JDK 1.3 *with* interfaces to mock. If we use JDK 1.3, we use a proxy and declare an invocation handler as an anonymous class - we do not need to provide *any* mock implementation class and only have to implement handling for the specific methods used by the method being tested. Putting JDK 1.3 aside, the mock objects project, AFAIK, is dependent on the existence of interfaces for everything that is mocked. IMO, this is an unrealistic expectation of a project where it is not generally useful to have an interface for every single class, especially package protected classes. Therefore, there are some classes for which we want to mock a subset of their methods for a particular test, but have no interface. The inner class patterns allow the overriding to facilitate this. Moreover, when testing a method of a particular class, we can use an anonymous inner class to override other methods of the *same class* as necessary to make fine-grained assertions and test our expectations. How do we do this without the patterns described? Tom |
From: Steve F. <st...@m3...> - 2001-08-17 21:51:48
|
PiBBcyBsb25nIGFzIHRoZSByZWZlcmVuY2VzIGluIHRoZSBvdXRlciBjbGFzcyBhcmUgZmluYWws IHdlIGNhbiB1c2UgYW55IG9mDQo+IHRoZSBFeHBlY3RhdGlvbi1pbXBsZW1lbnRpbmcgY2xhc3Nl cyBvZiBjb20ubW9ja29iamVjdHMuKiAqYW55d2hlcmUqIGluIHRoZQ0KPiB0ZXN0LCBpbmNsdWRp bmcgaW4gdGhlIGFub255bW91cyBpbm5lciBjbGFzcyBtZXRob2RzLCBhbmQgY2FsbCB0aGVpciB2 ZXJpZnkNCj4gbWV0aG9kcyBhdCB0aGUgZW5kIG9mIHRlc3QgYXMgYXBwcm9wcmlhdGUuIFNvIGZh ciwgdGhyb3VnaCByZWZhY3RvcmluZyBhbmQNCj4gZGVtZXRlci1iYXNlZCBtZXRob2QgZGVzaWdu IG9mIHRoZSBwcm9kdWN0aW9uIGNsYXNzZXMsIG15IGV4cGVyaWVuY2UgaXMgdGhhdA0KPiB0aGUg c2ltcGxlIGNvdW50ZXIgc3VmZmljZXMuIERvIHlvdSBoYXZlIGFueSBjb21wbGV4IGNsYXNzIGV4 YW1wbGVzIHdoZXJlDQo+IGRvaW5nIHZlcmlmaWNhdGlvbnMgaW4gdGhlIGlubmVyIGNsYXNzIGFu ZCBjaGVja2luZyB0aGUgbnVtYmVyIG9mIG1ldGhvZA0KPiBjYWxscyB2aWEgYSBjb3VudGVyIGlz IG5vdCBhZGVxdWF0ZT8NCg0KaW4gc29tZSBvZiB0aGUgY29kZSBJJ3ZlIGJlZW4gd29ya2luZyB3 aXRoLCBzdWNoIGFzIGFwcGxpY2F0aW9uIHNlcnZlcnMsIGVhY2ggdGVzdCBtYXkgaW52b2x2ZSBz ZXZlcmFsIG1vY2sgb2JqZWN0cy4gUHV0dGluZyBhbGwgb2YgdGhvc2UgaW50byBhIHNpbmdsZSBt ZXRob2Qgd291bGQgZ2V0IHJhdGhlciBsYXJnZS4NCiANCj4gIkFyZSB5b3Ugc2F5aW5nIHRoYXQg dGhlIHRyaWNrIGlzIHRvIGltcGxlbWVudCBpbiB0aGUgaW5uZXIgY2xhc3Mgb25seSB0aG9zZQ0K PiBtZXRob2RzIHlvdSBuZWVkIGZvciB0aGUgY3VycmVudCB0ZXN0PyBJZiBzbywgdGhhdCBuZWVk IGNsYXJpZnlpbmcuIg0KPiANCj4gVGhpcyBpcyBwcmVjaXNlbHkgdGhlIHBvaW50LiBXZSBvbmx5 IG92ZXJyaWRlIHRoZSBtZXRob2RzIHdlIG5lZWQgZm9yIHRoZQ0KPiBjdXJyZW50IHRlc3QgYW5k IG1ha2Ugb3VyIGFzc2VydGlvbnMgd2l0aGluIHRob3NlIG92ZXJyaWRkZW4gbWV0aG9kcy4gV2Ug Y2FuDQo+IGFsc28sIGFzIGFib3ZlLCB1c2UgYW4gRXhwZWN0YXRpb25Db3VudGVyIHRvIGNoZWNr IG1lc3NhZ2UgY2FsbHMuIElmIHRoaXMgaXMNCj4gbm90IGNsZWFyLCB3aGF0IGRvIHlvdSBzdWdn ZXN0IHRvIG1ha2Ugc3VyZSBpdCBpcyBjbGVhcj8NCg0KVGhlIGlkZWEgaXMgaW50ZXJlc3Rpbmcg YXMgYW5vdGhlciB0b29sIGluIHRoZSBib3guIEkgdGhpbmsgeW91IG5lZWQgdG8gYmUgbW9yZSBl eHBsaWNpdCBhYm91dCBvbmx5IG92ZXJyaWRpbmcgdGhlIG1ldGhvZHMgeW91ciBpbnRlcmVzdGVk IGluLiANCg0KT25lIHBvdGVudGlhbCBjYXRjaCBpcyBwcm90ZWN0aW9uIGFnYWluc3QgcmVmYWN0 b3JpbmcuIElmIHNvbWUgdGFyZ2V0IGNvZGUgY29kZSBjaGFuZ2VzIGFuZCBjYWxscyBhIGRpZmZl cmVudCBtZXRob2QsIHlvdXIgdGVzdHMgbWF5IG5vdCBjYXRjaCBpdCwgb3IgYXQgbGVhc3Qgbm90 IG9idmlvdXNseS4NCg0KPiBQdXR0aW5nIEpESyAxLjMgYXNpZGUsIHRoZSBtb2NrIG9iamVjdHMg cHJvamVjdCwgQUZBSUssIGlzIGRlcGVuZGVudCBvbiB0aGUNCj4gZXhpc3RlbmNlIG9mIGludGVy ZmFjZXMgZm9yIGV2ZXJ5dGhpbmcgdGhhdCBpcyBtb2NrZWQuIElNTywgdGhpcyBpcyBhbg0KPiB1 bnJlYWxpc3RpYyBleHBlY3RhdGlvbiBvZiBhIHByb2plY3Qgd2hlcmUgaXQgaXMgbm90IGdlbmVy YWxseSB1c2VmdWwgdG8NCj4gaGF2ZSBhbiBpbnRlcmZhY2UgZm9yIGV2ZXJ5IHNpbmdsZSBjbGFz cywgZXNwZWNpYWxseSBwYWNrYWdlIHByb3RlY3RlZA0KDQpub3Qgc28uIFRoZSBvbmx5IHRoaW5n IHdlIGNhbid0IGRvIGlzIGZpbmFsIGNsYXNzZXMgd2hpY2ggc2hvdWxkIGJlIHdyYXBwZWQgYW55 d2F5LiBXZSBzdWJjbGFzcyBmcm9tIHJlYWwgY2xhc3NlcyBhbmQgb3ZlcnJpZGUgZXZlcnl0aGlu Zy4NCg0KV2UgZG8gdGVuZCB0byBoYXZlIGEgdHJpcGxlIChpbnRlcmZhY2UsIG1vY2ssIGFuZCBh dCBsZWFzdCBvbmUgcmVhbCkgZm9yIGV2ZXJ5dGhpbmcuIEl0IGNhbiBiZSB0ZWRpb3VzLCBidXQg dGhhdCdzIG1vcmUgYW4gSURFIGVudmlyb25tZW50IHRoYW4gYW55dGhpbmcgZWxzZS4gSSdtIHBy ZXR0eSBzdXJlIHRoYXQgc29tZSBzdXBwb3J0IGNvdWxkIGJlIGF1dG9tYXRlZC4NCg0KV2UgYWxz byB0ZW5kIG5vdCB0byBib3RoZXIgd2l0aCBwYWNrYWdlIHByb3RlY3Rpb24uIFdlIGRvbid0IGtu b3cgd2hpY2ggY2xhc3NlcyB3aWxsIGJlIHVzZWZ1bCBpbiB0aGUgbG9uZyBydW4uIElmIHdlIHdh bnQgdG8gc2VwYXJhdGUgYSBmcmFtZXdvcmsgd2UgY2FuIHB1dCB0aGUgaW50ZXJmYWNlcyBpbiBh IGRpZmZlcmVudCBwYWNrYWdlLCBsaWtlIGp1bml0Lg0KDQpTdGV2ZQ0KDQo= |