Skip to content

Instantly share code, notes, and snippets.

@MrJadaml
Last active August 21, 2020 14:40
Show Gist options
  • Save MrJadaml/94e28fa2f4a3a6fbba3e3f20b98636e1 to your computer and use it in GitHub Desktop.
Save MrJadaml/94e28fa2f4a3a6fbba3e3f20b98636e1 to your computer and use it in GitHub Desktop.

Revisions

  1. Adam L revised this gist Jan 11, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion new-mocks-pattern-rfc.md
    Original file line number Diff line number Diff line change
    @@ -65,7 +65,7 @@ Not even close to the worse offender, but what I was working in was +3K lines of
    [Current story analytics mock](https://github.com/Lululemon/ecom-web-app/blob/master/src/__mocks/story-analytics.json)
    Not a perfect example as to what is laid out in the proposal, but a close example from my latest PR:
    [PR NCE-234](https://github.com/Lululemon/ecom-web-app/blob/66a507161c867d6ea2091457cdbd138bcc438b59/src/components/editorial-merchandising/editorial-merchandising.mocks.js)
    [NCE-234](https://github.com/Lululemon/ecom-web-app/blob/66a507161c867d6ea2091457cdbd138bcc438b59/src/components/editorial-merchandising/editorial-merchandising.mocks.js)
    ### File and organizational motivations:
  2. Adam L revised this gist Jan 11, 2019. 1 changed file with 23 additions and 4 deletions.
    27 changes: 23 additions & 4 deletions new-mocks-pattern-rfc.md
    Original file line number Diff line number Diff line change
    @@ -54,8 +54,11 @@ export const required = {
    ## Motivation
    After spending what felt like an unnecessary amount of time rummaging through monolithic mock data sets -- 95% of which was unrelated or not required to produce passing tests -- I broke out only the minimum required data I needed to get my specs to pass.
    The other shortfall with the current implementation was having a harder time . Given all the extra noise generated by irrelevant data to be parse though, building up a mental model of the problem at hand also became more cumbersome.
    Combined that with common interruptions of meetings, slack conversation, lunch/EOD etc. that resulting in losing that mental model and it becomes clear how nice it is to keep those simple to repeatedly spin them back up again.
    I also found it hard to know if the data for the use case I wanted already existed or if I should extend the mock to include it. There was a lack of clarity as to if I would either be breaking a dependency to data I was changing or if I were duplicating data that already existed.
    Not even close to the worse offender, but what I was working in was +3K lines of mock data:
    @@ -67,28 +70,42 @@ Not a perfect example as to what is laid out in the proposal, but a close exampl
    ### File and organizational motivations:
    The current \_\_mocks naming pattern is similar to both suggesting a sort of "private" module as per the underscore prefix, though I would think this not a private module.
    The current naming pattern is also similar to conventions used by other libraries with the \_\__mocks\_\_ which adds some confusion, particularly since those patterns are also used in the ecom app by things like jest mock functions/files and snapshots. Those mocks are also intended to be "private" sending something of a mixed message.
    The current \_\_mocks directory does not reflect any sort of pattern for how the mocks are organized. Some live under the /components directory while some have their own directories at the top level, but then some mocks are not under any sub directory and the module live at the top level.
    The proposed naming style follows the existing pattern used for test modules. Since those same test modules are the primary consumer of the mocks module having a matching pattern seems logical
    The proposal of co-locating mocks within the same directory as the feature and test module they support seems like a convenient and intuitive approach. It will be very clear where to find and document data specific to that feature.
    The proposed naming pattern also lends itself to potential devops tooling down the line.
    The proposed co-location pattern lends itself to easy, confident and comprehensive clean-up in the case of a feature/module removal -- Mock data for a removed module won’t turn into dead code lost in large shared mocks located elsewhere in the app.
    ### Module implementation motivations:
    The current implementation of mocks tend to be monolithic data structures that are not always easy to navigate. When test coverage for existing untested functionality -- of which there is a fair amount of -- or new functionality
    The current implementation of mocks tend to be monolithic data structures that are not always easy to navigate. When test coverage for existing untested functionality -- of which there is a fair amount of -- or new functionality.
    The current mocks and not always easy to extend either due to their monolithic structure or the lack of clarity of other dependencies of that mock.
    The current monolithic and dependency issues tends to drive extension by duplication only exacerbating both the monolithic and clarity issues.
    Current mocks appear to have an excess of irrelevant data -- though it is hard to say as it relates to the lack of clarity around the dependencies previously mentioned
    Current mocks appear to have an excess of irrelevant data -- though it is hard to say as it relates to the lack of clarity around the dependencies previously mentioned.
    Current mocks do not lend themselves to composition. Not necessarily a "flaw" so much as a missed opportunity.
    Current mocks have mixed indentation patterns -- likely a product of copy and pasting in blocks of data grabbed from elsewhere.
    The proposed implementation -- by nature of being feature oriented -- would lend itself to be more abbreviated data structures. Ideally one would not add data not declared via function-signature/propTypes for that util/component
    The proposed implementation -- by nature of being feature oriented -- would lend itself to be more abbreviated data structures. Ideally one would not add data not declared via function-signature/propTypes for that util/component.
    - The co-location of the mocks also drives a greater sense of relation and maintenance, preventing abuse by way of dumping huge data structures with unrelated fields.
    The co-location makes dependencies of the mock clear - easier to debug conflicts or extend
    The co-location makes dependencies of the mock clear - easier to debug conflicts or extend.
    The smaller scope allows mock data can be more easily broken down into more semantically named data sets tieing to specific use cases.
    The smaller scope allows mock data to be composable in an explicit manner -- see example above.
    The proposed implementation makes clean up easy if you delete a no longer relevant unit test. No large, implicit, catch-all data objects passed in during initial setup.
    @@ -117,5 +134,7 @@ What names and terminology work best for these concepts and why?
    ## Unresolved questions
    Would this also make sense for util/libs without causing unnecessary modularization?
    Would there be a sensible way to "import" in higher level data that may be shared by multiple modules? If so, what are some examples?
    Would there be a preference to a JSON format or a JS format?
  3. Adam L revised this gist Jan 11, 2019. 1 changed file with 9 additions and 7 deletions.
    16 changes: 9 additions & 7 deletions new-mocks-pattern-rfc.md
    Original file line number Diff line number Diff line change
    @@ -2,14 +2,16 @@

    Mocks are designated by a `.mocks.js` extensions and would be co-located with their corresponding module.

    # Basic example
    ## Basic example

    ```bash
    ├── SomeComponent
    ├── __snapshots__/
    ├── SomeComponent.scss
    ├── SomeComponent.js
    ├── SomeComponent.test.js
    ├── SomeComponent.mocks.js
    ```


    ```js
    @@ -49,7 +51,7 @@ export const required = {

    ```
    # Motivation
    ## Motivation
    After spending what felt like an unnecessary amount of time rummaging through monolithic mock data sets -- 95% of which was unrelated or not required to produce passing tests -- I broke out only the minimum required data I needed to get my specs to pass.
    The other shortfall with the current implementation was having a harder time . Given all the extra noise generated by irrelevant data to be parse though, building up a mental model of the problem at hand also became more cumbersome.
    @@ -90,29 +92,29 @@ The smaller scope allows mock data to be composable in an explicit manner -- see
    The proposed implementation makes clean up easy if you delete a no longer relevant unit test. No large, implicit, catch-all data objects passed in during initial setup.
    # Drawbacks
    ## Drawbacks
    - We would be maintaining two patterns for a while, while the existing pattern is slowly deprecated
    - Alternatively, to not maintain two patterns there would be an upfront cost to migrating existing mocks to fit the new pattern.
    # Alternatives
    ## Alternatives
    An alternative would be to define mock data within the test modules themselves -- I would prefer the existing pattern over that though.
    # Adoption strategy
    ## Adoption strategy
    - We could take an incremental strategy similar to how we approach adding new test coverage for areas of the codebase we touch.
    - There could also be an initial cleanup of "low hanging fruit" mocks that require minimal effort to migrate upfront.
    - There could then be a "wrap-up" phase down the line where it is clear the dependency requirement on the exiting pattern is mostly gone.
    # How we teach this
    ## How we teach this
    What names and terminology work best for these concepts and why?
    - Feature mocks: The full set of mock data associated to a specific feature.
    - Unit mocks: "Bare minimum" mock data associated to a specific unit test.
    - Required mock: The bare minimum data required in order for the feature to run without failing -- AKA `required` propTypes.
    # Unresolved questions
    ## Unresolved questions
    Would this also make sense for util/libs without causing unnecessary modularization?
    Would there be a sensible way to "import" in higher level data that may be shared by multiple modules? If so, what are some examples?
  4. Adam L renamed this gist Jan 11, 2019. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  5. Adam L created this gist Jan 11, 2019.
    119 changes: 119 additions & 0 deletions New Mocks Convention
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,119 @@
    # Summary

    Mocks are designated by a `.mocks.js` extensions and would be co-located with their corresponding module.

    # Basic example

    ├── SomeComponent
    ├── __snapshots__/
    ├── SomeComponent.scss
    ├── SomeComponent.js
    ├── SomeComponent.test.js
    ├── SomeComponent.mocks.js


    ```js
    // SomeComponent.mocks.js

    // data for default use case
    export const goodCat = {
    name: 'Moon',
    isCuddler: true
    isCounterSurffer: false
    }

    // data for conditional case
    export const naughtyCat = {
    name: 'Apollo',
    isCuddler: false,
    isCounterSurffer: true
    }

    // explict data composition
    export const cats = [ goodCat, naughtyCat ];

    export const cutePuppy = {
    name: 'Bronson',
    isCuddler: true,
    }

    // data not to be included with required data but to be tested
    export unrequiredDogs = [ cutePuppy ];

    // minimum data required not to error -- provides an explicit understanding of the component's data contract
    export const required = {
    i: 'must',
    have: cats,
    to: function()
    }

    ```

    # Motivation

    After spending what felt like an unnecessary amount of time rummaging through monolithic mock data sets -- 95% of which was unrelated or not required to produce passing tests -- I broke out only the minimum required data I needed to get my specs to pass.
    The other shortfall with the current implementation was having a harder time . Given all the extra noise generated by irrelevant data to be parse though, building up a mental model of the problem at hand also became more cumbersome.
    Combined that with common interruptions of meetings, slack conversation, lunch/EOD etc. that resulting in losing that mental model and it becomes clear how nice it is to keep those simple to repeatedly spin them back up again.
    I also found it hard to know if the data for the use case I wanted already existed or if I should extend the mock to include it. There was a lack of clarity as to if I would either be breaking a dependency to data I was changing or if I were duplicating data that already existed.

    Not even close to the worse offender, but what I was working in was +3K lines of mock data:
    [Current story analytics mock](https://github.com/Lululemon/ecom-web-app/blob/master/src/__mocks/story-analytics.json)

    Not a perfect example as to what is laid out in the proposal, but a close example from my latest PR:
    [PR NCE-234](https://github.com/Lululemon/ecom-web-app/blob/66a507161c867d6ea2091457cdbd138bcc438b59/src/components/editorial-merchandising/editorial-merchandising.mocks.js)

    ### File and organizational motivations:

    The current \_\_mocks naming pattern is similar to both suggesting a sort of "private" module as per the underscore prefix, though I would think this not a private module.
    The current naming pattern is also similar to conventions used by other libraries with the \_\__mocks\_\_ which adds some confusion, particularly since those patterns are also used in the ecom app by things like jest mock functions/files and snapshots. Those mocks are also intended to be "private" sending something of a mixed message.
    The current \_\_mocks directory does not reflect any sort of pattern for how the mocks are organized. Some live under the /components directory while some have their own directories at the top level, but then some mocks are not under any sub directory and the module live at the top level.

    The proposed naming style follows the existing pattern used for test modules. Since those same test modules are the primary consumer of the mocks module having a matching pattern seems logical
    The proposal of co-locating mocks within the same directory as the feature and test module they support seems like a convenient and intuitive approach. It will be very clear where to find and document data specific to that feature.
    The proposed naming pattern also lends itself to potential devops tooling down the line.
    The proposed co-location pattern lends itself to easy, confident and comprehensive clean-up in the case of a feature/module removal -- Mock data for a removed module won’t turn into dead code lost in large shared mocks located elsewhere in the app.

    ### Module implementation motivations:

    The current implementation of mocks tend to be monolithic data structures that are not always easy to navigate. When test coverage for existing untested functionality -- of which there is a fair amount of -- or new functionality
    The current mocks and not always easy to extend either due to their monolithic structure or the lack of clarity of other dependencies of that mock.
    The current monolithic and dependency issues tends to drive extension by duplication only exacerbating both the monolithic and clarity issues.
    Current mocks appear to have an excess of irrelevant data -- though it is hard to say as it relates to the lack of clarity around the dependencies previously mentioned
    Current mocks do not lend themselves to composition. Not necessarily a "flaw" so much as a missed opportunity.
    Current mocks have mixed indentation patterns -- likely a product of copy and pasting in blocks of data grabbed from elsewhere.

    The proposed implementation -- by nature of being feature oriented -- would lend itself to be more abbreviated data structures. Ideally one would not add data not declared via function-signature/propTypes for that util/component
    - The co-location of the mocks also drives a greater sense of relation and maintenance, preventing abuse by way of dumping huge data structures with unrelated fields.
    The co-location makes dependencies of the mock clear - easier to debug conflicts or extend
    The smaller scope allows mock data can be more easily broken down into more semantically named data sets tieing to specific use cases.
    The smaller scope allows mock data to be composable in an explicit manner -- see example above.
    The proposed implementation makes clean up easy if you delete a no longer relevant unit test. No large, implicit, catch-all data objects passed in during initial setup.


    # Drawbacks

    - We would be maintaining two patterns for a while, while the existing pattern is slowly deprecated
    - Alternatively, to not maintain two patterns there would be an upfront cost to migrating existing mocks to fit the new pattern.

    # Alternatives

    An alternative would be to define mock data within the test modules themselves -- I would prefer the existing pattern over that though.

    # Adoption strategy

    - We could take an incremental strategy similar to how we approach adding new test coverage for areas of the codebase we touch.
    - There could also be an initial cleanup of "low hanging fruit" mocks that require minimal effort to migrate upfront.
    - There could then be a "wrap-up" phase down the line where it is clear the dependency requirement on the exiting pattern is mostly gone.

    # How we teach this

    What names and terminology work best for these concepts and why?
    - Feature mocks: The full set of mock data associated to a specific feature.
    - Unit mocks: "Bare minimum" mock data associated to a specific unit test.
    - Required mock: The bare minimum data required in order for the feature to run without failing -- AKA `required` propTypes.

    # Unresolved questions

    Would this also make sense for util/libs without causing unnecessary modularization?
    Would there be a sensible way to "import" in higher level data that may be shared by multiple modules? If so, what are some examples?
    Would there be a preference to a JSON format or a JS format?