Search results for 'angular test'

Angular Test Tips (Mock)

2019. 6. 1. 15:35

한동안 개발 잉여력이 떨어져서 오래 쉬었네요.

회사에서 바쁘면 집에서 개발을 덜하게 됩니다.

개발을 천천히 하니 글 쓸 일을 더욱 없다는게...

 

그렇다해서 지금 바쁘지 않다는건 아니지만.

더 밀리면 안쓰고 넘어갈 것 같아서 기록합니다.

기록하다보면 더 정리되는 기분도 있구요.

 

TDD를 연습하자는 취지로 진행중인데,

소스가 많아질 수록 점점 Test가 어려워집니다.

Angular 또한 예외는 아니구요.

 

그 어려움을 덜어낸 방법 2가지 공유합니다.

 

1. Component Mocking

 

자식(Child) 컴포넌트를 포함하는

부모(Parent) 컴포넌트를 테스트하다보면

점점 테스트 설정이 무거워집니다.

 

부모 컴포넌트의 의존성 뿐 아니라

자식 컴포넌트의 의존성을 모두 주입해야하니까요.

그래서 자식컴포넌트를 Mock으로 주입하고

자식의 의존성은 생략해버리는 것이 유용합니다.

 

제 프로젝트는 아래와 같은 포함관계가 있습니다.

Budget > Calendar > Day Component

Budget 컴포넌트의 테스트가 비대해지는 중이죠.

그래서 Calendar 컴포넌트를 Mock을 했습니다.

의존성도 덜어냈구요.

그 방법은 아래와 같습니다.

 

1) Mock 컴포넌트의 선언

 

테스트(spec.js)에 가짜(Mock) 컴포넌트를 선언합니다. 실제와 동일한 selector를 가지도록 말이지요. Input, Output과 같이 테스트가 필요한 부분은 포함합니다.

...
@Component({
  selector: 'app-calendar',
  template: 'mock calendar'
})
class MockCalendarComponent {
  @Input('date') date: Date;
  @Output('dateChange') dateChange: EventEmitter = new EventEmitter();
}

 

2) Mock 컴포넌트의 사용

 

하위 컴포넌트를 추가해주었던 declarations에 선언해준 Mock 컴포넌트를 넣습니다.

describe('BudgetComponent', () => {
  let component: BudgetComponent;
  let fixture: ComponentFixture;
  let testableDailyExpenseState: Observable;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [BudgetComponent, MockCalendarComponent],
      imports: [
      ...
      ])]
    }).compileComponents();
  }));
  ...

 

이제 Calendar나 Day 컴포넌트의 의존성은 Budget 컴포넌트의 테스트에 더이상 필요가 없습니다.

자식 컴포넌트를 위한 설정 코드들도 덜어집니다.

 

2. Override Private Property

 

사실 이 방법은 좋은 방법은 아닙니다.

부득이하게 쓸 경우가 있어서 정리해둡니다.

NGXS를 쓰다보니 테스트가 난해하더군요.

Store 값을 변경하기가 까다로워서요. 

 

Selector로 가져오는 값이 private이기 때문.

Javascript의 Object.defineProperty를 이용했습니다. 이미 component에 정의된 속성을 수정하고 테스트용 값을 넣어주는 방식입니다.

beforeEach(() => {
    ...
    Object.defineProperty(component, 'dailyExpense$', { writable: true });
    let dailyExpenseState = {
      //test data
      ...
    }
    testableDailyExpenseState = of(dailyExpenseState)
    component.dailyExpense$ = testableDailyExpenseState;
    ...
  });

위와 같이 component의 private 속성에

원하는 테스트 값을 넣는 것이 가능합니다.

 

위와 같은 방법을 이용하여 Angular의 테스트를 조금 더 수월하게 하였습니다.

 

Angular 테스트 코드가 점점 무거워지고,

수많은 의존성과 설정에 불편하신 분들께

도움이 되었으면 좋겠습니다.

 

전체 소스는 아래 github에서 보실 수 있습니다.

소스주소: https://github.com/jsrho1023/account-book

'IT Tech > Angular' 카테고리의 다른 글

