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

I just spent a couple of hours fighting with the following problem. Began to write a question, and (as often) solved the problem just before hitting "Submit". So, I figured I'll save you some trouble.

I'm testing a VB WinForms application. The IDE generates some stuff, and I have a very convenient feature that I can call all my forms by their class names, i.e., if I have a frmClientCompanies class, I can write

Dim SelectedCompany As Support.ClientCompany = frmClientCompanies.SelectClient(1)


In short, we have a Singleton pattern here.

Here's my test code:
   <Test(), Repeat(2)> Public Sub TestCancelIncome()
      TypeMock.MockManager.Init()
      Dim ClientCompaniesMock = TypeMock.MockManager.Mock(GetType(KapList.frmClientCompanies))
      ClientCompaniesMock.ExpectAndReturn("SelectClient", Nothing).Args(1)
      Controller.CreateOperation(1)
      MockManager.Verify()
   End Sub


Note the Repeat attribute (I'm using Zanebug). If I repeat the test once, it runs fine, but the second time throws an exception:


         Iteration: 1
            +TestCancelIncome - Fail
               Thread Id: 4428
               Time: 3,73893609383315
         Iteration: 2
            +TestCancelIncome - Fail
               Thread Id: 4428
               Time: 0,620726377235095

               Result: 
               -----------------------------
               Object reference not set to an instance of an object.
               

               StackTrace: 
               -----------------------------
                  at KapList.frmClientCompanies.SelectClient(Int32 ObjectCategoryId) 
                  at KapList.Controller.CreateOperation(Int32 OperationTypeId)
                  at Tests.ControllerTester.TestCancelIncome()


So, obviously it doesn't mock some frmClientCompanies instance.

Further investigation (Trace) shows that there is some unmocked instance indeed. That instance does not exist before we call Controller.CreateOperation() on the second test (although we have already set the expectation).

I understand that after I called Verify() on the first test, all my calls to frmClientCompanies became unmocked, so even when I try to mock it again, it uses the real object, which is improperly initialized and thus throws an exception.

Of course, I could throw away the VB feature and do the C# way of properly creating and destroying all my forms. But what I like about TypeMock is that you don't have to change your production code in order to make it testable.

So, the answer is simple. Destroy your form manually before calling Verify()

Here's the final test code:
   <Test(), Repeat(2)> Public Sub TestCancelIncome()
      TypeMock.MockManager.Init()
      Dim ClientCompaniesMock = TypeMock.MockManager.Mock(GetType(KapList.frmClientCompanies))
      ClientCompaniesMock.ExpectAndReturn("SelectClient", Nothing).Args(1)
      Controller.CreateOperation(1)
      ClientCompaniesMock.ExpectAlways("Dispose")
      Controller.Destroy()
      MockManager.Verify()
   End Sub


Controller:
   Public Sub Destroy()
      My.Forms.frmClientCompanies = Nothing
   End Sub


However, it is still some mystery for me why it works this way. How comes an unmocked instance was being created after I mocked the class in the second iteration (this is buried in the autogenerated VS code I guess)? How happens that calling a Dispose method (which is mocked) fixes the situation?

In general, if I run an unexpected method on a mocked instance, what do I get?

ulu
asked by ulu (1.7k points)

4 Answers

0 votes
Thanks for the detailed explaination.
This will be very helpful to other TypeMock users.

:?: About your question.
In general, if I run an unexpected method on a mocked instance, what do I get?

:arrow: When the mock is Strict the test will fail, otherwise it will run the original method.
Just to make things clear, the default is that mocks of concrete classes are NOT strict and mocks of interfaces are strict.

However, it is still some mystery for me why it works this way. How comes an unmocked instance was being created after I mocked the class in the second iteration

:arrow: This is because TypeMock.MockManager.Mock mocks the next instance of the Type.
So ClientCompanies1 will be mocked in the following code:
Dim ClientCompaniesMock1 = TypeMock.MockManager.Mock(GetType(KapList.frmClientCompanies))
Dim ClientCompanies1 = New KapList.frmClientCompanies

but ClientCompanies1 will NOT be mocked in this code (it was created before MockManager.Mock)
Dim ClientCompanies1 = New KapList.frmClientCompanies
Dim ClientCompaniesMock1 = TypeMock.MockManager.Mock(GetType(KapList.frmClientCompanies))


This is what is happening in your test.
The first run we mock the next instance of KapList.frmClientCompanies and a new instance is created, so all is fine and danndy.
The second time we mock the next instance, but no new instance is created, the first instance is used.
As the first instance mock is already verified and has no expectations, The original method is called and this is why we fail.
This is also the reason why disposing the form works, because a new KapList.frmClientCompanies is created

:idea: Here is one solution: mock ALL instances (even instances that are already alive)
<Test(), Repeat(2)> Public Sub TestCancelIncome()
   TypeMock.MockManager.Init()
   Dim ClientCompaniesMock = TypeMock.MockManager.MockAll(GetType(KapList.frmClientCompanies))
   ClientCompaniesMock.ExpectAndReturn("SelectClient", Nothing).Args(1)
   Controller.CreateOperation(1)
   MockManager.Verify()
End Sub


B.T.W, do you think that we should support another behavior? for example if an object was once mocked and verifed should TypeMock fail the test if a method id called on the object?
answered by scott (32k points)
0 votes
B.T.W, do you think that we should support another behavior? for example if an object was once mocked and verifed should TypeMock fail the test if a method is called on the object?


No, I don't think so. The current behavior is quite logical, when you think about it. However, perhaps you could discuss this in the help.

This situation typically occurs if we have a mocked object referenced by a static variable that (object) hasn't been garbage collected between the tests (since we typically Verify at the end of the test). Sort of rare situation, too rare to change the framework, but nevertheless deserves its solution to be posted somewhere.

On the other hand, what you propose looks also logical. I can't see why anybody would want to call unmocked methods on a mocked object anyway, even more after Verify() has been called.
answered by ulu (1.7k points)
0 votes
Well, I hope that you managed to solve the problem
answered by scott (32k points)
0 votes
Yes, thank you.

And thank you for the great product.
answered by ulu (1.7k points)
...