Mike-Gibson / mock-apollo-client

Helps unit test components which use the Apollo Client.
MIT License
117 stars 15 forks source link

Jest TypeScript Unit Test Issue: Missing properties store, writeData, initQueryManager #23

Closed jcjp closed 3 years ago

jcjp commented 3 years ago

I am encountering an error of Type 'ApolloClient<NormalizedCacheObject> & { setRequestHandler: (query: DocumentNode, handler: RequestHandler<any, any>) => void; }' is missing the following properties from type 'ApolloClient<any>': store, writeData, initQueryManager

It works fine in JavaScript unit test using Jest. However we are currently moving to TypeScript even in unit test. I tried to create a mock apollo provider using this code.

import {shallowMount, createLocalVue, Wrapper} from '@vue/test-utils'
import Vuex,{Store} from 'vuex'
...
import {createMockClient} from 'mock-apollo-client'
import VueApollo from 'vue-apollo'
...
const localVue = createLocalVue()

localVue.use(Vuex)
localVue.use(VueApollo)

mockClient = createMockClient()
apolloProvider = new VueApollo({defaultClient: mockClient})
...
wrapper = shallowMount(IndexPage, {
 store,
 localVue,
 apolloProvider
})
...

I tried modifying the type of provided by the mock-apollo-client and add the missing properties however another issue is shown: Type 'MockApolloClient' is not assignable to type 'ApolloClient<any>'. The types of 'link.split' are incompatible between these types.

Mike-Gibson commented 3 years ago

From looking at the type error, it seems like you may be using version 1.x of mock-apollo-client, which is compatible with Apollo client 3, but your version of vue-apollo uses Apollo client vesion 2. It's surprising that the tests run successfully though.

Could you check and see if it is a version mismatch? If it is, then you should use the latest 0.x version of mock-apollo-client, which is 0.5.0 at the time of writing.

If the versions are correct, then it would be really useful if you could provide a repro so I can investigate further.

Thanks.

jcjp commented 3 years ago

From looking at the type error, it seems like you may be using version 1.x of mock-apollo-client, which is compatible with Apollo client 3, but your version of vue-apollo uses Apollo client vesion 2. It's surprising that the tests run successfully though.

Could you check and see if it is a version mismatch? If it is, then you should use the latest 0.x version of mock-apollo-client, which is 0.5.0 at the time of writing.

If the versions are correct, then it would be really useful if you could provide a repro so I can investigate further.

Thanks.

Here are my dependencies:

"dependencies": {
    "@nuxt/typescript-runtime": "^2.0.1",
    "@nuxtjs/apollo": "^4.0.1-rc.5",
    "core-js": "^3.8.2",
    "nuxt": "^2.14.12",
    "nuxt-i18n": "^6.18.0",
    "nuxt-property-decorator": "^2.8.8",
    "vue-class-component": "^7.2.6",
    "vue-property-decorator": "^9.1.2"
  },
  "devDependencies": {
    "@jagi/jest-transform-graphql": "^1.0.2",
    "@nuxt/types": "^2.14.12",
    "@nuxt/typescript-build": "^2.0.4",
    "@nuxtjs/eslint-config-typescript": "^5.0.0",
    "@nuxtjs/eslint-module": "^3.0.2",
    "@nuxtjs/robots": "^2.4.2",
    "@nuxtjs/sitemap": "^2.4.0",
    "@nuxtjs/stylelint-module": "^4.0.0",
    "@nuxtjs/vuetify": "^1.11.3",
    "@types/jest": "^26.0.20",
    "@vue/test-utils": "^1.1.2",
    "babel-core": "7.0.0-bridge.0",
    "babel-eslint": "^10.1.0",
    "babel-jest": "^26.6.3",
    "eslint": "^7.18.0",
    "eslint-config-airbnb": "^18.2.1",
    "eslint-config-prettier": "^7.2.0",
    "eslint-plugin-nuxt": "^2.0.0",
    "eslint-plugin-prettier": "^3.3.1",
    "eslint-plugin-vue": "^7.5.0",
    "husky": "^4.3.8",
    "jest": "^26.6.3",
    "lint-staged": "^10.5.3",
    "mock-apollo-client": "^1.0.0",
    "prettier": "^2.2.1",
    "sonarqube-scanner": "^2.8.0",
    "stylelint": "^13.8.0",
    "stylelint-config-prettier": "^8.0.2",
    "stylelint-config-standard": "^20.0.0",
    "ts-jest": "^26.5.0",
    "vue-jest": "^3.0.4",
    "vuetify-loader": "^1.7.2"
  }

I tried to look into my node_modules for the vue-apollo library and looked into the version in package.json it states that it is version 3.0.5. I will create a repo so that you could test this issue, but as of the moment I am busy thank you for your quick response!

