NestJS || Testing

Alpha, Orderly·2023년 9월 19일
0

nestJS

목록 보기
7/8

Testing?

  • 하나의 기능을 테스트하기 위해 여러 다른 기능을 사용하게 된다.
  • 이는 하나만 테스트하기엔 좋지 못하다.
  • 그렇기에, 다른 의존성은 가짜로 생성해 테스트를 하게 된다.

Unit Testing

Setup

  • 테스트 관련 파일은 .spec. 을 중간에 붙힌다.
    • auth.service.spec.ts
it('create instance of auth service', async () => {

    // 테스트를 위한 가짜 Service
    const fakeUsersService = {
        find: () => Promise.resolve([]),
        create: (email: string, password: string) => Promise.resolve({id: 1, email, password})
    }

    // 테스트를 위한 DI를 만든다.
    // UsersService 는 테스트 대상이 아니므로 가짜를 만들어 사용한다.
    const module = await Test.createTestingModule({
        providers: [
            AuthService,
            {
            // UsersService 를 필요로 할시 fake값을 사용하기.
                provide: UsersService,
                useValue: fakeUsersService,
            }
        ]
    }).compile();

    // DI로부터 AuthService를 가져옴.
    const service = module.get(AuthService);

    // 제대로 생성되었는지 테스트.
    // 이 과정에서 AuthService는 UsersService를 의존성으로서 필요로 하기에
    // 이 또한 확인이 된다.
    expect(service).toBeDefined();
})

가짜 데이터 Typing

    const fakeUsersService: Partial<UsersService> = {
        find: () => Promise.resolve([]),
        create: (email: string, password: string) => Promise.resolve({id: 1, email, password} as User)
    }
  • 일부만 구현할수 있기에 Partial 로 타입
  • 이 외 부분도 타입에 맞게 작성.

describe()

  • describe("이름", { 테스트들 }) 을 통해 위 it 테스트를 묶을수 있다.

beforeEach()

  • 안에 함수를 집어넣을수 있으며, 모든 테스트 이전에 beforeEach 안의 코드가 실행된다.

전체 다 적용된 코드 예시

describe("AuthService", () => {
    let service: AuthService;

    beforeEach(async () => {
            // 테스트를 위한 가짜 Service
            const fakeUsersService: Partial<UsersService> = {
                find: () => Promise.resolve([]),
                create: (email: string, password: string) => Promise.resolve({id: 1, email, password} as User)
            }
        
            // 테스트를 위한 DI를 만든다.
            // UsersService 는 테스트 대상이 아니므로 가짜를 만들어 사용한다.
            const module = await Test.createTestingModule({
                providers: [
                    AuthService,
                    {
                        provide: UsersService,
                        useValue: fakeUsersService,
                    }
                ]
            }).compile();
        
            // DI로부터 AuthService를 가져옴.
            service = module.get(AuthService);
    })
    
    it('create instance of auth service', async () => {
        expect(service).toBeDefined();
    })
})

Signup 함수를 테스트하는 예시

    it('create new user', async () => {
        const user = await service.signup('email@email.edu', 'password');
        // 해싱이 되어 값이 달라졌는지 확인
        expect(user.password).not.toEqual('password');
        // 제대로 해싱이 되어 점으로 구분이 되었는지 확인
        const [salt, hash] = user.password.split('.')
        expect(salt).toBeDefined()
        expect(hash).toBeDefined()
    })

Integration test

  • 전체적인 기능을 테스트하는 과정.
  • end to end 테스트로도 불린다.
  • npm run test:e2e 로 가능하다.
  • 기능.e2e-spec.ts 로 이름짓는다.

사용법

describe('Authentication System', () => {
  let app: INestApplication;

  beforeEach(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

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

  it('handles a signup request', () => {
    const email = 'asdlkjq@akl.com';

    return request(app.getHttpServer())
      .post('/auth/signup')
      .send({ email, password: 'alskdfjl' })
      .expect(201)
      .then((res) => {
        const { id, email } = res.body;
        expect(id).toBeDefined();
        expect(email).toEqual(email);
      });
  });
});
  • request 를 사용한다.

e2e 테스트에 대해 middleware 설정하기

  • 앱모듈 자체를 이용해 실행 후 테스트하기에 pipe나 cookiesession 을 설정해야 한다.
  • main.ts 가 아닌 앱 모듈에 이들을 설정하면 된다.
@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'sqlite',
      database: 'db.sqlite',
      synchronize: true,
      entities: [User, Report],
    }),
    UsersModule, 
    ReportsModule],
  controllers: [AppController],
  providers: [
    AppService,
    // 앱 파이프를 통해 ValidationPipe를 추가한다.
    {
      provide: APP_PIPE,
      useValue: new ValidationPipe({
        whitelist: true
      })
    }
  ],
})
export class AppModule {
  // 미들웨어를 여기에 실행시킨다.
  // Ex. Cookie Session
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(cookieSession({
      keys: ['anythingyouwant']
    })).forRoutes("*")
    // 모든 라우팅에 사용하도록 지정.
  }
}
profile
만능 컴덕후 겸 번지 팬

0개의 댓글