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
Hello,

I think that I have found a bug.
I am trying to mock my database class and I am having some difficulties.

Here is my mock code:
MockManager.Init();
m_MockConnection = MockManager.Mock(typeof(SqlConnection));
m_MockConnection.ExpectCall("Open");
m_MockConnection.ExpectGet("State",ConnectionState.Open);
         
MockObject mockCommand = MockManager.MockObject(typeof(IDbCommand));
IDbCommand command = (IDbCommand) mockCommand.Object;
m_MockConnection.ExpectAndReturn("CreateCommand",command);


The error occurs in the evaluation of the ExpectedAndReturn method.

It looks at the object type and not the interface type.

The error message is:
TypeMock.TypeMockException : No method CreateCommand in type System.Data.SqlClient.SqlConnection returns MockIDbCommand
at TypeMock.Mock.SetExpectation(String method, Object ret, Boolean isException, Boolean isAlways, Int32 timesToRun)
at TypeMock.Mock.ExpectAndReturn(String method, Object ret, Int32 timesToRun)
at TypeMock.Mock.ExpectAndReturn(String method, Object ret)



I think this is a bug.

Regards,

Benjamin
asked by benjamin dover (1.8k points)

6 Answers

0 votes
m_MockConnection = MockManager.Mock(typeof(SqlConnection)); 
IDbCommand command = (IDbCommand) mockCommand.Object;
m_MockConnection.ExpectAndReturn("CreateCommand",command);


It looks at the object type and not the interface type.



Hi,
TypeMock looks at all base classes and Interfaces to check that the return value is correct.

If we look at the CreateCommand documentation of SqlConnection, we see that it returns an SqlCommand and not an IDbCommand, just mock SqlCommand :D
answered by scott (32k points)
0 votes
Hi Scott,

You were right.

The only thing that I have to say in my defense:
Why can't we mock real objects with the MockManager.MockObject?

If it was possible to mock concrete object with MockManager.MockObject, this would make it possible for my example to work:

MockManager.Init(); 
m_MockConnection = MockManager.Mock(typeof(SqlConnection)); 
m_MockConnection.ExpectCall("Open"); 
m_MockConnection.ExpectGet("State",ConnectionState.Open); 
          
MockObject mockCommand = MockManager.MockObject(typeof(SqlCommand));//Not valid!
m_MockConnection.ExpectAndReturn("CreateCommand", mockCommand.Object); 


Thanks again Scott for all your help.

BTW, I went around the limitation of the MockObject method. :wink:

Best regards,

Benjamin
answered by benjamin dover (1.8k points)
0 votes
Hi Benjamin,
Why can't we mock real objects with the MockManager.MockObject?

You can mock real objects with TypeMock (That is what it is all about).
To return a mocked instance from another mocked method you can do:
// mock the next instance of SqlCommand
MockObject mockCommand = MockManager.Mock(typeof(SqlCommand));
// create the mocked instance
SqlCommand mockSqlCommand = new SqlCommand();
// Return the mocked command when CreateCommand is called
m_MockConnection.ExpectAndReturn("CreateCommand",mockSqlCommand); 

The reason is that you can now pass arguments to the constructor :-)
BTW, I went around the limitation of the MockObject method. :wink:

Just out of interest how did you do this?

Always happy to help
answered by scott (32k points)
0 votes
Hi Scott,

SqlCommand mockSqlCommand = new SqlCommand();


In my opinion, the only mock this line has is the variable name.
But, I understand that you can add
Mock realMockSqlCommand = MockManager.Mock(typeof(SqlCommand))


This works fine and I admit that I didn't think about it.
My solution was to generalize my code so that I could use only interfaces (IdbConnection, IdbCommand).

Here is some of the code:

// test Init code
MockManager.Init();

m_MockConnection = MockManager.MockObject(typeof(IDbConnection));
m_MockConnection.ExpectCall("Open");
m_MockConnection.ExpectGet("State",ConnectionState.Open);

Mock factory = MockManager.Mock(typeof(DatabaseProviderFactory));
factory.ExpectAndReturn("CreateConnection",m_MockConnection.Object);

...
// test code

MockObject mockReader = MockManager.MockObject(typeof(IDataReader));
mockReader.ExpectAndReturn("Read",true);
mockReader.ExpectAndReturn("GetString","data",4);
         
mockReader.ExpectAndReturn("Read",false);
mockReader.ExpectCall("Close");

MockObject mockCommand = MockManager.MockObject(typeof(IDbCommand));
IDbCommand command = (IDbCommand) mockCommand.Object;
mockCommand.ExpectSet("CommandText");
mockCommand.ExpectAndReturn("ExecuteReader",mockReader.Object);

m_MockConnection.ExpectAndReturn("CreateCommand",command);


The benefit of using the interface is that my test fixture work for any type of database now not only SQL Server. I used a factory class to create the database specific connection (see init code above).

Anywayz, that was my "Workaround" that lead me to a better design of my code.
A happy ending for me. :D

BTW, It is "day and night" for me with version 2.2.
This database test fixure failed at the first lines with version 2.1.
Keep up the good work.


Best Regards,

Benjamin
answered by benjamin dover (1.8k points)
0 votes
Hi,
Thanks for the example.
Glad to hear that your tests made your design better. :D
We also saw how get a mocked instance of a concrete class.
answered by scott (32k points)
0 votes
Hi,
With TypeMock.NET 2.3 you can now create a Mock Object from any type, and you can pass Constructor Arguments too.

for more information see the TypeMock.NET User Guide
answered by scott (32k points)
...