Demo Unit test for CloudStorageAPIClient - #8
Open
chelseatroy wants to merge 4 commits into
Open
Conversation
Collaborator
Author
|
force-pushed to do an interactive rebase of two commits I'd already pushed that were really part of the same change set. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Goal(s) of this Pull Request:
Figure out unit testing. This might be a conversation starter as opposed to a final implementation.
This test never calls the
google.cloudAPIs directly; instead, it mocks out calls to that API and checks that those mocked calls were made.As you can see from the commit history, I first rolled my own mocks manually.. Then in a follow-up commit I switched to this third party mock GCS client for Python.
This library tests against the real functions, I'm guessing, whenever the maintainers run the tests, so it seemed like the best choice I could find. This does mean our unit tests now depend on a third party library with six stars maintained by three people, none of whom work at Google, at the rate of maybe a commit per month, which does not suggest active monitoring or development.
To wit: the library doesn't support
client.get_bucket(), onlyclient.bucket(). So I had to change our implementation to be able to use this mocking library, even though our original implementation was functional. For our specific use case the switch didn't matter, but the two functions do not do exactly the same thing.This could have introduced regression risk, for the sake of working with the mocking library, and is an example of the reason I have misgivings about mocking libraries from people other than the maintainers of the thing being mocked. We could make a PR against the library repository to add
.get_bucket(), but if we're considering forking a library the very first time we use it, I think that raises valid questions about the suitability of the library for our use case.I'll share why I opted for the library rather than manual shallow mocks later in this description. How did I check for a behavior regression while making the switch? I ran our integration tests.
The issues with the integration tests for this functionality are:
For that reason, we turned them off by default and will run them on CI instead of having them run anytime developers try to do a test loop.
It would be nice to have a way to test this functionality that's fast, that doesn't require authentication. But the integration test has this important advantage:
google.cloudAPI is broken in such a way that our files are not getting stored or fetched, it fails. If Google changes the behavior of their API in a way that affects us, it fails. If we change our function calls in a way that produces a regression, it fails. We then get to find out from our test suite that our tool is broken, instead of from our consumers. That's valuable.Meanwhile, as long as our functions succeed at storing and fetching, the integration tests succeed. They don't check how we make that happen beyond calling the functions themselves. They are how I knew within seconds, when I had to switch the implementation to satisfy the unit test mocking library, that things still worked.
By contrast, this unit test in this PR tests exclusively implementation, and not behavior.
The circumstances under which this test would fail would be:
google.cloudAPI and forgets to put it back. Because integration tests will run on CI and those tests will also fail if this happens, this mistake would not slip into the main branch anyway.Why I switched to the library from shallow mocks:
testingflag to the function arguments that changes the return type of the function. Although this is better for our consumers, it's worse for maintainers and contributors: they now have to figure out why the functions' return type changes. Having the return type of a function change is an unconventional choice and one that adds overhead to understanding this API client's behavior. The library permits me to not have to choose between these two evils.However, no matter how we unit test this, to me it feels brittle. If these tests fail, we have to run the integration tests to verify whether anything is actually wrong, and if so, what precisely is wrong. Is it worthwhile to have these two tests that operate by a mocked API proxy, such that they run quickly but might fail for the wrong reasons? Maybe. But it's worth explicitly calling out, so I added comments to that effect.
Acceptance criteria:
pytestshows 2 tests passingpytest -m integrationshows 2 tests passingblobincloud_storage_api_client.pycauses a unit test and an integration test to fail