lostisland / faraday

Simple, but flexible HTTP client library, with support for multiple backends.
https://lostisland.github.io/faraday
MIT License
5.7k stars 971 forks source link

Test friction #1559

Open sammyhenningsson opened 2 months ago

sammyhenningsson commented 2 months ago

Basic Info

Issue description

I find it painful to test code using Faraday. I'm wondering if it would be possible to improve the testing experience. Or please give me suggestions if I can improve my usage of Faraday.

Typically will write clients that uses Faraday internally. For example:

class FooClient
  attr_reader :base_url

  def initialize(base_url: nil)
    @base_url = base_url || "https://example.com"
  end

  def foobar(search:)
    get('foobar', {search:})
  end

  private

  def get(path, params = nil, headers = nil)
    connection.get(path, params, headers).body
  end

  def connection
    headers = {'Accept' => 'application/json'}
    @connection ||= Faraday.new(url: base_url, headers:) do |f|
      f.adapter Rails.application.config.x.faraday.adapter # :test or :net_http depending on environment
    end
  end
end

I don't understand how I should stub requests that such a client makes. From the documentation, its says that stubbing should be done when initializing a Faraday::Connection. But (at least the way I write my clients), I don't have control over these internals and I would like a more "global" way of stubbing requests. Do you think there's any way this can be done/improved? Or do you have any suggestion for improving the way I design my clients?

iMacTia commented 2 months ago

Hi @sammyhenningsson, this is actually a great question! It would make for a really good discussion, but I think I might leave it as an issue to make it easier for people to find it.

From the documentation, its says that stubbing should be done when initializing a Faraday::Connection I'd love to know which documentation you're referring to here. My guess is you're referring to the test adapter docs. And you're right, the docs clearly state "This can be used to mock out network services in an application's unit tests".

However as you rightly pointed out, this isn't easy when you're developing a client, because that would require some sort of dependency injection, which would cause the test code to differ from the one used in production.

To be fair, I believe the test adapter is more suited to write tests for middleware, not for API clients like the one you're building. I also build lots of those and in my experience there are 2 main ways to write tests for these:

  1. Using Webmock (which supports Faraday out of the box); or
  2. Using VCR (which operates at a higher level)

Personally, I prefer the former for a variety of reasons, but I also know plenty of people who prefer the latter. Regardless of your preference, these are both external dependencies that you can use to write tests and are quite good at that, to the point that I don't think we need to introduce built-in tools in Faraday itself to mimic their features.

I should probably update the docs to formalise this advice, but interested in your feedback before I do that 🙂

sammyhenningsson commented 2 months ago

Thank you for this explanation! I guess webmock might be the best solution. (Not too big of a fan of VCR). I didn't realize that the test adapter was meant to be for middleware. But now I understand the reasoning for it. Thanks!