chevron-thin-right chevron-thin-left brand cancel-circle search youtube-icon google-plus-icon linkedin-icon facebook-icon twitter-icon toolbox download check linkedin phone twitter-old google-plus facebook profile-male chat calendar profile-male
0 votes
From C#, I am using a COM object. A method on the object returns a safearray of strings. Trying to mock this method call but TypeMock record expectations gives an error at runtime.
The code is fully functional; I'm just having trouble with the TypeMock (4.1) unit tests.

Here is the IDL for the interface method

   HRESULT Names([out, retval] SAFEARRAY(BSTR) *pQNames );


Here is the C++/COM implementation for the interface method

   STDMETHODIMP CRepSheet::get_Names(/*[out, retval]*/ SAFEARRAY **pQNames )
  {
     vector<TString> qNames;
     m_QuantaManager.GetNames(qNames);

     return VectorStrToSafeArray(qNames, pQNames);
 }


This is what the interface method looks like to C#
(as interpreted by VisualStudio)

   [Guid("...theGuid...")]
   [TypeLibType(1234)]
   public interface IRepSheet : IRepSheetGenerated
    {
        ...

        [DispId(3)]
        Array Names { get; }


And the C# code that calls method QNames and need be tested

private RepSheet myRepSheet;
  ...

foreach (string name in myRepSheet.Names)
{
  ...
}



So, finally, attempts to mock this only result in runtime error

   private RepSheet  theRepSheet;
      ...

   List<string> names = new List<string>();
   names.Add("abcde");
   Array repNames = names.ToArray();

   using (RecordExpectations recorder = RecorderManager.StartRecording())
   {
        recorder.ExpectAndReturn(theRepSheet.Names, repNames);
        ...



Test method MyUnitTest.ToolTest.DeleteNamesTest threw exception: TypeMock.TypeMockException:
*** Cannot use Return in this sequence, there must be a mocked statement first
Perhaps you are trying to mock a method from mscorlib.


Tried to do this a bit differently, but get the same error.

   using (RecordExpectations recorder = RecorderManager.StartRecording())
   {
        Array x = theRepSheet.Names;
        recorder.Return(repNames);


Presumably, the problem has to do with the Array type, but I do not see how to resolve this.

.
asked by cabin (2.3k points)

4 Answers

0 votes
The good news is that the problem you're facing does not have anything to do with the SafeArray.
In .NET COM Interop proxy is partially implemented in MSCorLib (which Typemock does mock right now) and so Isolator doesn't fake the call inside the recorder block.

If you need to stub the COM calls you can fake the interface instead as suggested in this thread: https://www.typemock.com/community/viewtopic.php?t=961
answered by dhelper (11.9k points)
0 votes
Modifying the test code (in accord with the link you included with your reply), TypeMock is no longer throwing an exception, but ExpectAndReturn is not returning the array (repNames). Upon executing the mocked statement, the return is empty (size of the array is zero).

            List<string> names = new List<string>();
            names.Add("abcde");
            Array repNames = names.ToArray();

            IRepSheet mockRepSheet = RecorderManager.CreateMockedObject<IRepSheet>(); 

            using (RecordExpectations recorder = RecorderManager.StartRecording())
            {
                recorder.ExpectAndReturn(mockRepSheet.Names, repNames);


.
answered by cabin (2.3k points)
0 votes
I'm having problmes reproducing this problem. Can you please provide the rest of the code - especially where you call mockRepSheet.Names?
answered by dhelper (11.9k points)
0 votes
With another set of eyes helping, we got this to work, though I don't entirely understand it.

The object being tested includes property "RepSheet", which returns instance: theRepSheet.
There seemed no need to mock that.

This did not work: ExpectAndReturn(theRepSheet.Names,repNames)
This does work: ExpectAndReturn(myObject.RepSheet.Names,repNames)

MyClass obj = new MyClass(theRepSheet);
MyClassAccessor myObjAccessor = new MyClassAccessor(obj);

 ...

private void SetTypeMockExpectationsForInitialize(MyClassAccessor myObject)
{
    List<string> names = new List<string>(); 
    names.Add("abcde"); 
    Array repNames = names.ToArray(); 

    using (RecordExpectations r= RecorderManager.StartRecording()) 
    { 
         r.ExpectAndReturn(myObject.RepSheet.Names, repNames);


The code being tested:

internal class MyClass
{
   ...

   public XRepSheet RepSheet 
   {
      get { return _myRepSheet; }
   }

   private void Initialize()
   {
      foreach (string name in RepSheet.Names) 
      { 
         ... 
      }
answered by cabin (2.3k points)
...