简体   繁体   English

Nest.js Jest 不能模拟测试应用程序,但可以模拟测试控制器

[英]Nest.js Jest cannot mock testing app but can mock testing controller

I have a Jest Nest.js controller test where I can stub the calls to the live database.我有一个 Jest Nest.js 控制器测试,我可以在其中存根对实时数据库的调用。 It works fine.它工作正常。

I would like to have 'stubbed e2e' tests using nests HTTP Server (I am using supertest).我想使用嵌套 HTTP 服务器进行“存根 e2e”测试(我使用的是超级测试)。 However, when I import the AppModule I cannot seem to get jest to override anything.但是,当我导入AppModule我似乎无法开玩笑地覆盖任何内容。

Here is my working controller setup using stubbed classes.这是我使用存根类的工作控制器设置。

describe('Working stubbed controller test', () => {
    let controller;

    beforeAll(async () => {
        const moduleFixture = await Test.createTestingModule({
            controllers: [MyController],
            providers: [
                {
                    provide: ObjectInjectionDependency,
                    useClass: ObjectInjectionDependency
                },
                {
                    provide: '@string/injection/dependency',
                    useClass: DbOjectMockIAmUsing
                },
                {
                    provide: '@another/string/injection/dependency',
                    useValue: '@another/string/injection/dependency',
                },
                {
                    provide: DbConnection,
                    useValue: {
                        selectOne: () => null,
                        selectAll: () => null
                    }
                }
            ]
        })
        .compile();

        controller = moduleFixture.get<MyController>(MyController)
    });

    it('/GET url', async () => {
        const request = {
            method: "GET",
            params: {},
            query: {}
        }

        expect(
            await controller.all(request)
        ).toHaveProperty(
            'data'
        )
    });
});

Here is my unsuccessful attempt at incorporating stubbed classes along with the HTTP server using supertest .这是我使用supertest将存根类与 HTTP 服务器合并的失败尝试。 The stubbed classes are ignored.存根类将被忽略。

describe('Bypassing stubs application test', () => {
    let app;
    let server;

    beforeAll(async () => {
        const moduleFixture = await Test.createTestingModule({
            imports: [AppModule],
            providers: [
                {
                    provide: ObjectInjectionDependency,
                    useClass: ObjectInjectionDependency
                },
                {
                    provide: '@string/injection/dependency',
                    useClass: DbOjectMockIAmUsing
                },
                {
                    provide: '@another/string/injection/dependency',
                    useValue: '@another/string/injection/dependency',
                },
                {
                    provide: DbConnection,
                    useValue: {
                        selectOne: () => null,
                        selectAll: () => null
                    }
                }
            ]
        })
        .compile();

        app = moduleFixture.createNestApplication();
        server = app.getHttpServer()
        await app.init();
    });

    it('/GET roots', async () => {
        expect(
            await request(server).get('/myEndpoint')
        ).toMatchObject({
            'statusCode': 200
        })
    });
});

I tried using the overrideProvider() methods but they didn't work either我尝试使用overrideProvider()方法,但它们也不起作用

const moduleFixture = await Test.createTestingModule({
    imports: [AppModule]
})
.overrideProvider(ObjectInjectionDependency)
    .useClass(ObjectInjectionDependency)
.overrideProvider('@string/injection/dependency')
    .useClass(DbOjectMockIAmUsing)
.overrideProvider('@another/string/injection/dependency')
    .useValue('@another/string/injection/dependency')
.overrideProvider(DbConnection)
    .useValue({
        selectOne: () => null,
        selectAll: () => null
    })
.compile() 

I also tried using Jest to override the classes我也尝试使用 Jest 来覆盖类

Jest.mock('@path/to/dbconnection', () => {
    selectOne: () => null,
    selectAll: () => null
}))

All didn't seem to have any effect.一切似乎都没有任何效果。

I tried spyOn()我试过spyOn()