Mike-Gibson commented 3 years ago

Thanks for the info. Looks like @nuxtjs/apollo@4.0.1-rc.5 => vue-apollo@^3.0.5=> peer dependency: apollo-client:^2.0.0", so I do think it's a version mismatch.

You can run

npm list apollo-client @apollo/client

it will list the tree of dependencies in your repo for apollo client (apollo-client is v2 and they renamed to @apollo/client in v3). Let me know what you find.

jcjp commented 3 years ago

Here is the output of the command you gave me:

@nuxtjs/apollo@4.0.1-rc.5
  └─┬ vue-cli-plugin-apollo@0.22.2
    ├── apollo-client@2.6.10 
    └─┬ graphql-tools@6.2.6
      └─┬ @graphql-tools/links@6.2.5
        └─┬ apollo-upload-client@14.1.2
          └── @apollo/client@3.3.7 

For additional information I use this guide to create my unit test: Testing Vue Apollo 2020 Edition

I tried upgrading my vue-apollo to use apollo version 3 using this command: npm install --save vue-apollo@next @apollo/client following this tutorial: From vue-apollo 3. But based on my checking ti still uses the @nuxtjs/apollo and ignores the vue-apollo version3.

But still encountering the error here is the npm tree:

├── @apollo/client@3.3.9 
└─┬ @nuxtjs/apollo@4.0.1-rc.5
  └─┬ vue-cli-plugin-apollo@0.22.2
    ├── apollo-client@2.6.10 
    └─┬ graphql-tools@6.2.6
      └─┬ @graphql-tools/links@6.2.5
        └─┬ apollo-upload-client@14.1.2
          └── @apollo/client@3.3.7

Update: I forced TypeScript to ignore the type checking on the file, however when I try to run the test it fails. The data and error are both empty.

Update: We were able to circumvent on the type checking for creating a apollo client with this code:

mockClient = createMockClient()

const apolloProvider = new VueApollo({
  defaultClient: {
    ...mockClient,
    store: jest.fn(),
    writeData: jest.fn(),
    initQueryManager: jest.fn()
  }
})

Update: We downgraded to version ^0.5.0 and we are encountering an error of Error sending the query 'myCollection' TypeError: Converting circular structure to JSON. This might be helpful adding jest.config.js:

module.exports = {
  setupFiles: ['<rootDir>/jest.setup.js'],
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/$1',
    '^~/(.*)$': '<rootDir>/$1',
    '^vue$': 'vue/dist/vue.common.js'
  },
  moduleFileExtensions: ['ts', 'js', 'vue', 'json'],
  transform: {
    '^.+\\.ts$': 'ts-jest',
    '^.+\\.js$': 'babel-jest',
    '.*\\.(vue)$': 'vue-jest',
    '\\.(gql|graphql)$': ['@jagi/jest-transform-graphql']
  },
  collectCoverage: true,
  collectCoverageFrom: [
    '<rootDir>/components/**/*.vue',
    '<rootDir>/pages/**/*.vue',
    '<rootDir>/plugins/**/*.vue',
    '<rootDir>/store/**/*.vue'
  ]
}
jcjp commented 3 years ago

Okay I found out what my issue with the unit test was. I still used your library you are correct it is version mismatch to make the type error go away. The data and error was empty because we incorrectly implemented the GraphQL query the correct query should be like this:

<script lang="ts">
  import {Vue, Component} from 'nuxt-property-decorator'
  import myCollectionQuery from '~/graphql/queries/myCollection.gql'

  @Component({
    apollo: {
      myCollection: {
        query: myCollectionQuery,
        error(error) {
          this.error = error.message
        }
      }
    }
  })
  export default class Index extends Vue {
    error = ''
  }
</script>

And my test file should be like this:

it('renders the list of product when query is resolved', async() => {
    createComponent()
    await wrapper.vm.$nextTick()
    expect(wrapper.vm.myCollection).toStrictEqual(myCollectionMock.data.myCollection)
})

Your mock data in the unit test should match the schema of the data of the GraphQL result. So let us say this is your myCollection.gql schema:

query MyCollection {
  myCollection {
    items {
      name
      description
    }
  }
}

your mock in your unit test file should be like this:

const myCollectionMock = {
  data: {
    myCollection: {
      items: [
        {
          name: 'Name 111',
          description: '111 description'
        },
        {
          name: 'Name 222',
          description: '222 description'
        },
        {
          name: 'Name 333',
          description: '333 description'
        }
      ]
    }
  }
}

We should now close this issue, thank you for your library!

Mike-Gibson commented 3 years ago

Glad to hear you managed to get it working