This is a migrated thread and some comments may be shown as answers.

Mocking SharePoint 2013 Client Side Object Model (CSOM)

9 Answers 728 Views
General Discussions
This is a migrated thread and some comments may be shown as answers.
David
Top achievements
Rank 1
David asked on 04 Mar 2014, 08:06 AM
I'm trying to mock a simple example of the SharePoint 2013 Client Side Object Model.  Here's the method from the SUT:
public IEnumerable<string>GetListTitles(){
ClientContext ctx = new ClientContext(SiteUrl);
ctx.Credentials = CredentialCache.DefaultCredentials;
    ListCollection lists = ctx.Web.Lists;
ctx.Load(lists);
         ctx.ExecuteQuery();
    foreach(List list in lists)
      {
         yield return list.Title;
      }
}

I need to be able to specify a collection of Titles.  I've tried everything I can think of but nothing has worked.  The latest code I've tried is:
var mockCtx = Mock.Create<ClientContext>(Constructor.Mocked);
            var mockWeb = Mock.Create<Web>();
            var mockLists = Mock.Create<ListCollection>();
            var mockList = Mock.Create<List>();
 
            
 
            Mock.Arrange( () => mockCtx.Web).Returns(mockWeb);
            Mock.Arrange( () => mockWeb.Lists).Returns(mockLists);
            Mock.Arrange( () => mockLists[Arg.AnyInt]).Returns(mockList);
            Mock.Arrange( () => mockList.Title)).Returnsa("fakeTitle");
            Mock.Arrange( () => mockCtx.Load<ListCollection>(mockLists)).DoNothing();
            Mock.Arrange( () => mockCtx.ExecuteQuery()).DoNothing();
             
             
    
    var actual = SUT.GetListTitlesByCSOM();
 
    Assert.AreEqual(expected, actual);

Any ideas?


Thanks,

Dave



9 Answers, 1 is accepted

Sort by
0
David
Top achievements
Rank 1
answered on 04 Mar 2014, 08:11 AM
Noticed a typo when I added the last code sample.  This line:
     Mock.Arrange( () => mockList.Title)).Returnsa("fakeTitle");
 Should read:
     Mock.Arrange( () => mockList.Title).Returns("fakeTitle");

This was just an error in the forum post - my code in VS doesn't have the typo.  :-)
0
James
Top achievements
Rank 2
answered on 04 Mar 2014, 10:01 PM

Hi David,

I'm not anywhere close to being a Sharepoint developer, so I need to ask a few questions;

What is a "SUT"?
Are you trying to mock the SUT or test the SUT?
Are you using JustMock Lite or the commercial version of JustMock?
Is ClientContext a class you defined or part of the Sharepoint objet

Thanks,
James Bender
0
David
Top achievements
Rank 1
answered on 04 Mar 2014, 10:09 PM
Hi James,

SUT is just the class I'm testing.  It's a custom class I developed that for now just contains the "GetListTitles" method at the top of my post.  I'm trying to test the GetListTitles method inside it.  GetListTitles uses the .NET SharePoint Client Side Object Model (.NET CSOM) to enable calls back to SharePoint from off-server.  ClientContext is a class inside CSOM that manages the connection back to SP.

I'm successfully able to mock ClientContext.  The problem I'm having is that when GetTitles calls ctx.Web.Lists, I need to have my mocked ListCollection returned so that I can control the contents of the collection.  I'm mocking the ListCollection in my test but I can't get it to be the return value for the Lists property of my mocked Web.

Does that make any sense?

Thanks,
Dave


0
David
Top achievements
Rank 1
answered on 04 Mar 2014, 10:10 PM
Sorry...missed one of your questions.  I'm using the full version of JustMock, not the Lite version.
0
James
Top achievements
Rank 2
answered on 04 Mar 2014, 10:24 PM
Hi David,

I can see that you are creating a mock of ClientContext and using the Constructor.Mocked argument. That will tell JustMock that you want to mock the constructor. To have the methods of the concrete class be replaced with the arrangements you need to use the IgnoreInstance method when you do the arrangement. 

Mock.Arrange(() => mockCtx.Web).Returns(mockWeb);

becomes

Mock.Arrange(() => mockCtx.Web).IgnoreInstance().Returns(mockWeb);

You can leave the Constructor.Mocked argument if you like, but based on this particular snippet of code I don't think you need it. Future mocking is part of JustMock commercial, so you need to make sure the profiler is enabled when you run the test. You can find more information about future mocking on the JustMock future mocking documentation page(http://www.telerik.com/help/justmock/advanced-usage-future-mocking.html).

