Mocking SharePoint 2013 Client Side Object Model (CSOM)

10 posts, 1 answers
  1. David
    David avatar
    9 posts
    Member since:
    Aug 2011

    Posted 04 Mar 2014 Link to this post

    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



  2. David
    David avatar
    9 posts
    Member since:
    Aug 2011

    Posted 04 Mar 2014 in reply to David Link to this post

    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.  :-)
  3. DevCraft R3 2016 release webinar banner
  4. James
    James avatar
    2 posts
    Member since:
    Jul 2013

    Posted 04 Mar 2014 in reply to David Link to this post

    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
  5. David
    David avatar
    9 posts
    Member since:
    Aug 2011

    Posted 04 Mar 2014 in reply to James Link to this post

    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


  6. David
    David avatar
    9 posts
    Member since:
    Aug 2011

    Posted 04 Mar 2014 in reply to David Link to this post

    Sorry...missed one of your questions.  I'm using the full version of JustMock, not the Lite version.
  7. James
    James avatar
    2 posts
    Member since:
    Jul 2013

    Posted 04 Mar 2014 in reply to David Link to this post

    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
  8. David
    David avatar
    9 posts
    Member since:
    Aug 2011

    Posted 04 Mar 2014 in reply to James Link to this post

    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
  9. Answer
    Kaloyan
    Admin
    Kaloyan avatar
    872 posts

    Posted 06 Mar 2014 Link to this post

    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!

  10. David
    David avatar
    9 posts
    Member since:
    Aug 2011

    Posted 11 Mar 2014 in reply to Kaloyan Link to this post

    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



  11. Martin
    Admin
    Martin avatar
    29 posts

    Posted 14 Mar 2014 Link to this post

    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.

     
Back to Top
DevCraft R3 2016 release webinar banner