Welcome to Typemock Answers. Here you can ask and receive answers from other community members. And if you liked or disliked an answer or thread: react with an up- or downvote Enjoy!

Mocking an Abstract Collection with an IEnumerator

+1 vote
I recently started evaluating TypeMock for my company and am having the following problem.


I have a collection class:
public abstract class MyAbstractCollection: IList
{
  private ArrayList list = new ArrayList();

  public void FillData()
  {
      //fill collection
      //...
  }
  protected internal ArrayList List
  {
      get
      {
          return _list;
      }
  }
  IEnumerator IEnumerable.GetEnumerator()
  {
       return _list.GetEnumerator();
  }
  //other methods
  // ...
}


Then multiple collection classes that inherit off of that
This one holds multiple MyObject's
public class MyCollection: TtgBpoCollection
{
   public MyCollection()
   {
   }

   public MyObject AddNew()
   {
      MyObject obj = new MyObject();
      List.Aadd(obj);

      return obj;
   }
}

public class MyObject
{
   private int i = 0;

   public MyObject()
   {
   }

   public int Value
   {
      get
      {
         return i;
      }
      set
      {
         i = value;
      }
   }
}


Then in my code i'm looping through the collection in my MainClass
private int Sum()
{
   MyCollection collection = new MyCollection();
   int sum = 0;

   collection.FillData();

   foreach(MyObject obj in collection)
   {
       sum = sum + obj.Value;
   }

  return sum;
}


Now in my nMock test I want to test this function by mocking collection
[Test]
public void N01_TestSum()
{
  MainClass main = new MainClass();
  MyCollection collection = new MyCollection();

  //fill collection with test data
  //...

  Mock collectionMock = MockManager.Mock(typeof(MyCollection));
  collectionMock .ExpectAndReturn("GetEnumerable", CreateEnumerator(collectionMock), null);

  int returnedValue = main.Sum();
  Assert.AreEqual(5, returnedValue, "Incorrect sum");
}

public IEnumerator CreateEnumerator(MyCollection collection)
{
   ArrayList list = new ArrayList();

  foreach(MyObject obj in collection)
  {
     list.Add(obj);
  }

  return list.GetEnumerator();
}


I always get the error:
MyTest.N01_TestSum : TypeMock.TypeMockException :
*** No method GetEnumerable in type MyCollection returns System.Collections.ArrayList+ArrayListEnumeratorSimple

I've simplified everything out but this gets the main idea across. I apologize for any syntax mistakes that may be in there. I'm running the latest version of TypeMock with nUnit and VS 2003.

Am I missing something or is there another way to mock a collection that is being used in a foreach loop?

Thanks!
asked Dec 20, 2006 by RSylvester (550 points)

17 Answers

+1 vote
Hi
The problem is the MockManager.Mock will mock the next instance of MyCollection. You can read about it here

So your code should be:

        public void N01_TestSum()
        {
            MainClass main = new MainClass();
            //will not work!
            //MyCollection collection = new MyCollection();

            //fill collection with test data
            //...

            Mock collectionMock = MockManager.Mock(typeof(MyCollection));
            collectionMock.ExpectAndReturn("GetEnumerable", GetEnumerator(collectionMock), null);
            //This will do the job!
            MyCollection collection = new MyCollection();

            int returnedValue = main.Sum();
            Assert.AreEqual(5, returnedValue, "Incorrect sum");
        } 

answered Dec 21, 2006 by ohad (37,080 points)
+1 vote
Hi Ohad,
The problem with doing it your way is that I then mock the MyCollection after "//This will do the job! ", which is not what I want. I want to mock the MyCollection inside MainClass and be able to use it in a foreach loop.

The reason I create collection at the top was so that i can fill that collection with data and use that when I mock MyCollection inside of MainClass.
answered Dec 21, 2006 by RSylvester (550 points)
+1 vote
Hi
It seems to me that you replaced 'GetEnumerator' with 'GetEnumerable' and collectionMock instead of collection
So instead of:
collectionMock .ExpectAndReturn("GetEnumerable", GetEnumerator(collectionMock), null);

It should be:
collectionMock .ExpectAndReturn("GetEnumerator", GetEnumerator(collection), null); 


This should fix the error message.
Another thing is that you probably will have to change the mock statement not to mock constructor:

Mock collectionMock = MockManager.Mock(typeof(MyCollection), Constructor.NotMocked); 


Hope I understand you this time :oops:
answered Dec 21, 2006 by ohad (37,080 points)
+1 vote
Hi,
You are correct I did messup using 'GetEnumerable' instead of 'GetEnumerator'. So I fixed that in my code. I also changed my first post to have CreateEnumerator in the test class so that there weren't two 'GetEnumerator''s causing confusion. Using collectionMock instead of collection was just a typeo in the post.

So in summary I made all the changes you suggested but I'm still getting the same error message.

Why do you think I'll want the constructor to not be mocked?
answered Dec 21, 2006 by RSylvester (550 points)
+1 vote
Hi
Why do you think I'll want the constructor to not be mocked?

Since I can not compile your example I'm guessing here.
If the constructor is mocked MyCollection ArrayList would not be initialize and you will get a null exception when calling FillData from Sum.

I made all the changes you suggested but I'm still getting the same error message.

I reproduced your problem and changing GetEnumerator to GetEnumerable fixed that.
This has no connection to the Mocked constructor issue since the error you are talking about happens before your mocked instance is created.

Can you please post your test method as it is now?
answered Dec 21, 2006 by ohad (37,080 points)
+1 vote
Hi,
Oh okay, that makes since. I thought maybe there was just something else I didn't understand. I added an ExpectCall("FillData", null) to handle that now.

I reproduced your problem and changing GetEnumerator to GetEnumerable fixed that.

Do you mean GetEnumerable to GetEnumerator? Because in your previous post you said that GetEnumerable should be GetEnumerator.

Here's my new test code:
[Test] 
public void N01_TestSum() 
{ 
  MainClass main = new MainClass(); 
  MyCollection collection = new MyCollection(); 

  //fill collection with test data 
  //... 

  Mock collectionMock = MockManager.Mock(typeof(MyCollection)); 
  collectionMock.ExpectAndReturn("GetEnumerator", CreateEnumerator(collection), null); 
  collectionMock.ExpectCall("FillData", null);

  int returnedValue = main.Sum(); 
  Assert.AreEqual(5, returnedValue, "Incorrect sum"); 
} 

public IEnumerator CreateEnumerator(MyCollection collection) 
{ 
   ArrayList list = new ArrayList(); 

  foreach(MyObject obj in collection) 
  { 
     list.Add(obj); 
  } 

  return list.GetEnumerator(); 
}
answered Dec 21, 2006 by RSylvester (550 points)
+1 vote
Do you mean GetEnumerable to GetEnumerator?

Oops my mistake it should be GetEnumerator.
I will get back to you about the new test.
answered Dec 21, 2006 by ohad (37,080 points)
+1 vote
Hi
Your new test run perfectly well on my machine.
There are two options to solve this:
1. Can you create a stand alone example that I can compile run and produce the error?
2. If option 1 is not possible can you give me more details about your code?
(TtgBpoCollection class for example)
answered Dec 21, 2006 by ohad (37,080 points)
+1 vote
I created my example in a seperate solution and was able to reproduce the error. It looks like the key to making it break is:
IEnumerator IEnumerable.GetEnumerator() 
{ 
     return _list.GetEnumerator(); 
}

by default when you use auto-complete IList has it as public so you have to change it to be exactly like I have it above. I'm not sure what kind of protection level(public, protected, private) it has when you don't specify anything but that's how our code is doing it.

If you still want a complete solution let me know how you want me to send it to you.

Thanks!
answered Dec 21, 2006 by RSylvester (550 points)
+1 vote
Hi
You are right. I reproduced the problem.
:!:
This syntax came to solve the problem of two (or more) interfaces with the same method signature.
In this case one of the implemented methods should be declared with a syntax of 'InterfaceName.Method()' the other should be declared as usual.
The method that is implemented with InterfaceName.Method() does not belong to your class (MyAbstractCollection)
but to System.Collections.IEnumerable.GetEnumerator.
(As you can see that in the reflector)
answered Dec 21, 2006 by ohad (37,080 points)
...