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
Refactor tests from full-stack => unit #6598
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains 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
This file contains 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
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why mock out the parsing functions? They shouldn't need to change for tests, right? At best, if you want to avoid writing to disk during tests, make it take an io.Reader or something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remember I used the phrase, "jedi mind trick", in one of our conversations about IoC? Here is a great example!
What we're really writing here is a command, not the parsing logic or anything to do with
clouds.yaml
. It's purely about interfacing with the user and then deferring logic to other components. Taking in anio.Reader
is too much detail for a command; it should deal in concepts, e.g. "go parse this path" or "what do you know about this cloud?" If you defer that kind of logic to another module, you can implement/test that logic in one spot and use it all over the place. It is a very subtle (at least I think so) but important difference.This is also why in the tests were placing files onto the file system and then reading them back out and checking against strings. Because we are not decoupled from handling cloud files, because we're not just a dumb command, this is the only way we can test.
And although testing is the first thing everyone brings up -- and it is important and what we're discussing -- it's not the main reason to do it this way. The main reason is so that the how is decoupled from the what. And you can change the how without having to also change this code. Rick and I just ran into this situation yesterday.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But we actually do need to test that the interactive add cloud code actually produces valid yaml that can be parsed. If we mock that out, then we don't know. I could go change what the parsing code expected, and these tests would all pass, but the code would be wrong and would not work in production.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From a product standpoint, yes, we need to know that it is doing the correct thing. This is the responsibility of functional and/or CI tests. The responsibility of these tests is only to know that it's gluing the invocation of commands together with the
CloudMetadataStore
code correctly.We can be reasonably sure that it's doing the correct thing because of the emergent correctness between these tests and these tests.
This is why we pass in a well-tested, orthogonal module: so that this code, and these tests, don't have to re-test the same functionality. Functional tests will eclipse both of these packages, and this is why you have fewer functional tests (see this).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't have to be a functional test somewhere else. It can be a unit test right here. One of the main points of testing this code is to test that it produces valid yaml to pass to Parse. Just merely testing that it calls Parse is not good enough. Putting it into a functional test somewhere else makes failures harder to debug and more likely to be caught much later in the process, which wastes developers time. It also means that someone could easily change one of those tests in a far away place, and we'd never know we lost code coverage until something breaks... hopefully in CI, but possibly in production.
None of the tests in gh/j/j/cloud/cloud_test.go test that add cloud interactive produces valid yaml for cloud.Parse*, AFAICT.
Given that it's trivial to test here, there seems to be no reason not to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we had the fake call functions from within the
cloud/
package, the boundaries of our tests are then different from that of our code; i.e. we'd be testing something that may not actually be happening. Not only is checking that it's callingParse
good enough, it's the only logically correct thing to do here. Likewise, I would not expect thecloud/
tests to test an interactive add-cloud run; only that when passed datax
into it's functions, it returns datay
. The correctness of the add-cloud command when utilized with thecloud/
package is both an emergent property of both test suites, and an explicit property of a functional test.Functional tests are about testing that all layers are put together properly and actually work. This is the appropriate place to test that an interactive add-cloud works as intended.
Aside from the logical correctness, there are practical implications as to why you don't want to do functional/full-stack tests here. They are arguably no more difficult to write, but they are significantly more difficult to maintain. Again, please see that link about the testing pyramid. There are fewer functional/CI tests for a reason. Our current tests offer further evidence of this.