Mocking fails for Spring.Collections descendant interfaces

Issue #403 closed
Zigmund Bulinsh created an issue

Good day, Stefan!

I faced the following issue when migrating projects from 1.2.6 to version 2.0:
Mocks for interfaces descendant from Spring.Collections (like interface(IDictionary<K,T>)) does not work anymore even if I add M+.

program Project1;
uses
Spring.Mocking,
Spring.Collections;
type
IMyDict = interface(IDictionary<Integer, String>)
['{C585B00C-AB3F-4414-B3AE-01C34FA8AE16}']
procedure NewMethod;
end;
var
LMock: Mock<IMyDict>;
begin
LMock.Setup.Executes.When.NewMethod;
end.

It works flawlessly with 1.2.6. Is this some conceptual change in the collections or am I missing something?

Kind regards, Zigmund.

Comments (8)

  1. Zigmund Bulinsh reporter

    Also when the mock behaviour is dynamic the methods which before returned mocked instance if setup was not provided return nil for spring.collections types.

  2. Zigmund Bulinsh reporter

    Implicit conversion in Setup.Returns(X).When.CreateNullableX does not work as well. So in version 1.2.6 we could write an underlying record type in returns for the method which returns nullable of that record. in 2.0 this does not work anymore (in one case there was even an exception on line where such a setup was done). Was this feature dropped now in 2.0 and now we always need to do Returns<Nullable<TRecord>>(LRec).When.CreateNullableTRecord?

  3. Stefan Glienke repo owner

    Is this some conceptual change in the collections

    Yes, it is - collection interface types have RTTI turned off to reduce code bloat because 99% it’s not needed.

    Adding {$M+} does indeed work:

    uses
      System.SysUtils,
      Spring.Mocking,
      Spring.Collections;
    
    type
      {$M+}
      IMyDict = interface(IDictionary<Integer, String>)
        ['{C585B00C-AB3F-4414-B3AE-01C34FA8AE16}']
        procedure NewMethod;
      end;
      {$M-}
    
    var
      LMock: Mock<IMyDict>;
    begin
      try
        LMock.Instance.NewMethod;
        LMock.Received(Times.Once).NewMethod;
      except
        on E: Exception do
          Writeln(E.ClassName, ': ', E.Message);
      end;
      Readln;
    end.
    

    You can however turn it back on by modifying Spring.Collections.pas For other issues (although related) please file a separate issue and don’t put them into “oh, by the way” comments, thank you :)

  4. Zigmund Bulinsh reporter

    Thank you for your reply. There is just quite a lot of places in the unit tests where methods return IList, IDictionary and they were never setup explicitly. Seems that now we are forced to do so.. or maybe we will make some conditional to generate RTTI for unit test projects, but not for production..

  5. Stefan Glienke repo owner

    FWIW I have looked after passing type T to Returns when the method returns Nullable<T> and from what I can see this works.

    The code that handles this is in CastToReturnType in Spring.Mocking.Interceptor.pas - if you have some code to repro this issue please file this as new issue, thank you.

  6. Zigmund Bulinsh reporter
    Adding {$M+} does indeed work:
    

    Only for methods defined in the descendant interface (NewMethod). Once trying to verify mock or setup on methods from IDictionary<Integer, String> it fails with EInsufficientRtti - because as you already mentioned there is no RTTI anymore for collections.

  7. Log in to comment