3

I'm attempting to write a unit test for a simple factory class that creates one of several possible implementing objects and returns it as an interface reference.

DUnit has a built in procedure, CheckIs(AObject: TObject; AClass: TClass; msg: string), that based on its name and the parameters it accepts should fail the test if the object's class type doesn't match the expected one. The only problem is it requires an object reference not an interface reference.

So I'm trying to use CheckTrue and perform the comparison in the body of the test but I'm not as familiar with Delphi's type checking support as I am with C#'s.

I know the is operator is out of the question since it only works with object references.

CheckTrue(LMyInterfaceReference {comparison here} TMyClass);

Any suggestions?

BTW, I'm using Delphi 2009 so I don't have access to the new RTTI support added in 2010+.

Kenneth Cochran
  • 11,954
  • 3
  • 52
  • 117
  • 2
    Delphi 2010 lets you do `Obj := IUnknown as TObject` – Cosmin Prund Mar 04 '11 at 17:01
  • 1
    Since you are using Delphi 2009 you'll need Barry Kelly's [workaround](http://stackoverflow.com/questions/5153486/how-to-compare-tfunc-tproc-containing-function-procedure-of-object/5153933#5153933), published very recently right here on Stack Overflow! Not posting as an answer since that would feel like stealing rep from Barry! – David Heffernan Mar 04 '11 at 17:08
  • @codeelegance, Barry's hack + an optional IFEDF when you upgrade your Delphi version looks like a pretty good deal. The hack works on Delphi 2009, and if you upgrade, you get the functionality (probably the same code) out-of-the-box. – Cosmin Prund Mar 04 '11 at 17:13
  • 1
    Instead of checking for a class, wouldn't it be more OOPish to check if a spcific interface is implemented? – mjn Mar 04 '11 at 17:25
  • 1
    I'm guessing that OP is doing unit tests and wants to check that the implementing object is as expected. – David Heffernan Mar 04 '11 at 17:27
  • I wouldn't do it unless it increases the unit test code coverage percentage value ;) – mjn Mar 04 '11 at 17:40

3 Answers3

4

I'm wondering why you MUST have to test this... maybe you really don't have to.

But if knowing the underlying object of a Interface is a must, you have two choices:

  • Add a method to the interface which returns the underlying object, just a TObject, and implement this in each class just by returning self.
  • Hack a bit, for example using this Interface to object routine.
jachguate
  • 16,976
  • 3
  • 57
  • 98
  • If a class is expected to have a specific behavior why would you NOT test it? You pass a type code (string, enum, whatever) to a factory and expect it to give you a corresponding instance of the type you requested. If it return's nil or an instance of the wrong type you've got a bug. – Kenneth Cochran Mar 04 '11 at 18:00
  • 3
    Codeelegance, if you're returning an interface, then check whether the *interface* is what was expected. If shouldn't matter what type the underlying object has, or even that there *is* an underlying object. As long as the interface behaves the way it's supposed to, why does the class matter? If the precise class type is important, then return classes, not interfaces. – Rob Kennedy Mar 04 '11 at 18:51
  • 1
    @Rob I am testing whether the *factory* behaves the way its suppose to. The consumer of the interface doesn't know or care about the implementation of the interface. It depends on the factory to ensure the correct implementation is created. That is what I am testing. – Kenneth Cochran Mar 04 '11 at 19:37
  • 2
    The factory's job is to produce things that implement a particular interface. If the factory gives you something, and the methods you call on that thing do what they're supposed to, then the factory has done its job. You're testing at the wrong level. – Rob Kennedy Mar 04 '11 at 20:03
  • Wrong level? Show me class that doesn't need to be tested and I show you a class that isn't needed at all. *You only need to test the stuff that you want to work.* - Kent Beck – Kenneth Cochran Mar 04 '11 at 22:44
  • I'm with @KennethCochran on this one I want to know I'm using the correct implementation before I start looking at why something that is dependent on it "isn't working" Besides the bit of code you don't test is always the bit of code it turns out you should have. – Tony Hopkinson Feb 04 '13 at 12:55
4

If you don't like hacks and don't feel like upgrading to Delphi 2010+ you may use an interface like this:

IImplementingObjectInterface = interface
  function GetImplementingObject: TObject;
end;

Make sure your objects also implement this interface and use it to extract the implementing object. If you need to do this for a lot of objects you can define your own TInterfacedObject derivate that already implements this so you can simply change your inheritance and be done.

Cosmin Prund
  • 25,498
  • 2
  • 60
  • 104
1

Barry Kelly (one of the main Embarcadero Delphi Compiler Engineers) wrote a nice An ugly alternative to interface to object casting this week.

It answers your question.

The fun is that Hallvard Vassbotn wrote a very similar piece of code back in 2004.

From Delphi 2010 on, you can just use an is check or as cast to go back from interface references to object references.

--jeroen

Community
  • 1
  • 1
Jeroen Wiert Pluimers
  • 23,965
  • 9
  • 74
  • 154