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
namespace test
{
    public class DBHelper
    {
        public static SqlConnection GetConnection()
        {
            return new SqlConnection();
        }
    }

    public class TestClass
    {
        public static void GetProductivityReportData(
            DateTime start_date,
            DateTime end_date)
        {
            using (SqlConnection sql_conn = DBHelper.GetConnection())
            {
                SqlCommand sqlCmd = new SqlCommand("BvSpRptProdByInterEx2", sql_conn);
                
                sqlCmd.Parameters.AddWithValue("@StartDate", start_date);
                sqlCmd.Parameters.AddWithValue("@EndDate", end_date);
            }
        }
    }

    [TestClass]
    public class Class1
    {
        [TestCleanup]
        public void TestCleanup()
        {
            MockManager.ClearAll();
        }

        [TestInitialize]
        public void TestInitialize()
        {
            MockManager.Init();
        }

        [TestMethod]
        public void Test1()
        {
            Bar(true);
        }

        [TestMethod]
        public void Test2()
        {
            Bar(false);
        }

        public void Bar(bool mockSqlParameter)
        {
            DateTime startDate = DateTime.Now;
            DateTime endDate = DateTime.Now;

            if (mockSqlParameter)
            {
                SqlParameterCollection mockedParams = RecorderManager.CreateMockedObject<SqlParameterCollection>();
            }

            using (RecordExpectations recorder = new RecordExpectations())
            {
                SqlConnection conn = DBHelper.GetConnection();
                recorder.ReturnDefaultImplementation();

                conn.Dispose();

                SqlCommand sqlCmd = new SqlCommand("", conn);
                recorder.ReturnDefaultImplementation().CheckArguments("BvSpRptProdByInterEx2", Check.IsAny());

                recorder.ExpectAndReturn(sqlCmd.Parameters.AddWithValue("@StartDate", startDate), null);
                recorder.CheckArguments("@StartDate", startDate);
            }

            TestClass.GetProductivityReportData(startDate, endDate);
        }
    }
}


1st test pass, second fails with error:
------ Test started: Assembly: delete.dll ------

TestCase 'test.Class1.Test2'
failed: System.NullReferenceException: Object reference not set to an instance of an object.
at System.Data.SqlClient.SqlParameterCollection.ValidateType(Object value)
at System.Data.SqlClient.SqlParameterCollection.Add(Object value)
at System.Data.SqlClient.SqlParameterCollection.Add(SqlParameter value)
at System.Data.SqlClient.SqlParameterCollection.AddWithValue(String parameterName, Object value)
D:\!6\delete\Class1.cs(29,0): at test.TestClass.GetProductivityReportData(DateTime start_date, DateTime end_date)
D:\!6\delete\Class1.cs(85,0): at test.Class1.Bar(Boolean mockSqlParameter)
D:\!6\delete\Class1.cs(58,0): at test.Class1.Test2()


0 passed, 1 failed, 0 skipped, took 6,27 seconds.



As you see from the code, the only difference is that in first test we calling RecorderManager.CreateMockedObject<SqlParameterCollection>() but in second test - not. BUT, we NOT using CreateMockedObject call result. I was always think that we can call CreateMockedObject to create a mocked object instance and that call IS NOT CHANGING test behaviour if we not using it result.

PS:
Can somebody explain me difference between:
RecorderManager.CreateMockedObject
MockManager.Mock
MockManager.MockObject

PPS:
Is RecorderManager.CreateMockedObject creates any expectations or records any mocks?

Thanks.
asked by tom3m (9.7k points)

5 Answers

0 votes
Hi,

I was always think that we can call CreateMockedObject to create a mocked object instance and that call IS NOT CHANGING test behavior if we not using it result.


