블로그 이미지
윤영식
Full Stacker, Application Architecter, KnowHow Dispenser and Bike Rider

Publication

Category

Recent Post

2013. 3. 20. 23:18 Testing, TDD/Test First

  TDD 또는 BDD로 진행을 하는 것은 소프트웨어의 명세를 만들어 가면서 보다 안정적인 서비스를 만드는 지름길이다. 물론 좀 더 시간을 투자해야하는 지름길이지만 나중의 유지보수를 생각하면 긴안목에서 역시 빠른 지름길이다. 특히 모바일 서비스의 경우는 몇번의 에러가 발생하면 사용자는 해당 앱을 삭제하고 절대로 사용하지 않는다는 보고가 있다. 그리고 페이스북에는 QA조직이 없고 개발자가 직접 모든 테스트를 수행하고 책임을 지도록 되어있다. 

  우리가 생산성을 위하여 프레임워크를 사용하듯, 안정성을 위해서는 테스트 코드가 필수이다. 또한 테스트 코드에 주석을 달고 Groc 같은 유틸로 문서화를 한다면 소프트웨어의 명세를 따로 하지 않아도 될 것이다. 

  백본기반의 개발에 있어서도 모델, 뷰, 라우터등의 TDD는 필요하며 이를 어떻게 하는지 따라해 보자



1) TDD 준비하기

  - backbone-mocha.zip 파일을 다운로드 받는다 

backbone-mocha.zip


  - tests/test-runner.html 을 수행한다 

  


  - test-runner.html 파일 이해

<!DOCTYPE html>

<html lang="en">

<head>

    <!-- Title &amp; Meta -->

    <title>Frontend tests</title>

    <meta charset="utf-8">


    <!-- Stylesheets -->

    <link rel="stylesheet" href="libs/mocha/mocha.css">

</head>

<body>


    <!-- mocha 결과 화면을 뿌리기 위한 div --> 

    <div id="mocha"></div>


    <!-- Testing Libraries 첨부 -->

    <script src="libs/mocha/mocha.js"></script>

    <script src="libs/chai/chai.js"></script>

    <script src="libs/sinon/sinon.js"></script> <!-- 본 예제에서는 sinon.js 안씀. 즉, 삭제가능 -->


    <!-- chai를 사용하고 mocha는 TDD를 사용한다. BDD는 bdd 소문자로 입력 

          TDD를 하면 mocha에서 suit(), setup(), teardown(), test() 4가지 메소드를 사용한다 

          첨부파일의 user.test.js 파일 참조


          chai는 should, expect, assert 방식을 선택할 수 있는데 여기서는 expect 방식을 선택

    -->

    <script>

        // Use the expect version of chai assertions - http://chaijs.com/api/bdd

        var expect = chai.expect;


        // Tell mocha we want TDD syntax

        mocha.setup('tdd');

    </script>


    <!-- 사용하는 Libs -->

    <script src="../libs/jquery/jquery-1.8.3.min.js"></script>

    <script src="../libs/underscore/underscore-min.js"></script>

    <script src="../libs/backbone/backbone-min.js"></script>


    <!-- 코딩한 원본 Source files -->

    <script src="../src/app.js"></script>

    <script src="../src/models/user.js"></script>

    <script src="../src/views/profile.js"></script>


    <!-- 개발한 소스와 맵핑되는 Test files -->

    <script src="models/user.test.js"></script>

    <script src="views/profile.test.js"></script>


    <!-- 최종적으로 mocha 를 수행한다 -->

    <script>

        mocha.run();

    </script>


</body>

</html>



2) Backbone Model TDD

  - tests/models/user.test.js 밑에 getFullName 테스트 코드 첨부

    + suite, setup, teardown, test : Mocha interface

    + expect : Chai interface

suite('User Model', function() {


    setup(function() {

        this.user = new app.models.User({

            first_name: "Yun",

            last_name: "DoWon"

        });

    });


    teardown(function() {

        this.user = null;

    });


    test('should exist', function() {

        expect(this.user).to.be.ok; // Tests this.user is truthy  <- chai 코드

    });


    test('calling getFullName should return first_name[space]last_name', function() {

        expect(this.user.getFullName()).to.equal('Yun DoWon');

    });


});

  

   - 호출하면 빨간색 Fail 발생

  - 에러 해결하여 초록 Green 으로 만들기위해 user.js 안에 getFullName 메소드 구현

