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
I have the following code, that is trying to mock a certain MS Sharepoint object:

using Microsoft.SharePoint.Workflow;
using NUnit.Framework;
using TypeMock;
[Test]
public void Test()
{
MockManager.Init();
MockObject spWFAssocs = MockManager.MockObject(typeof(SPWorkflowAssociationCollection));
spWFAssocs.ExpectGetAlways("get_Count", 0);
MockManager.Verify();
}

When I execute this test code I get this error:


failed: System.TypeLoadException : Method 'Add' on type 'MockSPWorkflowAssociationCollection' from assembly 'DynamicMockAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' is overriding a method that is not visible from that assembly.
at System.Reflection.Emit.TypeBuilder.TermCreateClass(Int32 handle, Module module)
at System.Reflection.Emit.TypeBuilder.CreateTypeNoLock()
at System.Reflection.Emit.TypeBuilder.CreateType()
at u.a(Type A_0, Object[] A_1)
at TypeMock.MockManager.MockObject(Type type, Constructor mockConstructors, Object[] args)
at TypeMock.MockManager.MockObject(Type type, Object[] args)


Any inside to this error, or any workarounds? I quickly tried using Mock instead of MockObject, it didn't give the error, but I may need to use MockObject to be able to mock different instance differently. I have TypeMock 3.7.1 installed currently.

TIA
asked by phazer (4k points)

10 Answers

0 votes
I have been able to isolate and replicate the problem, the cause is that the class that is being mocked is abstract, and has an abstract method which "internal", not visible to outside.

I compiled this simple class in a separate assembly:

public abstract class Class1
{
private int _m;
protected Class1()
{
_m = 55;
}
internal abstract void InternalAbstractMethod();
public int ReturnInt()
{
return _m;
}
}

When I tried to mock it from another assembly, with the following code:

MockObject mockObj = MockManager.MockObject(typeof (Class1));

I get the same failure error when the code is executed, as I mentioned in the above post.

So in short, mocking abstract classes that have internal abstract methods is causing failure. Is this a known issue? Is there any workarounds?

Thanks
answered by phazer (4k points)
0 votes
Hi,
Thanks for the detailed report. Currently .NET doesn't allow extending internal abstracts unless you add the [InternalsVisibleTo] directive.
[assembly: InternalsVisibleTo("DynamicMockAssembly")]

See Mocking Interfaces and Abstract classes
answered by scott (32k points)
0 votes
Well, in case of SharePoint dll, I can't add the internalsvisible, since it is compiled already. Is there any way to workaround this?
answered by phazer (4k points)
0 votes
As the method is internal, there must be other types in the SharePoint dll that extend SPWorkflowAssociationCollection, you can mock these instead.
I will also check to see if there is another way to do this.
answered by scott (32k points)
0 votes
Though I'm working with AAA, I'm encountering the same problem as the original poster: But for the life of me, I can't figure out a workaround. Has anyone managed to find a way to mock a SPWorkflowAssociationCollection or one of it's extending classes (SPListWorkflowAssociationCollection)???
answered by sean.mcdermid@colost (720 points)
0 votes
You might want to check the duck-type swapping:
You'll need an actual instance of SPWorkflowAssociationCollection and create a similar class with the required functionality.
You can read about it at:
https://www.typemock.com/Docs/UserGuide/ ... peAAA.html
answered by dhelper (11.9k points)
0 votes
That looks like it would work, except the part where I need an actual instance of a class that extends from SPWorkflowAssociationCollection (public abstract, with a protected constructor, and a number of protected abstract methods).

Since the out-of-the-box classes that extend from SPWorkflowAssociationCollection are all scoped as "protected", I can't think of a way to implement one of those (please tell me if I'm wrong!).

I thought I could swap instances of SPWorkflowAssociationCollection with a monstrosity of my own creation using Isolate.Swap.NextInstance<T>, but that fell short. Maybe obviously to some, I don't seem to be able to implement protected abstract methods (again, I worry my own coding ignorance shows through here).

My final thought on how to implement your suggestion is to instantiate a reference to an actual SharePoint site and retrieve a "live" instance of a SPWorkflowAssociationCollection; however, that kind of defeats the purpose of Test Driven Development doesn't it? The tests then MUST be run on a machine with SharePoint installed.

I understand that sometimes you have to do what you have to do, but I had hopes that SharePoint would allow for an elegant solution in one way shape or form. High hopes seemingly dashed on the harsh rocks of coding reality.
answered by sean.mcdermid@colost (720 points)
0 votes
Hi Sean,

I would really like to help you solve this issue.
Before throwing TDD away I would suggest we work together to find a way to work around this limitation, can you provide a code snippet (or your whole test) of what you are trying to accomplish so I can suggest how you can still test the desired feature without faking SPWorkflowAssociationCollection
answered by dhelper (11.9k points)
0 votes
After digging a bit I found a solution for Mocking/Faking SPWorkflowAssociationCollection:

We can use the old API (reflective & natural) to mock one of the private classes that inherit from SPWorkflowAssociationCollection.


var assembly = typeof(SPWorkflowAssociationCollection).Assembly;
Type t = assembly.GetType("Microsoft.SharePoint.Workflow.SPListWorkflowAssociationCollection");

var mockSPList = MockManager.MockObject(typeof(SPList));
var mock = MockManager.MockObject(t, mockSPList.MockedInstance);

var fakeInstance = mock.MockedInstance as SPWorkflowAssociationCollection;

Assert.IsNotNull(fakeInstance);

The bad news is that faking private/internal is currently not (yet) implemented in AAA and you'll have to use older API in your test.
answered by dhelper (11.9k points)
0 votes
Thanks for taking the time on this.

I did do a workaround that was not the least bit elegant: I wrapped the SPList.WorkflowAssociations.GetAssociationByName() call in it's own method and simply mocked the wrapping method. Not in the least bit pretty, but it worked. I'll definitely give your suggestion a try.

Here's the code for the hack (which is just the offending call moved to it's own method):


private static SPWorkflowAssociation GetWorkflowAssociation(string associatedWorkflowName, SPList list)
      {
         return list.WorkflowAssociations.GetAssociationByName(associatedWorkflowName, CultureInfo.CurrentCulture);
      }


Here is the code for the test, with my original efforts commented out:

//Isolate.WhenCalled(() => fakeTargetList.WorkflowAssociations.GetAssociationByName(associatedWorkflowName, CultureInfo.CurrentCulture)).WillReturn(fakeWorkflowAssociation);
         
Isolate.NonPublic.WhenCalled(typeof(ListToolKit), "GetWorkflowAssociation").WillReturn(fakeWorkflowAssociation);
answered by sean.mcdermid@colost (720 points)
...