Almost true Except where static constructor is concerned. When you create a MockObject the cctor and the ctor are activated, in this case they are not mocked and the activation of the cctor can definitely have some side affects that will trigger changes in behavior.
This is specifically true if you use Natural mocking which has a complex mechanism to delay the activation of the cctor (if you like I can go into this but I'm not sure its relevant here)

PS:
The big difference between Mock and MockObject is that when you create a MockObject TypeMock will create a Mock instance - used for recording the expectation and a real Instance of the type to be mocked - to be used later on (usually as an argument for other methods).
This is very usefull for mocking Interfaces and Abstract classes which can't be simply created (we achieve this by using reflection).

creating a mock does not creates a real instance. Instead the next time an object of that type is created in the code (whether in the test code or in the TESTED code) the instance created will be attached to the mock.

to simplify
 MockManager.MockObject(sometype);

is closely the same as:
MockManager.Mock(someType);
sometpye instance = new someType();

There is a very small difference between
RecorderManager.CreateMockedObject and MockManager.MockObject
both basically do the same the only difference is that the RecorderManager will set the verify mode of the created object according to what is set on him and returns the MockObject.Object value.

PPS:
If you are referring to what you see in the tracer so the answer is yes. internally MockObject can creates expectations for the ctor and cctor. which will be recorded by the tracer since during the creation of the real instance they are called.

PPPS:
I was not able to reproduce your exact behavior. so I'm still unsure for the exact cause for your failure (when i run your tests here they both pass :( ). But we already had some problems with the cctor behavior of the sqlConnection and SQlCommand classes - currently we are trying to fix this.
Let me know if you can sent me an example solution for this.
answered by lior (13.2k points)
0 votes
Sorry, I'm so stupid, questions below.

>>Almost true Except where static constructor is concerned. When you create a MockObject the cctor and the ctor are activated
You mean, that, when I'm calling e.g. RecorderManager.CreateMockedObject<MyType>(); type mock creates real object instance and calls static and usual constructors????? 1st why TypeMock doing this, 2nd what it passes as constructor parameters? Why you not creating instance the same way as you doing it e.g. for interfaces???

>>creating a mock does not creates a real instance.
Upper you writing thats cctor and ctor are calld, here you writing that instance is not created. Ufff, sorry, I'm absolutely confused :((

Can you explain with examples these 3 methods (RecorderManager.CreateMockedObject MockManager.Mock MockManager.MockObject)? And explain the difference. Also, describe please, in every method key aspects:
* Is real instance created
* Is cctor or ctor called

PS:
>>I was not able to reproduce your exact beh
Yes, I've solution and can send it you, please send me email so I can answer with zip archive.
answered by tom3m (9.7k points)
0 votes
Hi Tom,
You must have found a bug :twisted: somewhere because what you say is correct.
Please try
CreateMockedObject<SqlParameterCollection>(Constructor.StaticNotMocked)

Start the TypeMock Tracer, and run the test, then tell me what methods of SqlParameterCollection are called.

Now to answer your questions.
1. When Mock is used TypeMock waits for the next time a new instance of the type is created and then mocks that instance.
2. When MockAll is used TypeMock mocks all past and future instances of the type
3. When MockObject is used, TypeMock creates a new instance and mocks it
4. When RecorderManager.CreateMockedObject is used you don't get the mock controller, but the actual mocked instance (same as MockObject.Object) you can set expectations using NaturalMocks.

:arrow: because it is not possible to create an instance of an abstract or interface, TypeMock builds a new class on the fly and mocks that class.

:arrow: because there is no implementation of those abstract methods - those methods are considered Strict and will fail if called

:arrow: When TypeMock creates a mocked instance (3 and 4) it will guess the default constructor arguments automatically unless you explicitly pass them.
answered by scott (32k points)
0 votes
OK.
The real problem was found and I'm guessing its in the gray area between a feature and a bug :twisted:
(Just joking its a bug)

As I mentioned in a previous post, the change in behavior is actually caused by the static constructor.
While in Test1 the statement:
SqlParameterCollection mockedParams = RecorderManager.CreateMockedObject<SqlParameterCollection>(Constructor.Mocked);
caused the cctor to be executed (the default behavior in this case is not to mock the constructors of the object created)
In Test2 the real cctor was not executed in any stage.

so how this effected the test?
well that relate to the call to:
sqlCmd.Parameters.AddWithValue("@EndDate", end_date);

which as the test is written is not mocked, and causes the "real" implementation to be executed.
unfortunately this call succeed if and only if the real cctor was done,

now after this long explanation I'm guessing that were having two real issues here:

:?: why is there a change in the behavior between the test?
Answer: Well there shouldn't be and we will work on fixing it.

:?: how can a user understand from the exception thrown how to solve this?
Answer: He probably can't. Thats why were working on changing that. (This is part of a bigger issue which relates to the ability to mock fields.)

hope this explain everything
feel free to post if you more questsions
answered by lior (13.2k points)
0 votes
Hi,

The fix for all these issues was included as part of the 4.22 release.
answered by lior (13.2k points)
...