This should work. If not please let me know we can dig a little deeper.

Thanks,
James Bender
0
David
Top achievements
Rank 1
answered on 04 Mar 2014, 10:39 PM
I've tried the IgnoreInstance on every one of my Arrange calls (one at a time and all together) and it didn't work.  If I have a mocked Web, how do I have the Lists property of that mocked Web return my mocked ListCollection instead of the real one?  I had thought this would do it:
Mock.Arrange( () => mockWeb.Lists).Returns(mockLists);

But it doesn't seem to be working.  

Or, maybe the problem is when the code in my GetListTitles method tries to iterate the mocked ListCollection something fails.  How can I tell if my mocked ListCollection is actually being returned?

I also tried mocking the GetEnumerator of the ListCollection but that failed miserably.

Thanks,

Dave
0
Accepted
Kaloyan
Telerik team
answered on 06 Mar 2014, 03:48 PM
Hi David,

In order to achieve this, you will need to use the JustMocks ReturnsCollection feature as shown here for example.

Here is the code that worked on my side:
var mockctx = Mock.Create<ClientContext>();
var mockWeb = Mock.Create<Web>();
var mockList = Mock.Create<List>();
 
// Arranges the constructor of every new instance of ClientContext.
Mock.Arrange(() => new ClientContext(Arg.AnyString)).DoNothing();
// Arranges every new call to ClientContext.Web to return our mockWeb.
Mock.Arrange(() => mockctx.Web).IgnoreInstance().Returns(mockWeb);
// Arranges mockWeb.Lists to return our fake collection.
Mock.Arrange(() => mockWeb.Lists).ReturnsCollection(new List<List>() {mockList});
// Arranges mockList.Title to return a fake title.
Mock.Arrange( () => mockList.Title).Returns("fakeTitle");
 
// Arranges every new call to ClientContext.Load no matter the arguments to do nothing.
Mock.Arrange(() => mockctx.Load(Arg.IsAny<ListCollection>())).IgnoreInstance().DoNothing();
// Arranges every new call to ClientContext.ExecuteQuery to do nothing.
Mock.Arrange(() => mockctx.ExecuteQuery()).IgnoreInstance().DoNothing();
Please, give it a try and let me know if there are further issues.

I hope it helps.

Regards,
Kaloyan
Telerik

DevCraft Q1'14 is here! Join the free online conference to see how this release solves your top-5 .NET challenges. Reserve your seat now!

0
David
Top achievements
Rank 1
answered on 11 Mar 2014, 07:09 PM
After some excellent troubleshooting help from James, this is now working.  The first problem I had was the "yield return list.Title" in my foreach loop in the GetListTitles method.  JustMock did not like that at all - it prevented me from being able to step into the code in debug mode and see what was going on.  Once we fixed that by removing the yield return and instead building up a collection object in the foreach and simply returning it after the foreach was over things started looking up.

The final test code looked like this:
//Arrange
            var mockCtx = Mock.Create<ClientContext>(Constructor.Mocked);
            var mockWeb = Mock.Create<Web>();
            var mockLists = Mock.Create<ListCollection>();
 
            Mock.Arrange( () => mockLists.GetEnumerator()).IgnoreInstance().Returns(getEnum());
            Mock.Arrange( () => mockCtx.Web).IgnoreInstance().Returns(mockWeb);
            Mock.Arrange( () => mockWeb.Lists).IgnoreInstance().Returns(mockLists);
            Mock.Arrange( () => mockCtx.Load<ListCollection>(mockLists)).IgnoreInstance().DoNothing();
            Mock.Arrange( () => mockCtx.ExecuteQuery()).IgnoreInstance().DoNothing();
             
            //Act
            var actual = stuff.GetListTitlesByCSOM();
 
           //Assert
             Assert.IsNotNull(actual);
 
             Assert.AreEqual<int>(expected.Count(), actual.Count());
             for (int i = 0; i < expected.Count()-1; i++)
             {
                 Assert.AreEqual(expected.ElementAt(i), actual.ElementAt(i));
             }


Thanks again for the help!
Dave



0
Martin
Telerik team
answered on 14 Mar 2014, 03:01 PM
Hello David,

We are glad that your tests are working. If you need any further assistance, do not hesitate to write us again!

Regards,
Martin
Telerik
 

DevCraft Q1'14 is here! Watch the online conference to see how this release solves your top-5 .NET challenges. Watch on demand now.

 
Tags
General Discussions
Asked by
David
Top achievements
Rank 1
Answers by
David
Top achievements
Rank 1
James
Top achievements
Rank 2
Kaloyan
Telerik team
Martin
Telerik team
Share this question
or