Memoryleak when dynamically mocked interface is passed as result of function for the same mock

Issue #362 resolved
Deeem2031 created an issue

Tested with https://bitbucket.org/sglienke/spring4d/commits/121f07661f77e8e14581c5bc1d4089e4cf2cd95c

A memory leak occurs when setting up a mock to return itself as a result of a function call.

parentMock.Setup.Returns(TValue.From<IParent>(parentMock.AsType<IParent>.Instance)).When.GetChild;

I attached a patch which contains a test demonstrating the problem.

Comments (6)

  1. Stefan Glienke repo owner

    Well, yes, duh - circular reference.

    I think you meant parentMock.AsType<IChild> because that code would fail anyway.

    I don’t see the use case of this - mocks automatically return mocks for interface returning methods so this is not necessary at all. They are different instances though but I doubt the code under test would fail in that case.

  2. Stefan Glienke repo owner

    Not sure what commit that should be (maybe some that got lost due to a force push) but I went back to bba6f47 and it leaks there as well.

    FWIW call Reset on the mock to clear its expectations and resolve the circular reference.

  3. Deeem2031 reporter

    Calling Reset does only help if you didn’t call parentMock.AsType<IChild>() before. Not sure why. So this still results in a memory leak:

    var
      parentMock: Mock<IParent>;
    begin
      parentMock.AsType<IChild>();
      parentMock.Setup.Returns(TValue.From<IChild>(parentMock.AsType<IChild>.Instance)).When.GetChild;
      parentMock.Reset;
      Check(True); // no fail condition; check for memory leaks
    end;
    

    Btw. the use case is for a workaround of multiple inheritance on interfaces. I’m not quite sure if this still satisfies good code architecture but we have a codebase with interfaces inheriting multiple interfaces (which is not possible natively in Delphi) by using functions.

    type
      IBase1 = interface
        function BaseFunction: Boolean;
      end;
    
      IBase2 = interface
        function BaseFunction2: Boolean;
      end;
    
      IImplementBothBases = interface(IBase1)
        function AsBase2: IBase2;
      end;
    

    If you have an instance of IImplementBothBases you can now access IBase1 and IBase2 without having to deal with the situation the class might not implement IBase2, as these are always implemented by simply returning Self.

    And this is exactly the behavior we replicated with parentMock.Setup.Returns(TValue.From<IChild>(parentMock.AsType<IChild>.Instance)).When.GetChild; or implementBothBases.Setup.Returns(TValue.From<IBase2>(implementBothBases.AsType<IBase2>.Instance)).When.AsBase2;

  4. Stefan Glienke repo owner

    var
      parentMock: Mock<IParent>;
      childMock: Mock<IChild>;
    begin
      childMock := parentMock.AsType<IChild>;
      parentMock.Setup.Returns(childMock.Instance).When.GetChild;
      parentMock.Reset;
    

  5. Log in to comment