Angular Dialog(Material Design)  (0) 2019.01.01
Angular Component Communication (Output)  (0) 2018.12.16
Angular Component Communication (Input)  (0) 2018.12.02
Angular Test Headless  (0) 2018.11.18
Angular Pipes  (0) 2018.11.12

TechTrip IT Tech/Angular

Angular Test Headless

2018. 11. 18. 20:59

Angular의 테스트가 좀 번거롭게 느껴졌습니다.

매번 Chrome 브라우저가 뜨는 것이 귀찮았죠.


그리고 한 번만 실행하고 싶었습니다.

기본적으로는 코드 변경 시마다 반복하거든요.


그리고 가장 귀찮았던건 진행률...

깔끔하게 표출되지 않습니다.

길고 지저분하게 찍혀요.

에러가 나면 로그 보기도 어렵게말이죠.


가장 귀찮았던 것의 해결방법부터 보죠.


1. Progress 제거


사실 매우 간단하게 해결되었습니다.

ng test에 옵션이 있거든요.

바로 --no-progress 옵션

ng test --no-progress 

위 명령어를 통해 해결!


2. 한 번만 실행


이것도 옵션으로 해결될 줄 알았습니다.

그런데 잘 안되더군요.

문서의 --single-run 이란 옵션

그런데 동작하지 않습니다.

Unknown Option이란 경고만...


그래서 karma.conf.js 파일을 수정합니다.

singleRun이란 속성값을 true로 바꿉니다.

옵션 변경을 통해 해결!


3. 브라우저 실행 안함


두 가지 방법이 있었습니다.

1) Headless Chrome을 사용하는 방법

2) Phantom.js를 이용하는 방법


저는 후자가 마음에 들더라구요.

jest를 이용할 때 Phantom을 써서 그런지..

반가운 마음도 ^^


사용방법은 간단합니다.


1) dependency 설치

npm install --save-dev karma-phantomjs-launcher

위 명령어로 설치해줍니다.


2) karma.conf.js 수정

아래와 같이 변경해주면 됩니다.

아래 코드에 highlight 된 부분이 중요한 부분이에요.

module.exports = function (config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine', '@angular-devkit/build-angular'],
    plugins: [
      require('karma-jasmine'),
      require('karma-phantomjs-launcher'),
      require('karma-jasmine-html-reporter'),
      require('karma-coverage-istanbul-reporter'),
      require('@angular-devkit/build-angular/plugins/karma')
    ],
    client:{
      clearContext: false
    },
    coverageIstanbulReporter: {
      dir: require('path').join(__dirname, 'coverage'), 
      reports: [ 'html', 'lcovonly' ],
      fixWebpackSourcePaths: true
    },
    angularCli: {
      environment: 'dev'
    },
    reporters: ['progress', 'kjhtml'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['PhantomJS'],
    singleRun: true,

    phantomjsLauncher: {
      exitOnResourceError: true
    }
  });
};

require('karma-phantomjs-launcher')

browsers: ['PhantomJS']


바로 요 두 부분의 코드입니다.


아래는 깔끔해진 콘솔에서의 테스트 모습이에요.


테스트 실행 화면


브라우저 테스트가 번거로우셨거나

테스트 콘솔이 길어서 불편하셨던 분은

저처럼 바꿔보시는 걸 추천드립니다.


CI 구성하시는 분들은 필수!

*CI(Continuous Integration)


'IT Tech > Angular' 카테고리의 다른 글

Angular Component Communication (Output)  (0) 2018.12.16
Angular Component Communication (Input)  (0) 2018.12.02
Angular Pipes  (0) 2018.11.12
Angular Router (NGXS)  (0) 2018.11.05
Angular Router  (0) 2018.10.31

TechTrip IT Tech/Angular

Angular 테스트(Test) 하기

2018. 4. 11. 22:00

Angular의 테스트에 대해 보고자 합니다.

여지껏 테스트는 안해왔는데요.

(테스트할 코드가 많지 않았기도했지만...)


Angular는 Jasmine이라는 테스트 프레임웍을 사용합니다.

Jasmine은 테스트를 이해하기 쉽게 정리하는 것을 돕습니다.


