NestJS 架构和测试 [英] NestJS architecture and testing

查看:57
本文介绍了NestJS 架构和测试的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在使用 NestJS 开展一个项目,在进行测试时,我认为该架构有点麻烦.据我了解,推荐的方法是有两层:控制器和服务.

I am currently working on a project using NestJS and when it comes to testing I think the architecture is bit of a pain. As far as I understand the recommended approach is to have two layers: controllers and service.

控制器在需要时相当容易测试,尽管我不明白测试模块的必要性(服务也是如此):

The controllers is fairly easy to test when needed, although I do not understand the need for testingModules (same goes for services):

const module = await Test.createTestingModule({
  controllers: [MyController]
  providers: [MyService] // jest.mock is used to mock MyService
}).compile();

controller = module.get(MyController)

直接注入我需要的任何模拟似乎相当容易:

It seems fairly easy to inject whatever mock I need directly:

controller = new MyController(new MyService()) // again jest.mock is used to mock MyService

对我来说,后一个例子更短、更容易理解并且准确表达了我想要测试的内容.它不会测试我的依赖注入是否有效,而是(至少在我们的应用程序中)在启动时由 NestJS 检查.

For me the latter example is shorter, easier to understand and expresses exactly what I want to test. It does not test that my dependency injection works, but (at least in our application) that is checked by NestJS at startup.

在测试服务方面,我发现自己对存储库进行了很多的模拟.

When it comes to testing services, I find myself doing a lot of mocking of repositories.

const spy = jest.spyOn(repository, 'save')
  .mockReturnValueOnce(null) // something like jest-when should be introduced

await service.create('Question on Stackoverflow', 'I do not get it...')

expect(spy.mock.calls[0][0]).toEqual({
  title: 'Question on Stackoverflow',
  content: 'I do not get it...',
  comments: [] // no initial comments
})

我发现的每个教程/示例都建议将存储库放入服务中.我曾考虑将它放在控制器(或 GraphQL 的解析器)中,但是当同时使用 Rest 和 GraphQL 时,这感觉不像是正确的职责分离并导致代码重复.对我来说,最好的方法似乎是创建另一个层(或向我的实体类添加函数),然后它负责域/业务逻辑.这很容易测试(没有模拟和承诺)

Every tutorial/example I've found suggests putting the Repository in the service. I've thought about putting it in the controller (or resolver for GraphQL), but is just doesn't feel like the right separation of responsibilities and leads to duplication of code when both Rest and GraphQL is used. For me the best approach seems to be creating another layer (or add functions to my entity classes), which would then be responsible for the domain/business logic. This is easy to test (no mocks and no promises)

const question = Domain.create('Question on Stackoverflow', 'I do not get it...')
// or const question = Question.create('Question on Stackoverflow', 'I do not get it...')

expect(question).toEqual({
  title: 'Question on Stackoverflow',
  content: 'I do not get it...',
  comments: [] // no initial comments
})

然后服务将只负责 I/O,这似乎是一个很好的关注点分离.

The service would then only be responsible for I/O which seems like a great separation of concerns.

我现在的问题是:这对 NestJS 有意义吗?似乎这不是建议的方法.也许我遗漏了一些重要的东西,或者我让架构变得更加复杂.

My question is now: does this make sense when it comes to NestJS? It seems like this is not the suggested way to do it. Maybe I am missing some important stuff or maybe I am complicating the architecture more that necessary.

提前感谢您的意见
安德烈亚斯

Thanks for your inputs in advance
Andreas

推荐答案

做类似事情的能力

const myController = new MyConstroller(new MyService());

理论上很棒,但是一旦您的服务开始依赖其他服务(Redis、TypeORM、应用程序中的其他服务),它很快就会变得很少使用

is great in theory, but once your service starts having dependencies on other services (Redis, TypeORM, other services in your application) it can quickly become handful to work with

const myController = new MyController(
  new MyService(
    new RedisService(redisOptions),
    new MyEntityReposiotry(myEntityRepoOptions),
    new OtherService(new RedisService(redisOpitons))
  )
);

并且确保您可以在制作控制器之前将这些设置为变量,这样您就可以实例化更少的类,但是您仍然需要自己进行大量这些注入.

and sure you can set these to variables before making your controller so you're instantiating less classes, but you're still having to make a lot of these injections yourself.

使用 Nest 架构和测试类,您可以快速执行类似操作

With the Nest architecture and Test class you can do something quick like

beforeEach(async () => {
  const module = await Test.createTestingModule({
    controllers: [MyController],
    providers: [
      {
        provide: MyService,
        useValue: myServiceMock
      }
    ],
  }).compile();
});

现在您可以轻松地获得对我的服务的模拟的引用,您可以定义服务函数的一般结构和默认返回值,并且仍然可以使用 获取对服务和控制器类的引用module.get(MyService|MyController).

And now you easily have a reference to what your mock for my service is, you can define the general structure and default return values of the service functions, and can still get the reference to the service and controller class with module.get<MySerivce|MyController>(MyService|MyController).

当然,你总是可以使用 jest.mock() 来启动一个基本的存根并模拟你认为合适的返回值,但请记住这会如何影响测试的外观.NestJS 的吸引力之一是它 固执己见,而且它有自己喜欢的外观.

Of course, you can always use jest.mock() to start a basic stub and mock the return values as you see fit, but keep in mind how that can affect the look of your tests. One of the draws of NestJS is that it is opinionated and it has a certain way it likes to look.

如果您想查看示例,您可以查看此 repo一堆针对不同类型项目的测试样本,包括 rxjs、typeorm 和 graphql.

If you'd like to see examples, you an check out this repo with a bunch of test samples for different types of projects, including rxjs, typeorm, and graphql.

这篇关于NestJS 架构和测试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