【问题标题】:NestJS - question regarding service-mockingNestJS - 关于服务模拟的问题
【发布时间】:2019-09-14 03:48:07
【问题描述】:

我目前正在用 NestJS 重写我们的普通节点 API 服务器,我遇到了以下问题:我有一个 CacheService,它充当 redis 的包装器,并注入到各种其他服务中。

现在,如果客户端请求包含自定义标头(键:x-mock-redis,值:someRedisMockKey)并且如果服务器在调试模式下运行,而不是调用 redis,则应返回模拟的 json-value (该值是从名为 someRedisMockKey 的文件中读取的)。

我可以将CacheService 的范围设置为“请求”并注入客户端请求,允许我检查模拟头是否存在,如果在调试模式下运行则返回模拟值。 但是我发现这违反直觉,因为我的逻辑违反了单一责任原则,并且不应该在生产模式下运行。我也希望我的CacheService 有默认范围而不是“请求”。

任何建议如何更优雅地做到这一点?

【问题讨论】:

    标签: dependency-injection mocking nestjs


    【解决方案1】:

    在此之前,抱歉,如果我误解了问题或限制,我想会尝试解释它们并指出它应该是什么样子。

    • 生产总是使用 Redis
    • 您可以在不同的端口上设置应用实例,使其与“暂存”(或其他)应用实例完全分离

    如果您能满足第二个条件,您可以使用自定义模块并为您的服务应用不同的客户端包装器(策略):

    Cache module 的自定义提供程序

    import * as redis from 'redis'
    import { INTERNAL_CACHE_CLIENT, INTERNAL_CACHE_MODULE } from './cache.constants'
    import { CacheModuleAsyncOptions, InternalCacheOptions } from './cache.module'
    import CacheClientRedis from './client/cache-client-redis'
    // ...
    
    export const createAsyncClientOptions = (options: CacheModuleAsyncOptions) => ({
      provide: INTERNAL_CACHE_MODULE,
      useFactory: options.useFactory,
      inject: options.inject,
    })
    
    export const createClient = () => ({
      provide: INTERNAL_CACHE_CLIENT,
      useFactory: (options: InternalCacheOptions) => {
        const { production, debug, noCache, ...redisConfig } = options
        // pardon for the ifs ; )
        if (noCache) {
          return new CacheClientInMemory()
        }
        if (production) {
          return new CacheClientRedis(redis.createClient(redisConfig))
        }
    
        if (debug) {
          return new MockedCache()
        }    
    
        return new CacheClientMemory()
      },
      inject: [INTERNAL_CACHE_MODULE],
    })
    

    如前所述,您可以在 CacheClient 周围使用任何包装器,在您的情况下,它将从文件中提供数据。为简单起见,任何缓存客户端实现的接口示例如下:

    export interface CacheClient {
      set: (key: string, payload: string) => Promise<boolean>
      get: (key: string) => Promise<string | null>
      del: (key: string) => Promise<boolean>
    }
    

    现在,由于我们已经让模块决定应该使用哪种策略,服务只需要:

    constructor(
        @Inject(INTERNAL_CACHE_CLIENT) private readonly cacheClient: CacheClient) {
      }
    

    如果它仍然违反原则,或者您真的需要在运行时决定它,请随时指出。

    干杯!

    【讨论】:

    • 感谢您提供有趣的意见。问题是我真的需要决定在运行时使用什么(即我试图描述客户端何时发送特定标头)。有什么想法吗?
    • 会将责任转移到 CacheClient 但它与服务本身非常相似,但是在这种情况下 ClientImplementation 将表现为一种策略:耸耸肩:
    猜你喜欢
    • 2021-06-25
    • 2021-06-05
    • 2019-12-01
    • 2019-04-30
    • 2019-09-04
    • 2020-03-22
    • 2016-06-12
    • 2020-05-06
    • 1970-01-01
    相关资源
    最近更新 更多