(function(global, _, Backbone, undefined) {


    app.models.User = Backbone.Model.extend({

        getFullName: function() {

               // model 내역은 user.test.js의 setup에서 이미 설정해 놓았음

        return this.get('first_name') + " " + this.get('last_name');

        }

    });


})(this, _, Backbone);


  - 브라우져 호출 결과 : "User Model" 제목은 user.test.js 에서 지정한 제목임

  



3) Backbone View TDD

  - test/views 폴더 밑에 profile.test.js 파일을 만든다 

  - 다음 코드를 넣는다

suite('Profile View', function() {

 

    // Create a User model to pass into our view to give it data

    var model = new app.models.User({

        first_name: 'Yun',

        last_name: 'YoungSik',

        age: 23

    });

 

    setup(function() {

        this.profile = new app.views.Profile({

            // Pass in a jQuery in memory <div> for testing the view rendering

            el: $('<div>'), 

            

            // Pass in the User model 

            // dependency inversion makes this simple to test, 

            // we are in control of the dependencies rather 

            // than the view setting them up internally.

            model: model 

        });

    });

 

    teardown(function() {

        this.profile = null;

    });

 

    test('should exist', function() {

        expect(this.profile).to.be.ok;

    });

 

});


  - 브라우저에서 호출하기전 test-runner.html 에 profile.js 와 profile.test.js 를 넣는다

    <!-- Source files -->

    <script src="../src/app.js"></script>

    <script src="../src/models/user.js"></script>

    <script src="../src/views/profile.js"></script>


    <!-- Test -->

    <script src="models/user.test.js"></script>

    <script src="views/profile.test.js"></script>


  - 브라우져 호출 결과

  - src/views 폴더에 profile.js 파일을 생성하고 다음 코드는 넣고 다시 호출한다

(function(global, $, _, Backbone, undefined) {

 

    app.views.Profile = Backbone.View.extend({

 

    });

 

})(this, $, _, Backbone);

    

  - 정상 호출됨  

 


  - profile.test.js 안에 구현하고 싶은 다음 테스트 코드를 넣고 호출한다

    test('should exist', function() {

        expect(this.profile).to.be.ok;

    });

    // view 에 대한 render 테스트 : Regex 로 해당 내역이 화면출력으로 나왔는지 테스트

    test('render()', function() {

        this.profile.render();

     

        // 렌더링된 결과화면에 대하여 정규표현식으로 검사를 하는 것이 핵심!

        expect(this.profile.$el.html().match(/Yun/)).to.be.ok;

        expect(this.profile.$el.html().match(/YoungSik/)).to.be.ok;

        expect(this.profile.$el.html().match(/23/)).to.be.ok;

    });


  - profile.js 의 내역을 구현지 않았기 때문에 테스트에 대한 결과는 빨간색 fail 

  


  - profile.js 의 render() 메소드를 구현한다 

(function(global, $, _, Backbone, undefined) {

    app.views.Profile = Backbone.View.extend({

 

        render: function() {

            var html = "<h1>"+ this.model.getFullName() +"</h1>" +

                "<p>"+ this.model.get('first_name') +" is "+ this.model.get('age') + " years old";

            this.$el.html(html);

        }

 

    });

})(this, $, _, Backbone); 


  - 브라우져 호출 결과

  

 


테스트 코드를 만들고 실행하여 에러(빨간색)가 발생하면 테스트 코드에 대응하는 업무 코드를 짜고, 다시 실행하여 정상(녹색)적으로 돌아가게 한다. 정상에는 두 단계가 있는데, 비즈니스 로직이 없이 API와 정적값을 통해 정상(녹색) 통과를 하고 나면 이후 비즈니스 로직을 넣고 다시 정상(녹색) 통과를 하는지 체크한다. 



<참조> 

  - 원문 : Testing Backbone.js with mocha 

  - http://visionmedia.github.com/mocha/

  - http://chaijs.com/

posted by 윤영식