typescript 使用Pinia服务

xiozqbni  于 2023-03-04  发布在  TypeScript
关注(0)|答案(1)|浏览(169)

我有一个处理API通信的服务(应该是唯一做实际API调用的文件)。我在我的商店里使用这个服务有一些问题。
我现在的逻辑是这样的:
为了交换服务以获得更好的可测试性,服务通过一个prop引入到Vue组件中。我的问题是,我找不到一个好的方法来告诉商店使用这个服务,所以我创建了一个可组合的useService来"存储"服务,然后可以导入并在商店中使用。

// component.vue
const props = defineProps<{
  service: ServiceInterface
}>()

// Get the composable and set the service
const actualService = useService()
actualService.setService(props.service)

// Use the store
const store = useStore()
// useService.ts
const service = ref<ServiceInterface>()

export const useService = () => {
  const setService = (newService: ServiceInterface) => {
    service.value = newService
  }

  return {
    service,
    setService
  }
}
// useStore.ts

// import composable
const { service } = useService()

export const useStore = defineStore('storeName', {
  actions: {
    async fetchData(id: string) {
      // Ensure service is set
      if (!service.value) {
        throw new Error('No service')
      }

      // Use service
      service.value.getData(id)
    }
  }
})

这个设置确实有效,但是我需要仔细检查服务可用的每个方法,我觉得我把整个设置弄得过于复杂了。有人有什么想法来改进这个流程吗?
我想我正在寻找这样的东西(不工作):

export const useStore = (service: ServiceInterface) => {
  return defineStore('storeName', {
    actions: {
      async fetchData(id: string) {
        service.getData(id)
      }
    }
  })
}
jhdbpxl9

jhdbpxl91#

我看不出有什么理由让服务成为React式的,你希望在测试中能够用模拟来替换它,但是一旦它被示例化了,你就不需要动态地替换它的任何方法(这是你希望它成为React式的唯一原因)
我会选择(service.ts):

export const someCall = (...args) => {
  // make some call here, using optional args
},
export const someOtherCall = (...args) => {
  // make some other call here, using optional args
}

其他任何地方:

import { someCall, someOtherCall } from '@/path/to/service'
// use them directly

或者:

import * as service from '@/path/to/service'
// use service.someCall(args)
    • 注意:**将上面的...args替换为每次调用所需的实际参数。

您可以在测试中轻松替换service.ts

jest.mock('@/path/to/service')

或仅模拟特定方法:

const mockSomeOtherCall = jest.fn()
jest.mock('@/path/to/service', () => {
  ...jest.requireActual('@/path/to/service'),
  someOtherCall: mockSomeOtherCall
})

it('calls someOtherCall', () => {
  mockSomeOtherCall.mockReset()

  // mount component here, then:

  expect(mockSomeOtherCall).not.toHaveBeenCalled()

  // trigger something that should call `someOtherCall` then:

  expect(mockSomeOtherCall)
    .toHaveBeeCalledWith(expectedArgumentValues);
})
    • 注意:**在上面的代码中@/path/to/service是通用的。例如,如果你把service.ts(或.js)放在src/services里面,路径实际上是@/services/service。在一个测试套件中,通过使用jest.mock('@/services/service')(或它的任何更具体的变体),你可以有效地用模拟替换那个模块的实际内容,同时运行那个测试套件中的测试。

相关问题