수색…


재스민 테스트 프레임 워크 설치

Angular 2 앱을 테스트하는 가장 일반적인 방법은 Jasmine 테스트 프레임 워크를 사용하는 것입니다. Jasmine을 사용하면 브라우저에서 코드를 테스트 할 수 있습니다.

설치

시작하려면 jasmine-core 패키지 ( jasmine 아님) 만 있으면됩니다.

npm install jasmine-core --save-dev --save-exact

확인

Jasmine이 제대로 설정되었는지 확인하려면 다음 내용으로 ./src/unit-tests.html 파일을 만들고 브라우저에서 엽니 다.

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="content-type" content="text/html;charset=utf-8">
  <title>Ng App Unit Tests</title>
  <link rel="stylesheet" href="../node_modules/jasmine-core/lib/jasmine-core/jasmine.css">
  <script src="../node_modules/jasmine-core/lib/jasmine-core/jasmine.js"></script>
  <script src="../node_modules/jasmine-core/lib/jasmine-core/jasmine-html.js"></script>
  <script src="../node_modules/jasmine-core/lib/jasmine-core/boot.js"></script>
</head>
<body>
   <!-- Unit Testing Chapter #1: Proof of life.  -->
   <script>
     it('true is true', function () {
       expect(true).toEqual(true);
     });
   </script>
</body>
</html>

Gulp, Webpack, Karma 및 Jasmine을 사용한 테스트 설정

우리가 필요로하는 첫 번째 작업은 Webpack 엔진을 위해 설정 한 구성에서 카르마에게 Webpack을 사용하여 테스트를 읽도록 지시하는 것입니다. 여기서 ES6에서 코드를 작성하기 때문에 바벨 (bavelle)을 사용하고 있습니다. Typescript와 같은 다른 맛을 위해 변경할 수 있습니다. 또는 Pug (이전의 Jade) 템플릿을 사용하면 필요하지 않습니다.

여전히 전략은 동일하게 유지됩니다.

그래서 이것은 webpack 설정입니다 :

const webpack = require("webpack");
let packConfig = {
    entry: {},
    output: {},
    plugins:[
        new webpack.DefinePlugin({
            ENVIRONMENT: JSON.stringify('test')
        })
    ],
    module: {
       loaders: [
        {
            test: /\.js$/,
            exclude:/(node_modules|bower_components)/,
            loader: "babel",
            query:{
                presets:["es2015", "angular2"]
            }
        },
        {
            test: /\.woff2?$|\.ttf$|\.eot$|\.svg$/,
            loader: "file"
        },
        {
            test: /\.scss$/,
            loaders: ["style", "css", "sass"]
        },
        {
            test: /\.pug$/,
            loader: 'pug-html-loader'
        },
        ]
    },
    devtool : 'inline-cheap-source-map'
};
module.exports = packConfig;

그런 다음 webpack config를 사용하려면 karma.config.js 파일이 필요합니다.

const packConfig = require("./webpack.config.js");
module.exports = function (config) {
    config.set({
    basePath: '',
    frameworks: ['jasmine'],
    exclude:[],
    files: [
        {pattern: './karma.shim.js', watched: false}
    ],

    preprocessors: {
        "./karma.shim.js":["webpack"]
    },
    webpack: packConfig,

    webpackServer: {noInfo: true},

    port: 9876,

    colors: true,

    logLevel: config.LOG_INFO,

    browsers: ['PhantomJS'],

    concurrency: Infinity,

    autoWatch: false,
    singleRun: true
});
};

지금까지, 우리는 웹팩을 사용하는 업보를 말한, 우리는라는 파일 karma.shim.js에서 시작하는 데 말했습니다. 이 파일은 webpack의 시작점 역할을 할 것입니다. webpack은이 파일을 읽고 importrequire 문을 사용하여 모든 의존성을 모으고 테스트를 실행합니다.

이제 karma.shim.js 파일을 살펴 보겠습니다.

// Start of ES6 Specific stuff
import "es6-shim";
import "es6-promise";
import "reflect-metadata";
// End of ES6 Specific stuff

