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
Welcome to Typemock Community! Here you can ask and receive answers from other community members. If you liked or disliked an answer or thread: react with an up- or downvote.
0 votes
I'm having a problem with WhenCalled not finding the correct override when a method parameter is a nullable type:

(Abbreviated sanitized example)
class CExternalDevice
{        
      // MR_1
      internal CRawResponseData MakeRequest( RQHeader header, Byte[] cmdPayloadData, UInt32? expectedResponseSize)
      {
         // talk to external device not avail during test

         // ...

         // ultimately 

         return new CRawResponseData();
      }

      // MR_2
        internal CRawResponseData MakeRequest( RQHeader header, UInt32? expectedResponseSize)
        {
            return WriteCommandPacket(header, new Byte[] {}, expectedResponseSize);
        }

      // MR_3
        internal CRawResponseData MakeRequest( RQHeader header )
        {
            return WriteCommandPacket(header, new Byte[] {}, 0);
        }

      // MR_4
        internal CRawResponseData MakeRequest( RQHeader header, Byte[] data)
        {
            return WriteCommandPacket( header, data, 0);
        }
}


class CSomethingToTest
{
       private CFancyResponseData SimpleMakeFancyRequest( CATEGORY category, CODE cmd )
        {
            RQHeader header = new RQHeader( category, code );

            //Write packet with an unknown (null) response size
            CRawResponseData response = _myExternalDevice.MakeRequest( header, expectedResponseSize: null );

            return new CFancyResponseData( response );
        }

       private CRawResponseData SimpleMakeRequest( CATEGORY category, CODE cmd )
        {
            RQHeader header = new RQHeader( category, code );

            CRawResponseData response = _myExternalDevice.MakeRequest( header );

            return  response;
        }
}



[TestMethod( )]
public void MyTest()
{
         CSomethingToTest  testTarget = new CSomethingToTest();

         bool fakeDeviceConnected = true;

            CExternalDevice fakeExternalDevice = Isolate.Fake.Instance<CExternalDevice>( Members.ReturnRecursiveFakes, ConstructorWillBe.Ignored );
            Isolate.WhenCalled( ( ) => fakeExternalDevice.Connected( ) ).DoInstead( c =>
                {
                    return fakeDeviceConnected;
                }
            );

            testTarget._myExternalDevice = fakeExternalDevice;

            RQHeader dummyHeader = new RQHeader( 0, 0 );
            Byte[ ] dummyData = new byte[ 0 ];

         //  UInt32? dummyResponseSize = null;
         //  doing this makes ANON_FAKE_MR_1  throw an exception  "param cannot be null",

            UInt32? dummyResponseSize = 0;

         CRawResponseData simulatedResponse = new CRawResponseData();

            Isolate.WhenCalled( ( ) => fakeExternalDevice.MakeRequest( dummyHeader ) )
                                    .CallOriginal( );

            Isolate.WhenCalled( ( ) => fakeExternalDevice.MakeRequest( dummyHeader, dummyResponseSize ) )
                                    .CallOriginal( );

            Isolate.WhenCalled( ( ) => fakeExternalDevice.MakeRequest( dummyHeader, dummyData ) )
                                    .CallOriginal( );

         // ANON_FAKE_MR_1
            Isolate.WhenCalled( ( ) => fakeExternalDevice.MakeRequest( dummyHeader, dummyData, dummyResponseSize ) )
                                .DoInstead( callContext => 
                                    {
                                        return  simulatedResponse;
                                    }
            );

         ////////////////
         //  this calls MR_3, and then ANON_FAKE_MR_1, as expected
         testTarget.SimpleMakeRequest( 11, 22 );

         /////////////////
         //  this calls MR_2, and then MR_1  (ignores ANON_FAKE_MR_1)  NOT expected (or wanted)
         testTarget.SimpleMakeFancyRequest( 10, 20 );


}


Any suggestions about what I'm doing wrong?
asked by jAH_BD (720 points)

6 Answers

0 votes
Hi,

In the code you posted MakeRequest is called once inside SimpleMakeRequest but from the test code it looks like you are expecting multiply calls for MakeRequest.
Is that the case? How many times MakeRequest gets called.

As a side note it is considered a good practice to do one assert per test (test should test one thing)
This will make your test easier to read and less error prone.
answered by ohad (35.4k points)
0 votes
As posted, I'm expecting MakeRequest to be called twice.
Once from the call to testTarget.SimpleMakeRequest()
and the second time from testTarget.SimpleMakeFancyRequest().

I expect both calls to be intercepted by the DoInstead marked ANON_FAKE_MR_1, only the first call is intercepted, the second call routes to one of the original functions ignoring all of the DoInstead functions.
answered by jAH_BD (720 points)
0 votes
AH, cut & paste error probably confusing the issue.
Corrected:

      // MR_2 
        internal CRawResponseData MakeRequest( RQHeader header, UInt32? expectedResponseSize) 
        { 
            return MakeRequest(header, new Byte[] {}, expectedResponseSize); 
        } 

      // MR_3 
        internal CRawResponseData MakeRequest( RQHeader header ) 
        { 
            return MakeRequest(header, new Byte[] {}, 0); 
        } 

      // MR_4 
        internal CRawResponseData MakeRequest( RQHeader header, Byte[] data) 
        { 
            return MakeRequest( header, data, 0); 
        } 
answered by jAH_BD (720 points)
0 votes
Hi,

In this case the Isolator behaving as expected.
The forth call to Isolate.WhenCalled statement specify that the method MakeRequest(RQHeader header, Byte[] cmdPayloadData, UInt32? expectedResponseSize)
Should be replaced with your delegate and return
This is exactly the forth call to MakeRequest in the test.

Also in the example code you are not using the callContext of the delegated inside DoInstead so you can do the same with shorter code:
Isolate.WhenCalled(() => fakeExternalDevice.MakeRequest(dummyHeader, dummyData, dummyResponseSize)).WillReturn(simulatedResponse);


Please let me know if it helps.
answered by ohad (35.4k points)
0 votes
Hi,

In this case the Isolator behaving as expected.
The forth call to Isolate.WhenCalled statement specify that the method MakeRequest(RQHeader header, Byte[] cmdPayloadData, UInt32? expectedResponseSize)
Should be replaced with your delegate and return
This is exactly the forth call to MakeRequest in the test.


The call to SimpleMakeRequest indeed goes to the delegate as expected (after calling MR_3).

I'm expecting the call to SimpleMakeFancyRequest to also go to the delegate after calling MR_2, but instead it goes to the original, MR_1.

you can do the same with shorter code:


Abbreviated code, the original does a little bit more.
answered by jAH_BD (720 points)
0 votes
Hi,

Sorry my mistake, now I understand the problem.
The problem you see is because of bug in the Isolator with Nullable types.
I'll update you once we will fix this issue.
answered by ohad (35.4k points)
...