아래 예제를 참고해보도록 하죠.

describe('BudgetComponent', () => {
  ...
  it('should create', () => {
    expect(component).toBeTruthy();
  });
  ...
}


테스트 코드의 일부를 가져와봤습니다.

it은 무엇이 테스트 되는지를 보여줍니다.

describe는 여러개의 it을 묶어서 설명합니다.


또한 Jasmine은 테스트를 편리하게 하기 위한 API를 제공하죠.

예를들어 아래와 같은 메서드들이 있습니다.

expect().nothing();
expect(thing).toBe(expected);
expect(result).toBeDefined();
expect(result).toBeGreaterThan(3);
expect(result).toBeLessThan(0);
expect(thing).toBeNaN();
expect(result).toBeNull();
expect(thing).toBeTruthy();
expect(array).toContain(anElement);
expect(string).toContain(substring);
expect(bigObject).toEqual({"foo": ['bar', 'baz']});
...


메서드의 이름만 봐도 어떤 걸 테스트하는지 이해가 됩니다.

더 많은 메서드들은 아래 출처에서 참고하시면 되구요.


Angular 테스트를 수행하려면 아래와 같은 명령어 하나면 됩니다.

ng test


위와 같이 실행하면 브라우저에 아래와 같은 화면이 뜹니다.


Angular Test 실행화면



Karma라는 툴이 실행됨을 알 수 있는데요.

Jasmine 테스트를 브라우저로 쉽게 수행하는 걸 돕습니다.

Jasmine 테스트를 위해 html을 작성해야하는데요.

Karma가 대신하도록 Angular에 이미 설정이 되어있습니다.


그럼 실제 테스트 코드를 보도록 하죠.

Budget Component를 테스트하는 코드입니다.

budget.component.spec.ts 파일이에요.

테스트 코드는 spec 파일에 작성하게 됩니다.

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MatInputModule } from '@angular/material/input';
import { MatTableModule } from '@angular/material/table';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { BudgetComponent } from './budget.component';

describe('BudgetComponent', () => {
  let component: BudgetComponent;
  let fixture: ComponentFixture;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ BudgetComponent ],
      imports: [ MatInputModule, MatTableModule, BrowserAnimationsModule ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(BudgetComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('should have budget value', () => {
    const budgetComponent: HTMLElement = fixture.nativeElement;
    let titleElement = budgetComponent.querySelector(".budget-total");
    expect(titleElement.textContent).toEqual("Today's Budget: ₩ 20000");
  })
});


먼저 BudgetComponent가 사용하는 모듈들이 import되어야 합니다.

그리고 it이 수행되기 전 필수 작업들이 beforeEach에서 수행되죠.

컴포넌트를 컴파일하고 화면을 그리는 작업입니다.


'should create'라는 테스트는 기본으로 생성됩니다.

component가 생성 되는지를 테스트하는 것이지요.

빠진 모듈이 있으면 should create부터 실패합니다.


저는 budget value가 셋팅이 되는지를 테스트했습니다.

fixture.nativeElement로부터 화면을 가져올 수 있어요.

budget-total 클래스를 가진 h3 요소의 값을 검사했습니다.

20000으로 설정된 값이 표출되고 있는지를요.

간단한 테스트죠.


더 복잡하고 다양한 테스트 스킬이 존재합니다.

경험해보면서 공유할 가치가 있는 것들은 공유할게요.


코드가 변하면서 테스트도 변해야합니다.

TDD(Test Driven Development)가 한창 화두일 때가 있었죠.

테스트를 먼저 작성하고 코딩을 하는 방식인데요.

저는 적절히 섞어서 작업해나갈 예정입니다.


소스주소: https://github.com/jsrho1023/account-book


출처:

https://angular.io/guide/testing

https://jasmine.github.io/tutorials/your_first_suite

'IT Tech > Angular' 카테고리의 다른 글

Angular Service 만들기  (2) 2018.05.13
Angular Domain Model  (0) 2018.04.24
Angular Material Table  (0) 2018.04.02
Angular Material Icon  (0) 2018.03.18
Angular Material Header Toolbar  (0) 2018.03.11

TechTrip IT Tech/Angular