import "zone.js/dist/zone";
import "zone.js/dist/long-stack-trace-zone";
import "zone.js/dist/jasmine-patch";
import "zone.js/dist/async-test";
import "zone.js/dist/fake-async-test";
import "zone.js/dist/sync-test";
import "zone.js/dist/proxy-zone";

import 'rxjs/add/operator/map';
import 'rxjs/add/observable/of';

Error.stackTraceLimit = Infinity;

import {TestBed} from "@angular/core/testing";
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting} from "@angular/platform-browser-dynamic/testing";

TestBed.initTestEnvironment(
     BrowserDynamicTestingModule,
     platformBrowserDynamicTesting());

let testContext = require.context('../src/app', true, /\.spec\.js/);
testContext.keys().forEach(testContext);

본질적으로 우리는 각 코어 테스트에서 TestBed 를 가져 와서 모든 테스트에 대해 한 번만 시작해야하므로 환경을 시작합니다. 그런 다음 src / app 디렉토리를 반복적으로 통과 하고 .spec.js로 끝나는 모든 파일을 읽고 testContext에 제공하여 실행합니다.

나는 보통 시험을 수업과 같은 장소에 두려고 노력한다. Personat의 취향에 따라 클래스를 사용하여 종속성 및 리팩토링 테스트를보다 쉽게 ​​가져올 수 있습니다. 하지만 src / test 디렉토리 아래와 같이 테스트를 다른 곳에 두려면 여기에 기회가 주어집니다. karma.shim.js 파일에서 마지막 행을 변경하십시오.

완전한. 무엇이 남았나? 아아, 위에서 만든 karma.config.js 파일을 사용하는 꿀꺽 꿀꺽하는 일 :

gulp.task("karmaTests",function(done){
    var Server = require("karma").Server;
    new Server({
        configFile : "./karma.config.js",
        singleRun: true,
        autoWatch: false
    }, function(result){
        return result ? done(new Error(`Karma failed with error code ${result}`)):done();
    }).start();
}); 

지금 우리가 만든 config 파일로 서버를 시작하고, 한 번 실행되도록 지시하고 변경 사항을 감시하지 않습니다. 나는 그들이 시험 할 준비가되어있을 때에 만 시험이 진행될 것이므로 스위트 룸에 더 좋다고 느낍니다.하지만 당신이 달라 지길 원한다면 변경해야 할 곳을 알 수 있습니다.

그리고 마지막 코드 샘플로 여기 Angular 2 튜토리얼 인 "Tour of Heroes"에 대한 테스트 세트가 있습니다.

import {
    TestBed,
    ComponentFixture,
    async
} from "@angular/core/testing";

import {AppComponent} from "./app.component";
import {AppModule} from "./app.module";
import Hero from "./hero/hero";

describe("App Component", function () {

    beforeEach(()=> {
        TestBed.configureTestingModule({
            imports: [AppModule]
        });
    
        this.fixture = TestBed.createComponent(AppComponent);
        this.fixture.detectChanges();
    });

    it("Should have a title", async(()=> {
        this.fixture.whenStable().then(()=> {
            expect(this.fixture.componentInstance.title).toEqual("Tour of Heros");
        });
    }));

    it("Should have a hero", async(()=> {
        this.fixture.whenStable().then(()=> {
            expect(this.fixture.componentInstance.selectedHero).toBeNull();
        });
    }));

    it("Should have an array of heros", async(()=>
        this.fixture.whenStable().then(()=> {
            const cmp = this.fixture.componentInstance;
            expect(cmp.heroes).toBeDefined("component should have a list of heroes");
            expect(cmp.heroes.length).toEqual(10, "heroes list should have 10 members");
            cmp.heroes.map((h, i)=> {
                expect(h instanceof Hero).toBeTruthy(`member ${i} is not a Hero instance. ${h}`)
            });
        })));

        it("Should have one list item per hero", async(()=>
        this.fixture.whenStable().then(()=> {
            const ul = this.fixture.nativeElement.querySelector("ul.heroes");
            const li = Array.prototype.slice.call(
                this.fixture.nativeElement.querySelectorAll("ul.heroes>li"));
            const cmp = this.fixture.componentInstance;
            expect(ul).toBeTruthy("There should be an unnumbered list for heroes");
            expect(li.length).toEqual(cmp.heroes.length, "there should be one li for each hero");
            li.forEach((li, i)=> {
                expect(li.querySelector("span.badge"))
                    .toBeTruthy(`hero ${i} has to have a span for id`);
                expect(li.querySelector("span.badge").textContent.trim())
                    .toEqual(cmp.heroes[i].id.toString(), `hero ${i} had wrong id displayed`);
                expect(li.textContent)
                    .toMatch(cmp.heroes[i].name, `hero ${i} has wrong name displayed`);
            });
        })));

    it("should have correct styling of hero items", async(()=>
        this.fixture.whenStable().then(()=> {
            const hero = this.fixture.nativeElement.querySelector("ul.heroes>li");
            const win = hero.ownerDocument.defaultView ||hero.ownerDocument.parentWindow;
            const styles = win.getComputedStyle(hero);
            expect(styles["cursor"]).toEqual("pointer", "cursor should be pointer on hero");
            expect(styles["borderRadius"]).toEqual("4px", "borderRadius should be 4px");
        })));

    it("should have a click handler for hero items",async(()=>
        this.fixture.whenStable().then(()=>{
            const cmp = this.fixture.componentInstance;
            expect(cmp.onSelect)
                .toBeDefined("should have a click handler for heros");
            expect(this.fixture.nativeElement.querySelector("input.heroName"))
                .toBeNull("should not show the hero details when no hero has been selected");
            expect(this.fixture.nativeElement.querySelector("ul.heroes li.selected"))
                .toBeNull("Should not have any selected heroes at start");

            spyOn(cmp,"onSelect").and.callThrough();
            this.fixture.nativeElement.querySelectorAll("ul.heroes li")[5].click();

            expect(cmp.onSelect)
                .toHaveBeenCalledWith(cmp.heroes[5]);
            expect(cmp.selectedHero)
                .toEqual(cmp.heroes[5], "click on hero should change hero");
        })
    ));
});

여기서 주목할 점은 beforeEach () 가 테스트 모듈을 구성하고 테스트에서 컴포넌트를 생성하는 방법과 detectChanges ()를 호출하여 각도가 실제로 이중 바인딩과 all을 통과하도록하는 방법입니다.

각 테스트는 async ()에 대한 호출이며 fixture를 검사하기 전에 언제나 StStable 이 해결할 것을 기다립니다. 그런 다음 componentInstance를 통해 구성 요소에 액세스하고 nativeElement 를 통해 요소에 액세스합니다.

올바른 스타일링을 점검하는 테스트가 있습니다. 자습서의 일부로 각도 팀은 구성 요소 내부에서 스타일을 사용하는 방법을 보여줍니다. 우리의 테스트에서는 getComputedStyle () 을 사용하여 스타일이 우리가 지정한 곳에서 왔는지 확인했지만 Window 객체가 필요합니다. 테스트에서 볼 수있는 것처럼 요소에서 가져옵니다.

HTTP 서비스 테스트

일반적으로 서비스는 원격 API를 호출하여 데이터를 검색 / 전송합니다. 그러나 단위 테스트는 네트워크 호출을해서는 안됩니다. Angular는 내부적으로 XHRBackend 클래스를 사용하여 HTTP 요청을 처리합니다. 사용자는이 동작을 변경하여 동작을 변경할 수 있습니다. 각도 테스트 모듈은 http 요청을 테스트하고 어설 션하는 데 사용할 수있는 MockBackendMockConnection 클래스를 제공합니다.

posts.service.ts 이 서비스는 게시물 목록을 가져 오기 위해 API 끝점에 posts.service.ts 합니다.

import { Http } from '@angular/http';
import { Injectable } from '@angular/core';
import { Observable }     from 'rxjs/rx';

import 'rxjs/add/operator/map';

export interface IPost {
    userId: number;
    id: number;
    title: string;
    body: string;
}

@Injectable()
export class PostsService {
    posts: IPost[];

    private postsUri = 'http://jsonplaceholder.typicode.com/posts';

    constructor(private http: Http) {
    }

    get(): Observable<IPost[]> {
        return this.http.get(this.postsUri)
                .map((response) => response.json());
    }
}

posts.service.spec.ts 여기서는 http API 호출을 조롱하여 위의 서비스를 테스트합니다.

import { TestBed, inject, fakeAsync } from '@angular/core/testing';
import {
    HttpModule,
    XHRBackend,
    ResponseOptions,
    Response,
    RequestMethod
} from '@angular/http';
import {
    MockBackend,
    MockConnection
} from '@angular/http/testing';

import { PostsService } from './posts.service';

describe('PostsService', () => {
    // Mock http response
    const mockResponse = [
        {
            'userId': 1,
            'id': 1,
            'title': 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',
            'body': 'quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto'
        },
        {
            'userId': 1,
            'id': 2,
            'title': 'qui est esse',
            'body': 'est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla'
        },
        {
            'userId': 1,
            'id': 3,
            'title': 'ea molestias quasi exercitationem repellat qui ipsa sit aut',
            'body': 'et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut'
        },
        {
            'userId': 1,
            'id': 4,
            'title': 'eum et est occaecati',
            'body': 'ullam et saepe reiciendis voluptatem adipisci\nsit amet autem assumenda provident rerum culpa\nquis hic commodi nesciunt rem tenetur doloremque ipsam iure\nquis sunt voluptatem rerum illo velit'
        }
    ];

    beforeEach(() => {
        TestBed.configureTestingModule({
            imports: [HttpModule],
            providers: [
                {
                    provide: XHRBackend,
                    // This provides mocked XHR backend
                    useClass: MockBackend
                },
                PostsService
            ]
        });
    });

    it('should return posts retrieved from Api', fakeAsync(
        inject([XHRBackend, PostsService],
            (mockBackend, postsService) => {
                mockBackend.connections.subscribe(
                    (connection: MockConnection) => {
                        // Assert that service has requested correct url with expected method
                        expect(connection.request.method).toBe(RequestMethod.Get);
                        expect(connection.request.url).toBe('http://jsonplaceholder.typicode.com/posts');
                        // Send mock response
                        connection.mockRespond(new Response(new ResponseOptions({
                            body: mockResponse
                        })));
                    });

                postsService.get()
                    .subscribe((posts) => {
                        expect(posts).toBe(mockResponse);
                    });

            })));
});

각도 구성 요소 테스트 - 기본

구성 요소 코드는 다음과 같습니다.

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  template: '<h1>{{title}}</h1>'
})
export class MyAppComponent{
  title = 'welcome';
}

각도 테스트의 경우 angle은 좋은 테스트 케이스를 작성하는 데 도움이되는 테스트 프레임 워크와 함께 테스트 유틸리티를 제공합니다. 각도 유틸리티는 @angular/core/testing 에서 가져올 수 있습니다.

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MyAppComponent } from './banner-inline.component';

describe('Tests for MyAppComponent', () => {
  
  let fixture: ComponentFixture<MyAppComponent>;
  let comp: MyAppComponent;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [
        MyAppComponent
      ]
    });
  });

  beforeEach(() => {

    fixture = TestBed.createComponent(MyAppComponent);
    comp = fixture.componentInstance;

  });

  it('should create the MyAppComponent', () => {
    
      expect(comp).toBeTruthy();  

  });

});

위의 예에서 컴포넌트 존재에 대한 테스트 케이스를 설명하는 테스트 케이스는 하나뿐입니다. 위의 예제에서 TestBedComponentFixture 와 같은 각도 테스트 유틸리티가 사용됩니다.

TestBed 는 각도 테스트 모듈을 만드는 데 사용되며 configureTestingModule 메소드로이 모듈을 configureTestingModule 하여 테스트하려는 클래스의 모듈 환경을 생성합니다. beforeEach 함수에서 테스트 모듈을 구성하는 이유 인 모든 테스트 케이스를 실행하기 전에 구성 할 테스트 모듈.

TestBed createComponent 메소드는 테스트중인 컴포넌트의 인스턴스를 생성하는 데 사용됩니다. createComponentComponentFixture 반환합니다. Fixture는 컴포넌트 인스턴스 자체에 대한 액세스를 제공합니다.



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow