-
Notifications
You must be signed in to change notification settings - Fork 115
Unit test tooling #625
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
Merged
Merged
Unit test tooling #625
Changes from all commits
Commits
Show all changes
41 commits
Select commit
Hold shift + click to select a range
43355a6
linux build speed improvements
daweim0 fd61e50
small changes
daweim0 f0f3cf7
added protection against node json with missing data
daweim0 e54ecf9
Merge branch 'david/faster_builds' into david/malformed-node-fix
daweim0 e6a5e4b
fixed build speedups
daweim0 f9fd570
Revert "fixed build speedups"
daweim0 f83ce83
Revert "Merge branch 'david/faster_builds' into david/malformed-node-…
daweim0 95edf55
initial snapshot
daweim0 564bb5f
added some comments
daweim0 565bbb1
testing github actions
daweim0 33a4a39
fixing workflow file
daweim0 523d87d
test
daweim0 f36853a
test
daweim0 b92c779
testing again
daweim0 fd41f57
making ruby test script executable
daweim0 7eec628
got rid of global object registry, still have the flag though
daweim0 dc009d1
more complex go unit test with mocking
daweim0 050604d
Merge branch 'ci_dev' of github.com:microsoft/Docker-Provider into da…
daweim0 a5e230b
go build broken
daweim0 1078d46
fixed go build
daweim0 aed8cc4
basic in_kube_nodes test
daweim0 083f7b4
Merge branch 'david/malformed-node-fix' of github.com:microsoft/Docke…
daweim0 8b970ef
test of malformed node fix done, ready to polish and write documentation
daweim0 7690d6b
some documentation
daweim0 ed93c3a
Merge branch 'ci_dev' of github.com:microsoft/Docker-Provider into da…
daweim0 b6905ed
cleanup
daweim0 a7456e2
forgot to include test driver in last commit
daweim0 8768946
cleanup almost done, giving up on filter_cadvisor2mdm_test.rb for now
daweim0 ad637d3
about to try to remove ^M newlines
daweim0 bdfadcc
Merge branch 'ci_dev' of github.com:microsoft/Docker-Provider into HEAD
daweim0 a05b1de
cleanup
daweim0 6d2ce76
more cleanup
daweim0 eef9fcc
ready for review
daweim0 504bcf5
installing fluentd in github action container (not sure why that wasn…
daweim0 e367370
fixing github actions again
daweim0 9118de6
removing byebug
daweim0 c5d5221
fixing windows build
daweim0 66f89a6
updating windows makefile.ps1
daweim0 efe4f16
Merge branch 'ci_dev' of github.com:microsoft/Docker-Provider into da…
daweim0 1176d71
Merge branch 'david/unit-tests' of github.com:microsoft/Docker-Provid…
daweim0 915c6f1
Merge branch 'david/unit-tests' of github.com:microsoft/Docker-Provid…
daweim0 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| name: Run Unit Tests | ||
| on: | ||
| pull_request: | ||
| types: [opened, synchronize, reopened] | ||
| branches: | ||
| - ci_dev | ||
| - ci_prod | ||
| jobs: | ||
| Golang-Tests: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Check out repository code | ||
| uses: actions/checkout@v2 | ||
| - name: Run unit tests | ||
| run: | | ||
| cd ${{ github.workspace }} | ||
| ./test/unit-tests/run_go_tests.sh | ||
| Ruby-Tests: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Check out repository code | ||
| uses: actions/checkout@v2 | ||
| - name: install fluent | ||
| run: | | ||
| sudo gem install fluentd -v "1.12.2" --no-document | ||
| sudo fluentd --setup ./fluent | ||
| - name: Run unit tests | ||
| run: | | ||
| cd ${{ github.workspace }} | ||
| ./test/unit-tests/run_ruby_tests.sh |
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| # Dev Guide | ||
|
|
||
| More advanced information needed to develop or build the docker provider will live here | ||
|
|
||
| <!-- TODO: eventually move dev info from README.md to here--> | ||
|
|
||
| ## Testing | ||
| Last updated 8/18/2021 | ||
|
|
||
| To run all unit tests run the commands `test/unit-tests/run_go_tests.sh` and `test/unit-tests/run_ruby_tests.sh` | ||
|
|
||
| #### Conventions: | ||
| 1. Unit tests should go in their own file, but in the same folder as the source code their testing. For example, the tests for `in_kube_nodes.rb` are in `in_kube_nodes_test.rb`. Both files are in the folder `source/plugin/ruby`. | ||
|
|
||
| ### Ruby | ||
| Sample tests are provided in [in_kube_nodes_test.rb](source/plugin/ruby/in_kube_nodes_test.rb). They are meant to demo the tooling used for unit tests (as opposed to being comprehensive tests). Basic techniques like mocking are demonstrated there. | ||
|
|
||
| #### Conventions: | ||
| 1. When modifying a fluentd plugin for unit testing, any mocked classes (like KubernetesApiClient, applicationInsightsUtility, env, etc.) should be passed in as optional arguments of initialize. For example: | ||
| ``` | ||
| def initialize | ||
| super | ||
| ``` | ||
| would be turned into | ||
| ``` | ||
| def initialize (kubernetesApiClient=nil, applicationInsightsUtility=nil, extensionUtils=nil, env=nil) | ||
| super() | ||
| ``` | ||
|
|
||
| 2. Having end-to-end tests of all fluentd plugins is a longshot. We care more about unit testing smaller blocks of functionality (like all the helper functions in KubeNodeInventory.rb). Unit tests for fluentd plugins are not expected. | ||
|
|
||
| ### Golang | ||
|
|
||
| Since golang is statically compiled, mocking requires a lot more work than in ruby. Sample tests are provided in [utils_test.go](source/plugin/go/src/utils_test.go) and [extension_test.go](source/plugin/go/src/extension/extension_test.go). Again, they are meant to demo the tooling used for unit tests (as opposed to being comprehensive tests). Basic techniques like mocking are demonstrated there. | ||
|
|
||
| #### Mocking: | ||
| Mocks are generated with gomock (mockgen). | ||
| * Mock files should be called *_mock.go (socket_writer.go => socket_writer_mock.go) | ||
| * Mocks should not be checked in to git. (they have been added to the .gitignore) | ||
| * The command to generate mock files should go in a `//go:generate` comment at the top of the mocked file (see [socket_writer.go](source/plugin/go/src/extension/socket_writer.go) for an example). This way mocks can be generated by the unit test script. | ||
| * Mocks also go in the same folder as the mocked files. This is unfortunate, but necessary to avoid circular package dependencies (anyone else feel free to figure out how to move mocks to a separate folder) | ||
|
|
||
| Using mocks is also a little tricky. In order to mock functions in a package with gomock, they must be converted to reciever methods of a struct. This way the struct can be swapped out at runtime to change which implementaions of a method are called. See the example below: | ||
|
|
||
| ``` | ||
| // declare all functions to be mocked in this interface | ||
| type registrationPreCheckerInterface interface { | ||
| FUT(string) bool | ||
| } | ||
|
|
||
| // Create a struct which implements the above interface | ||
| type regPreCheck struct{} | ||
|
|
||
| func (r regPreCheck) FUT(email string) bool { | ||
| fmt.Println("real FUT() called") | ||
| return true | ||
| } | ||
|
|
||
| // Create a global variable and assign it to the struct | ||
| var regPreCondVar registrationPreCheckerInterface | ||
|
|
||
| func init() { | ||
| regPreCondVar = regPreCheck{} | ||
| } | ||
| ``` | ||
|
|
||
| Now any code wishing to call FUT() will call `regPreCondVar.FUT("")` | ||
|
|
||
| A unit test can substitute its own implementaion of FUT() like so | ||
|
|
||
| ``` | ||
| // This will hold the mock of FUT we want to substitute | ||
| var FUTMock func(email string) bool | ||
|
|
||
| // create a new struct which implements the earlier interface | ||
| type regPreCheckMock struct{} | ||
|
|
||
| func (u regPreCheckMock) FUT(email string) bool { | ||
| return FUTMock(email) | ||
| } | ||
| ``` | ||
|
|
||
| Everything is set up. Now a unit test can substitute in a mock like so: | ||
|
|
||
| ``` | ||
| func someUnitTest() { | ||
| // This will call the actual implementaion of FUT() | ||
| regPreCondVar.FUT("") | ||
|
|
||
| // Now the test creates another struct to substitue. After this like all calls to FUT() will be diverted | ||
| regPreCondVar = regPreCheckMock{} | ||
|
|
||
| // substute another function to run instead of FUT() | ||
| FUTMock = func(email string) bool { | ||
| fmt.Println("FUT 1 called") | ||
| return false | ||
| } | ||
| // This will call the function defined right above | ||
| regPreCondVar.FUT("") | ||
|
|
||
| // We can substitue another implementation | ||
| FUTMock = func(email string) bool { | ||
| fmt.Println("FUT 2 called") | ||
| return false | ||
| } | ||
| regPreCondVar.FUT("") | ||
|
|
||
| // put the old behavior back | ||
| regPreCondVar = regPreCheck{} | ||
| // this will call the actual implementation of FUT() | ||
| regPreCondVar.FUT("") | ||
|
|
||
| } | ||
| ``` | ||
|
|
||
| A concrete example of this can be found in [socket_writer.go](source/plugin/go/src/extension/socket_writer.go) and [extension_test.go](source/plugin/go/src/extension/extension_test.go). Again, if anybody has a better way feel free to update this guide. | ||
|
|
||
|
|
||
|
|
||
| A simpler way to test a specific function is to write wrapper functions. Test code calls the inner function (ReadFileContentsImpl) and product code calls the wrapper function (ReadFileContents). The wrapper function provides any outside state which a unit test would want to control (like a function to read a file). This option makes product code more verbose, but probably easier to read too. Either way is acceptable. | ||
| ``` | ||
| func ReadFileContents(fullPathToFileName string) (string, error) { | ||
| return ReadFileContentsImpl(fullPathToFileName, ioutil.ReadFile) | ||
| } | ||
| ``` | ||
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
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
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
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
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
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.
Uh oh!
There was an error while loading. Please reload this page.