Unit Testing Question:

Unit Testing Question:

I have a class which implements an interface, and I want to unit test it. I am testing the class, so my test code references the class.

However, in my class, it passes itself as an interface reference to another interface, and when this happens, my test class cannot be Freed and breaks.

The code below shows my problem (I use DUNITX).

Main has a field ISub, which knows about IMain.

When IMain is created, it passes itself as a reference to ISub (TMain -> IMain cast)

when I call FMain.Free; it breaks with an "invalid pointer operation" as it has been used as an interface.

test code:

type
Isub = interface end;
IMain = interface end;

TSub = class( TInterfacedObject, Isub )
FMain: IMain;
constructor Create( const AOwner: IMain);
end;

TMain = class( TInterfacedObject, IMain )
FSub: Isub;
constructor Create;
destructor Destroy; override;
end;

[TestFixture]
TUT_TEST = class
private
FMain : TMain;
public
[Setup] procedure Setup;
[Teardown] procedure Teardown;

[Test] procedure A;
end;

implementation

constructor TSub.Create(const AOwner: IMain);
begin
inherited Create;

FMain := AOwner;
end;

constructor TMain.Create;
begin
inherited;

FSub := TSub.Create( Self );
end;
destructor TMain.Destroy;
begin
inherited; // <-- never reach this line
end;

procedure TUT_TEST.Setup;
begin
FMain := TMain.Create;
end;
procedure TUT_TEST.Teardown;
begin
if Assigned( FMain ) then
FMain.Free; // <-- fail as this has been referenced as an interface
FMain := nil;
end;

procedure TUT_TEST.A;
begin
Assert.IsNotNull( FMain );
end;


So, I have the following questions:
How do you test the class when this happens?
Would you just test the interface instead, avoiding this problem?
Do I ignore the Free in my Teardown and just let it be handled as an interface?

In my code, ISub must know about its owner in order to call it. This COULD be done with events, but it is not a problem in production as everything is interfaces, only by adhering to the rule of testing the class rather than the interface am I having this issue.

Any help would be great!

Comments

  1. "I have a class which implements an interface, and I want to unit test it. I am testing the class, so my test code references the class." - I would say that is the crux of the problem right there... if you are testing the class, why are you bothering with interfaces at all. 

    As others have pointed out, you have a circular reference issue - https://www.finalbuilder.com/resources/blogs/postid/410/weakrefence-in-delphi-solving-circular-interfac

    ReplyDelete
  2. Thanks for all the help here folks!
    I have used all the suggestions and solved my issue:

    1. I now test the class through the interface and no longer have to deal with mixing types
    2. I will next review the sub/main relationship to see if I can break the coupling, but using an interface is now working fine for now.
    3. In other scenarios I would use dependency injection to pass sub into main, but in my real case Main is a factory created COM interface. So I am stuck with Sub having to be created in the constructor.

    Thanks again!

    ReplyDelete
  3. Asbjørn Heid if you want to test specific implementations of an interface, generate a general test class which tests only the interface and subclasses of the general in which you only change the implementation class.
    All tests should be green or your implementation do not implement your interface correctly.

    ReplyDelete

Post a Comment