jest.spyOn(DbConnection, 'selectOne').mockImplementation(() => null);
jest.spyOn(DbConnection, 'selectAll').mockImplementation(() => null);

but I seem to get a strange error但我似乎收到了一个奇怪的错误

No overload matches this call.
  Overload 1 of 4, '(object: typeof DbConnection, method: never): SpyInstance<never, never>', gave the following error.
    Argument of type 'string' is not assignable to parameter of type 'never'.
  Overload 2 of 4, '(object: typeof DbConnection, method: never): SpyInstance<never, never>', gave the following error.
    Argument of type 'string' is not assignable to parameter of type 'never'.ts(2769)

I understand that testing the controller is 'good enough' but I am still curious what I am doing wrong, as I am sure I will find a use case for this testing method in future.我知道测试控制器“足够好”,但我仍然很好奇我做错了什么,因为我相信我将来会找到这种测试方法的用例。

EDIT:编辑:

It turns out I was encountering two problems.结果我遇到了两个问题。 Firstly, for whatever reason, I struggled to have Jest mock a method of a class.首先,无论出于何种原因,我都很难让 Jest 模拟一个类的方法。 As per @Estus Flask's suggestion, I can now at least mock/stub methods根据@Estus Flask 的建议,我现在至少可以模拟/存根方法

import { DbConnection } from '@some/path';

jest.spyOn(DbConnection.prototype, 'selectOne').mockReturnValue(null); 

Secondly, nearly all tutorials explaining to mock using the explicit import path其次,几乎所有教程都解释使用显式导入路径进行模拟

import { DbConnection } from '@some/path';

jest.mock('@some/path');
DbConnection.mockReturnValue(null);

left out the detail about typescripts type checking which I discovered from this answer causing issues.遗漏了我从这个答案中发现的导致问题的打字稿类型检查的细节。

let myMock = <jest.Mock<DbConnection>>DbConnection;

While the initial error described above was different, the type casting and examples from the linked answer solved a lot of confusion.虽然上述初始错误有所不同,但链接答案中的类型转换和示例解决了很多困惑。

Still, given there is a bounty, perhaps someone can explain why the providers array is pretty much ignored when the import array contains the AppModule尽管如此,鉴于有赏金,也许有人可以解释为什么当import数组包含AppModule时, providers数组几乎被忽略

For anyone else coming across this question, the problem (at least in my case) is using对于遇到此问题的任何其他人,问题(至少在我的情况下)正在使用

imports: [AppModule]

From what I can tell, Nest resolves all dependencies itself when using imports and since the AppModule is the module that basically loads all the app dependencies, it seems that for whatever reason, all the classes I give in the providers array are ignored.据我所知,Nest 在使用imports时会自行解决所有依赖项,并且由于AppModule是基本上加载所有应用程序依赖项的模块,似乎无论出于何种原因,我在providers数组中providers所有类都被忽略了。

To solve this I just had to use my original method but call the HTTP server like so为了解决这个问题,我只需要使用我原来的方法,但像这样调用 HTTP 服务器

const response = await request(server).get('myUrl');

So the final structure was所以最终的结构是

describe('Working stubbed controller test', () => {
    let app;
    let server;

    beforeAll(async () => {
        const moduleFixture = await Test.createTestingModule({
            controllers: [MyController],
            providers: [
                ObjectInjectionDependency,
                {
                    provide: '@string/injection/dependency',
                    useClass: DbOjectMockIAmUsing
                },
                {
                    provide: '@another/string/injection/dependency',
                    useClass: AnotherClassIAmUsing,
                },
                {
                    provide: DbConnection,
                    useValue: {
                        selectOne: () => null,
                        selectAll: () => null
                    }
                }
            ]
        })
        .compile();

        app = moduleFixture.createNestApplication();
        server = app.getHttpServer()
        await app.init();
    });

    it('/GET url', async () => {
        const response = await request(server).get('myUrl');
        
        expect(response.body.data).toBe('stuff');
    });
});

Teardown methods not included不包括拆卸方法

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM