microsoft/commercial-marketplace-client-dotnet

Possibility to instantiate ResolvedSubscription in tests

piotr-consensus opened this issue · 0 comments

Hi Team!

Is your feature request related to a problem? Please describe.

We forked the Commercial-Marketplace-SaaS-Accelerator project which references commercial-marketplace-client-dotnet library. I'm in the process of adding some integration tests to our fork, where I'd like to mock out IMarketplaceSaaSClient because (as far as I understand, please correct me if I'm wrong) it is the component accessing the "real" Fulfillment API.

So basically I'm trying to mock out this line from FulfillmentApiService in Commercial-Marketplace-SaaS-Accelerator:

var resolvedSubscription = (await this.marketplaceClient.Fulfillment.ResolveAsync(marketPlaceAccessToken)).Value;

This proves difficult because of the IMarketplaceSaaSClient.Fulfillment property:

    public interface IMarketplaceSaaSClient
    {
        FulfillmentOperations Fulfillment { get; }

        SubscriptionOperations Operations { get; }
    }

It is of concrete class type and not an interface. That in itself is not a problem, I can set mock of IMarketplaceSaaSClient to return mock (so a sub-class) of FulfillmentOperations, which in turn returns a mock of Response<ResolvedSubscription> from FulfillmentOperations.ResolveAsync(...) method (wrapped in Task of course) thanks to the fact that this method is virtual.

What I cannot do is create a real instance or mock of ResolvedSubscription itself, as all of its constructors (including parameterless) are internal. Because of that I cannot set up the FulfillmentOperations.ResolveAsync(...) method to return anything in the mentioned line of code

var resolvedSubscription = (await this.marketplaceClient.Fulfillment.ResolveAsync(marketPlaceAccessToken)).Value;

Describe the solution you'd like
Currently the workaround would be to isolate entire class making use of IMarketplaceSaaSClient (so in the Accelerator project that's FulfillmentApiService, but that reduces code coverage as it skips code included in our fork, which we control now.
What I would prefer is ability to instantiate whatever gets returned from IFulfillmentOperations.ResolveAsync(...), so if e.g. that method's signature was changed from

public virtual async Task<Response<ResolvedSubscription>> ResolveAsync(
      string xMsMarketplaceToken,
      Guid? requestId = null,
      Guid? correlationId = null,
      CancellationToken cancellationToken = default (CancellationToken))

to

public virtual async Task<Response<IResolvedSubscription>> ResolveAsync(
      string xMsMarketplaceToken,
      Guid? requestId = null,
      Guid? correlationId = null,
      CancellationToken cancellationToken = default (CancellationToken))

i.e. ResolvedSubscritpion would get an interface extracted, and that interface would be used in return type of ResolveAsync instead.

Describe alternatives you've considered

Alternatively one of the constructors on ResolvedSubscription could be made public so I could instantiate this class in tests.

Additional context

As extra convenience, maybe it would be possible to change

    public interface IMarketplaceSaaSClient
    {
        FulfillmentOperations Fulfillment { get; }

        SubscriptionOperations Operations { get; }
    }

to

    public interface IMarketplaceSaaSClient
    {
        IFulfillmentOperations Fulfillment { get; }

        ISubscriptionOperations Operations { get; }
    }

seeing that both FulfillmentOperations and SubscriptionOperations already have those interfaces.

Thanks for your consideration, and please let me know if anything needs any extra info.