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