Skip to content
This repository has been archived by the owner on Mar 22, 2019. It is now read-only.

Added the ability to override non-zero values #15

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

apourchet
Copy link

@apourchet apourchet commented Oct 26, 2017

Please note that this patch does not break any of the existing functionalities
or guarantees of the package; it is only a collection of features that I would
have liked the package to have.

Previously, if some subtree of an object that was to be populated
was not zero/nil, we would terminate the population of that subtree
then and there. However this makes the package hard to use for very
targeted dependencies that only occur at the bottom of the dependency
tree (e.g: a network client or database layer).
With this patch, the injection does move downwards and populate the
fields that it can (see inject_test.go::TestNonEmptyInterfaceTraversal for example).

Furthermore, we can now have constructors populate our structs with
default values, that will then be overridden by the injection
mechanism if the tag inject:"override" is present on the field.
This means that we can now use sane defaults when creating our objects
that we can inject mocks for during unit tests.

// A is our bogus wrapper of the http.Client
type A struct {
    Client *http.Client `inject:"override"`
}

// NewA returns a fully functional A, with a non-nil http client.
func NewA() *A {
    return &A{
        Client: &http.Client{}
    }
}

var testClient *http.Client

func TestMain(m *testing.M)
    testClient = NewTestClient()
    os.Exit(m.Run())
}

func TestWithBogusClient(t *testing.T) {
    a := NewA()
    inject.Populate(a, testClient)

    // Here the injector should have overridden the default http client
    // inside A to use the bogus client.
    a.Do(...)
}

The last feature that I was looking for was the traversal of non-nil
interfaces. For instance:

// I is our bogus interface. It has no functions for the sake of
// brevity.
type I interface {}

// A implements that bogus interface
type A struct {
    Client *http.Client `inject:""`
}

// Nested contains an I which does not need to be injected. However,
// the I (which will be of type A at runtime) needs to be traversed so that
// we can inject the right *http.Client.
type Nested struct {
    Iface I
}

func main() {
    n := &Nested{
        Iface: &A{},
    }
    specialclient := &http.Client{}
    inject.Populate(n, specialclient)
}

This is useful in bigger projects when you have many subcomponents that
use an *http.Client or sub-interface of that, and need to all have that
client mocked during unit tests.

@facebook-github-bot
Copy link

Thank you for your pull request and welcome to our community. We require contributors to sign our Contributor License Agreement, and we don't seem to have you on file. In order for us to review and merge your code, please sign up at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need the corporate CLA signed.

If you have received this in error or have any questions, please contact us at cla@fb.com. Thanks!

@facebook-github-bot
Copy link

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Facebook open source project. Thanks!

Previously, if some subtree of an object that was to be populated
was not zero/nil, we would terminate the population of that subtree
then and there. However this makes the package hard to use for very
target dependencies that only occur at the bottom of the dependency
tree (e.g: a network client or database layer).

With this patch, the injection does move downwards and populate the
fields that it can.

Furthermore, we can now have constructors populate our structs with
default values, that will then be overridden by the injection
mechanism if the tag `inject:"override"` is present on the field.
This means that we can now use sane defaults when creating our objects
that we can inject mocks for during unit tests.
```go
// A is our bogus wrapper of the http.Client
type A struct {
    Client *http.Client `inject:"override"`
}

// NewA returns a fully functional A, with a non-nil http client.
func NewA() *A {
    return &A{
        Client: &http.Client{}
    }
}

var testClient *http.Client

func TestMain(m *testing.M)
    testClient = NewTestClient()
    os.Exit(m.Run())
}

func TestWithBogusClient(t *testing.T) {
    a := NewA()
    inject.Populate(a, testClient)

    // Here the injector should have overridden the default http client
    // inside A to use the bogus client.
    a.Do(...)
}
```

The last feature that I was looking for was the traversal of non-nil
interfaces. For instance:
```go
// I is our bogus interface. It has no functions for the sake of
// brevity.
type I interface {}

// A implements that bogus interface
type A struct {
    Client *http.Client `inject:""`
}

// Nested contains an I which does not need to be injected. However,
// the I (which will be of type A at runtime) needs to be traversed so that
// we can inject the right *http.Client.
type Nested struct {
    Iface I
}

func main() {
    n := &Nested{
        Iface: &A{},
    }
    specialclient := &http.Client{}
    inject.Populate(n, specialclient)
}
```
This is useful in bigger projects when you have many subcomponents that
use an *http.Client or sub-interface of that, and need to all have that
client mocked during unit tests.
@emanuel-v-r
Copy link

emanuel-v-r commented Jul 9, 2018

Created this repo https://github.com/imaramos/goject with the purpose of extend this libary due to the lack of maintainance.
The only feature added was the described in this pull request #21.
The rest is only renaming.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants