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
Quite frequently when testing Sharepoint code i need to return different mocks based on ctor parameters (their values). Swap.Nextinstance is very brittle and sometimes impossible to use at all (if tested code spans background thread).

I would like to have possibility to create mocks and instruct Isolator to return different ones based on ctor params. More details on topic: https://www.typemock.com/community/viewtopic.php?t=1654
asked by damiand2 (2k points)

8 Answers

0 votes
Hi,

Can you send us an example of what you are trying to do?
We think we might have a solution within the frame of the current API.
You can send the example to our support mail:
support at typemock.com
answered by ohad (35.5k points)
0 votes
I would like to be able to do:

var fake1 = Isolate.Fake.Instance<SPSite>();
var fake2 = Isolate.Fake.Instance<SPSite>();

Isolate.WhenCalled(()=> fake1.OpenWeb()).WillReturn(something);
Isolate.WhenCalled(()=> fake2.OpenWeb()).WillReturn(something different);

Isolate.Swap.WithCtorArguments<SPSite>(url = "someurl1").With(fake1);
Isolate.Swap.WithCtorArguments<SPSite>(url = "url2").With(fake2);

Of course API can be different, it does not really matter.
answered by damiand2 (2k points)
0 votes
Hi
Currently you can do that by using sequences (i.e created the fakes in the order they are created in the code) like I explained in the post you linked to.
answered by ohad (35.5k points)
0 votes
Do you mean this?

Isolate.Swap.NextInstance<SPSite>().With(fake1);
Isolate.Swap.NextInstance<SPSite>().With(fake2);


I cannot rely on such code when code under test is spawning threads that create SPSites objects. I have to have reliable way of returning proper mocks for specific ctor args.
answered by damiand2 (2k points)
0 votes
Hi,

A workaround to this can be a refactor to factory method. It requires minor change in the production code but it will solve the fakes matching easily.

After this refactor, we can handle the fake matching using DoInstead():

[TestMethod, Isolated]
public void ChooseFakeByArguments()
{            
    var fakeA = Isolate.Fake.Instance<SPSite>();
    var fakeB = Isolate.Fake.Instance<SPSite>();

    Isolate.WhenCalled(() => Factory.CreateSPSite(null)).DoInstead(context =>
        {
            string url = (string)context.Parameters[0];
            if(url == "url1")
            {
                return fakeA;
            }
            else
            {
                return fakeB;
            }
        }
}


Regards,
Elisha,
Typemock Support
answered by Elisha (12k points)
0 votes
I can fake out methods outside of Microsoft.Sharepoint namespace if i have license for sharepoint isolator? I'm afraid not. Maybe there are some other workarounds?
answered by damiand2 (2k points)
0 votes
This is indeed a limitation with Isolator for SharePoint. Regarding the original feature request, it's a missing feature in our backlog.

It's considered a bit of a smell to rely on implementation details (c'tor arguments in your case) so much when writing tests, as the resulting tests are pretty fragile. Can you send over a more complete example of the code you are trying to test so we can think of a better way to test this?

Thanks,
Doron
Typemock Support
answered by doron (16.5k points)
0 votes
Hi,

We've found a workaround to this limitation:

The workaround is based on usage of SwapAll(), and map the behavior according to the instance being called at runtime. I'll try to show it using an example, where we fake all instances of SPSite and make their Url be based on the Guid used in their construction. It's a meaningless code in real life, but it should help understanding the concept.


[TestMethod, Isolated]
public void FakeBasedOnConcsturctorArgumentsExample()
{
    // A fake instance we'll use to intercept all SPSite instances in the code
    var fakeSPSite = Isolate.Fake.Instance<SPSite>();
    // A map between an instance of SPSite and the faked Url value
    var fakeUrlsMap = new Dictionary<SPSite>();
    // Set the behavior to return for each SPSite the faked url from map
    Isolate.WhenCalled(() => fakeSPSite.Url).DoInstead(ctx => fakeUrlsMap[(SPSite)ctx.Instance]);

    // Gets the fake controller to acheive constructor interception
    var fakeController = MockManager.GetMockOf(fakeSPSite);
    // Place a callback for every SPSite construvtor
    fakeController.MethodSettings(".ctor").MockMethodCalled += (sender, eventArgs) =>
        {
            // We assume in this example that only SPSites with GUID will be called
            // It is simple to adjust it to any overload by querying the SentArguments
            var guidUsedInConstructor = (Guid)eventArgs.SentArguments[0];
            // The sender is the actual instance of SPSite the code under test holds
            var instantiatedSite = (SPSite)sender;
            // We're setting the fake behavior based on the constructor arguments
            fakeUrlsMap[instantiatedSite] = guidUsedInConstructor + "Fake";
        };

    // Signals that every constructed SPSite behavior sould be swapped with our fake
    Isolate.Swap.AllInstances<SPSite>().With(fakeSPSite);

    // Shows that the Url is faked according to the Guid used in the constructor
    var firstGuid = Guid.NewGuid();
    var firstSPSite = new SPSite(firstGuid);
    Assert.AreEqual(firstGuid + "Fake", firstSPSite.Url);
    
    // Shows that the Url is faked according to the Guid used in the constructor
    var secondGuid = Guid.NewGuid();
    var secondSPSite = new SPSite(secondGuid);
    Assert.AreEqual(secondGuid + "Fake", secondSPSite.Url);
}


Let me know if it helps.

Regards,
Elisha,
Typemock Support
answered by Elisha (12k points)
...