Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How can I mock/substitute/stub Shopifysharp services during unit tests ? #654

Closed
bilalyasin1616 opened this issue Jul 26, 2021 · 6 comments
Labels

Comments

@bilalyasin1616
Copy link

bilalyasin1616 commented Jul 26, 2021

I am trying to write unit tests for an application that uses Shopifysharp for managing API calls to Shopify stores.
I am wandering how I can do the unit tests without actually calling the Shopify API. I have been looking into mocking using some mocking frameworks like NSubstitue or MOQ but I have read in few articles that mocking external types is not the best idea.
Careful mocking types we don't own.
So I would like to know thoughts from someone in Shopifysharp's team that what will be the best approach for this. Thanks

@Zikoat
Copy link
Contributor

Zikoat commented Oct 29, 2021

These are just my experiences with trying to test an app.

ShopifySharp uses integration tests in their own test suite, see related issue #397. A nice change would be to expose the HttpClient ShopifySharp so we can inject the HttpClient or a HttpClientMessageHandler. That way, you would be able to return custom http responses in the HttpClientMessageHandler.

While the HttpClient is private, you could try using an api mocking framework such as WireMock.net to intercept the HTTP request/responses.

You could also wrap ShopifySharp in its own class, and then mock the interface so it returns a new shopify object (eg. Order). If you first create an integration test, you can serialize the response, save it, and then convert the integration tost to a unit test by returning the deserialized object.

@nozzlegear
Copy link
Owner

I'm currently working on adding an IHttpClientFactory to all ShopifySharp services, which would let you change the HttpClient and inject whatever responses you need for unit tests. Tracking in #369

@nozzlegear
Copy link
Owner

@bilalyasin1616 As for how to mock/substitute ShopifySharp, at the moment that's somewhat difficult because the package doesn't use interfaces for its API classes. That's something I want to add, it's just a lot of tedious copy/pasting of existing method signatures that I haven't had the time to do yet (tracking it here: #362).

In the meantime, you should be able to override any *Service class and make the methods return whatever you want for testing purposes. Then you'd be able to skip the calls to Shopify's API. Something like this:

public class MockOrderService : ShopifySharp.OrderService
{
    public MockOrderService() : base ("https://example.com", "fake access token")
    {
    
    }
    
    public override async Task<ShopifySharp.Order> GetAsync(long orderId, CancellationToken token = default)
    {
        // Skip the calls to Shopify's API and just return whatever you need for your tests
        return new ShopifySharp.Order
        {
            // ...
        };
    }
}

This still doesn't work very well for dependency injection, though, since all *Service classes are scoped to individual domains/access tokens.

@nozzlegear
Copy link
Owner

You can now use a global IHttpClientFactory and set instance-specific HttpClient with ShopifySharp v5.14.0 on Nuget. Let me know if you have any further questions!

@bilalyasin1616
Copy link
Author

@nozzlegear thanks, appreciate the help. For the time being I substituted the Shopifysharp services using NSubstitute and override the virtual functions to return dummy object. Will try it out soon.

@akashvekariya
Copy link

@bilalyasin1616 It seems you found a workaround to mock it using NSubstitute, we're using the same library and I am struggling with logic. Can you please give me some idea how you mocked it? Here's my function

public async Task<Response<CustomerList>> GetCustomerDetailsAsync(
        CustomerDetailsRequest customerDetailsRequest,
        AuthorizedRequest authorizedRequest)
    {
        var customerService = new CustomerService(
            authorizedRequest.Configurations?.InstanceUrl,
            authorizedRequest.Credentials?.AccessToken)

        var filter = new CustomerSearchListFilter
        {
            Query = QueryBuilder.GenerateQueryStringEmailPhone(customerDetailsRequest.EmailAddress,
                customerDetailsRequest.PhoneNumber)
        };

        var customerResponse = await customerService.SearchAsync(filter);
        return new CustomerList() {
               Customers = customerResponse.Items
        };
    }

I was trying to mock CustomerService Initially

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants