-
Notifications
You must be signed in to change notification settings - Fork 382
Please allow us to mock the Client used by _withClient #64
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
Comments
This is why the If you really want to use global state, you can write |
The problem is we're trying to mock things out for libraries that we didn't write and don't yet know exist that use the top-level API. Let me phrase the question a different way: How are we supposed to test code that uses the top-level methods from the http package? |
If you want to unit-test code like that, it should probably be re-written. Again, those methods are intended for quick-and-dirty scripts, and designed to be easy to swap out for explicit client calls for more advanced or robust use-cases. |
It would be really easy to make these APIs testable. What harm is there in allowing them to be tested? |
It adds API surface and makes it harder to reason locally about code, and it encourages users to continue using the quick-and-dirty global APIs rather than switching to the more-explicit, more-efficient |
Could we have a way to disable the inefficient API? Currently, the |
It sounds like you want to ensure that all code you call goes through a particular HTTP API. I understand why that would be useful, but it's kind of at odds with the broader philosophy of encapsulation. Dart's language design and existing APIs generally operate on the philosophy that a chunk of code is customizable only in the ways it explicitly makes itself customizable. The move towards sealed classes and members in Dart 2 reinforces this further. This philosophy can definitely be inconvenient when you want to modify code in ways that its authors don't expect, like overriding its HTTP client, but you get a lot in exchange. The API surface of a package is vastly smaller when monkey-patching isn't allowed, which makes it easier to avoid breaking changes and ends up making the whole ecosystem much more stable. As an example, a package might switch from the global The good news is, if and when the author decides they want to move to a more customizable API, the |
What I want to ensure is that if someone decides to use Flutter and Dart official packages, and then does the obvious thing with them, that they end up with efficient and testable code. Currently, for Flutter, with our fork of the http package, this is possible (modulo the efficiency aspect, apparently), because the easiest APIs for doing HTTP traffic are testable. We want to stop forking the http package. Unfortunately, doing so today means a regression in our usability, because as it stands today, using Flutter and this official package and then doing the obvious thing with it means you end up with untestable code. We could solve this in several ways:
I don't really mind how we solve the problem. Currently there is a very attractive API in the http package, and yet this API is not testable (and apparently is less efficient). I do not believe making it testable is what would make it attractive. It is already attractive. |
I think I may be operating on a broader understanding of "testable". Adding tests for a chunk of code always involves some amount of work—the tests need to be written, and when they uncover bugs those bugs need to be fixed. When using That said, it definitely seems that we should be clearer in our messaging about the downsides of the global functions, and the migration path to an explicit |
By "testable" I mean "if the code was written without bugs, according to the documentation, in the most obvious and convenient way, then adding tests will not require any changes to the code". |
From a concrete user-experience perspective, though, the difference between making a small modification to the base code and making no modification is very small relative to the effort of writing tests in the first place—especially since most untested code does in practice have bugs, so the base code will probably need to change anyway. |
The difference is that if the API isn't testable, then I don't want anyone on the team using it, even by mistake, even if they don't write tests for it, because we know for a fact that the code cannot be correct -- it will definitely, at some point, have to change (when we test it). So either the analyzer should report it ( |
Maybe I misunderstand something, but I'm on @nex3's side on this one. Imagine asking to mock At least for UI frameworks, I find dependency injection useful here: class WidgetThatNeedsHttp {
final Client _http;
WidgetThatNeedsHttp(this._http);
} Instead of hoping that global functions are mockable. |
I'm not sure what it would mean to mock DateTime as a whole. Certainly the fact that you can't test code that uses The existence of other poorly designed APIs in the Dart ecosystem is not a good justification for following that pattern here, IMHO, especially given how easy it would be to fix. |
That's a policy that you're welcome to apply to your team. You could probably even automate it by writing something like: // flutter/http.dart
import "package:http/http.dart";
export "package:http/http.dart"
hide delete, get, head, patch, post, put, read, readBytes;
var client = new Client(); and grepping for any files that import That's not a policy that I want to bake into the entire ecosystem, though. Global functions are never mockable in Dart unless they explicitly specify otherwise, which indicates that most people using them in I'm very interested in giving users the information they need to make an informed decision about which API to use, and in making the migration path from the simple case to the powerful case as smooth as possible. But I'm not interested in making the simple case more complex, or in getting rid of it entirely. |
That's exactly what we do for |
Any chance these issues with testability will get addressed with the set of breaking changes for Dart 2.0? |
This is a pretty old issue, but maybe there are new ideas and views on how to handle this now. I understand the reasoning behind @nex3 not wanting to make the changes @Hixie was looking for, but at the same time I share @Hixie 's desire for a better solution. I think that it would make sense to at least mention this issue in the README and discourage people from using the "helper" functions directly and recommend the best practice of accepting a (mockable) |
Just got bit by this. Completely ported an internal http API class to use Being able to insert a mock client into the http package that covers the top-level function would be the perfect solution for me. I will now look into hacky solutions instead, which can't be the wanted outcome. |
You can use |
We would like to be able to inject a client into the http package (e.g. the MockClient obtained from http/testing) such that the top-level methods such as "get" and "head" and so forth will use the mock client instead of a brand new Client.
We had previously implemented this by forking
http
and changing it as follows:https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/http/client.dart#L32
We would like to just use the
http
package directly now, but without this way to inject a MockClient, we have the problem that anyone using the convenience methods likehttp.get()
will end up doing actual network traffic in our tests instead of hitting our mock.The text was updated successfully, but these errors were encountered: