<div dir="ltr">High level thoughts:<div><ol><li>Using an interface{} instead of patching function pointers sounds like a great thing</li><li>Writing a provider via faking out rather than implementing a double means that it is really hard to run the cross-provider interface tests (environs/jujutest), which means we run into what we have with Azure, where *some* of the functions return a different error than the one we expect (see the recent 'juju restore' regression). Our interface tests aren't amazing, but if we can push more on that, then we can avoid having implementations leak through their abstractions.</li><li>I do realize that time-to-something-working is a lot faster with a Fake implementation, but ongoing support and maintenance might pay off more. (I love how quickly the GCE provider was available, I'm concerned if we know that it actually acts like EC2/Openstack when it comes to edge cases.)</li></ol><div>John</div></div><div>=:-></div><div><br></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Wed, Feb 11, 2015 at 10:53 PM, Eric Snow <span dir="ltr"><<a href="mailto:eric.snow@canonical.com" target="_blank">eric.snow@canonical.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">tl;dr Using fakes for testing works well so I wrote a base fake type. [1]<br>
<br>
While working on the GCE provider, Wayne and I started taking a<br>
different approach to unit testing than the usual 1. expose an<br>
external dependency as an unexported var; 2.export it in<br>
export_test.go; 3. patch it out in tests. [2] Instead we wrote an<br>
interface for the provider's low-level API and implemented the<br>
provider relative to that. [3] Then in tests we used a fake<br>
implementation of that interface instead of the concrete one. Instead<br>
of making the actual API requests, the fake simply tracked method<br>
calls and controlled return values.<br>
<br>
Using the fake had a number of benefits:<br>
<br>
1. our tests were very focused on what code gets exercised, meaning we<br>
don't take responsibility for what amounts to testing our dependencies<br>
at the same time as the code at hand.<br>
2. the tests ran faster since they aren't making HTTP requests (even<br>
if just to localhost).<br>
3. the provider code isn't cluttered up with<br>
vars-only-there-to-be-patched-out. [2]<br>
4. the test code isn't cluttered with many separate s.PatchValue calls. [2]<br>
5. setting up the faked return values was cleaner.<br>
6. checking which methods were called (in order) along with the<br>
arguments, was easier and cleaner.<br>
<br>
In addition to all that, taking the fake approach required that we<br>
encapsulate our low-level dependencies in a single interface. This<br>
was good because it forced us to spell out those dependencies. That<br>
helped us write the provider better. The low-level interface also<br>
made the code more maintainable since it makes the boundary layers<br>
explicit, and formats it in a concise display (the interface<br>
definition).<br>
<br>
So I've taken the base fake type from the GCE patch and pulled it into<br>
patch against the testing repo. [1] I've made some adjustments based<br>
on use cases I've had in subsequent patches. Nate has the bright idea<br>
of getting some feedback from the team before landing anything "since<br>
it's the kind of thing that'll start popping up everywhere, and I want<br>
to make sure it passes muster with others."<br>
<br>
I'm not suggesting that fakes are the solution to all testing problems<br>
so let's please avoid that discussion. :) Neither am I proposing that<br>
any existing tests should be converted to use fakes. Nor am I<br>
suggesting the need for any policy recommending the use of fakes. As<br>
well, we still need to make sure we have enough "full stack" test<br>
coverage to be confident about integration between layers. Relatedly,<br>
the concrete implementation of low-level interfaces needs adequate<br>
test coverage to ensure the underlying APIs (remote, etc.) continue to<br>
match expected behavior. So yeah, neither fakes nor isolated unit<br>
tests meet all our testing needs. I just think the base fake type<br>
I've written will help facilitate a cleaner approach where it's<br>
appropriate.<br>
<br>
Thoughts?<br>
<br>
-eric<br>
<br>
[1] <a href="https://github.com/juju/testing/pull/48" target="_blank">https://github.com/juju/testing/pull/48</a><br>
[2] We were still feeling out the approach so we still took the<br>
var-and-patch in some cases.<br>
[3] See "gceConnection" in <a href="http://reviews.vapour.ws/r/771/diff/#46" target="_blank">http://reviews.vapour.ws/r/771/diff/#46</a>.<br>
<span class="HOEnZb"><font color="#888888"><br>
--<br>
Juju-dev mailing list<br>
<a href="mailto:Juju-dev@lists.ubuntu.com">Juju-dev@lists.ubuntu.com</a><br>
Modify settings or unsubscribe at: <a href="https://lists.ubuntu.com/mailman/listinfo/juju-dev" target="_blank">https://lists.ubuntu.com/mailman/listinfo/juju-dev</a><br>
</font></span></blockquote></div><br></div>