pnp / pnpcore

The PnP Core SDK is a modern .NET SDK designed to work for Microsoft 365. It provides a unified object model for working with SharePoint Online and Teams which is agnostic to the underlying API's being called
https://aka.ms/pnp/coresdk/docs
MIT License
299 stars 191 forks source link

Mocking PnPContext using Moq #948

Closed jansenbe closed 2 years ago

jansenbe commented 2 years ago

Discussed in https://github.com/pnp/pnpcore/discussions/789

Originally posted by **sachinsatav** March 14, 2022 Hi, I am using PnP.Core in one of my project to create new sharepoint sites and I need to write unit tests around it. I am able to mock context factory since there is an interface for it. `var _mockContextFactory = new Mock();` However I am not able to mock **PnPContext** since there is no public constructor. Also there is not interface which I can use for mocking. I looked at SDK test projects - https://github.com/pnp/pnpcore/tree/dev/src/sdk/PnP.Core.Test.Common & https://github.com/pnp/pnpcore/tree/dev/src/sdk/PnP.Core.Test but the setup is quite complicated and most of method calls used in there are only exposed to SDK solution projects. Can someone please help with how I can mock PnPContext? Thank you.
jansenbe commented 2 years ago

@sachinsatav, @wonderplayer : can you explain more about your requirements/needs. The mentioned discussion speaks about being able to set Web and Uri as properties...

wonderplayer commented 2 years ago

@jansenbe I'd like to set up unit testing. But if code contains PnP.Core framework in it, then I can't mock it - workaround to create class that will be calling PnP.Core and mock that class. So, in general, it would be perfect if I could mock any part of PnPContext - web functions, administration, list, files, etc.

jansenbe commented 2 years ago

@wonderplayer : can you show me a sample workaround class and sample test using that? Feel free to directly mail to bjansen@microsoft.com in case there's too much cleanup needed for public disclosure of the code.

wonderplayer commented 2 years ago

@jansenbe Will this do? https://github.com/wonderplayer/PnPCore-ConsoleApp

jansenbe commented 2 years ago

@wonderplayer : I've just pushed this change (https://github.com/pnp/pnpcore/commit/32dcbfb679ca93d39094e954765dbd0a3aad2e5c). Can you tomorrow evaluate and provide feedback. Let's work together to make PnP Core mockable using Moq.

wonderplayer commented 2 years ago

@jansenbe Sure, let's do it 😊 Created mocked context, but tests failing. It requires ILogger - guess if I add resolution dependency injection for it, then it might work, but not necessarily all solutions will be using ILogger. At least IPnPContextFactory does not require ILogger dependency.

jansenbe commented 2 years ago

@wonderplayer : can you share the stack trace of the failure?

wonderplayer commented 2 years ago

@jansenbe I've pushed my changes to that repo. New test file with mock PNP context.

jansenbe commented 2 years ago

@wonderplayer : I did play a bit with your code and experimented with adding an interface for PnPContext. Using that below code works...would such an approach work for you?

var ctx = new Mock<IPnPContext> { DefaultValue = DefaultValue.Mock };

IWeb web = ctx.Object.Web;

ctx.Setup(c => c.Web.GetAsync()).ReturnsAsync(web);
var webMock = Mock.Get(web);
webMock.Setup(p => p.Title).Returns(title);

var result = await web.GetAsync();

Assert.AreEqual(title, result.Title);

Interface added on PnPContext:

/// <summary>
/// PnPContext interface to support mocking
/// </summary>
public interface IPnPContext
{
    /// <summary>
    /// Entry point for the Web Object
    /// </summary>
    IWeb Web { get; }
}
jansenbe commented 2 years ago

Code is not yet committed, will extend the interface to support additional objects before commit

jansenbe commented 2 years ago

@wonderplayer : I pushed the interface change and dropped the static method. Let me know if this unblocks your scenario.

wonderplayer commented 2 years ago

Thanks! I'll check tomorrow

wonderplayer commented 2 years ago

@jansenbe with the scenario that I have, it works perfectly! Now I can mock various parts, as they are interfaces. Thank you!

jansenbe commented 2 years ago

closing, thx!