Unit Testing in Emberjs

I apply the QUnit to my Emberjs project to do unit/integration testing. In unit testing we usually have to mock object/methods to test every condition. I choose SinonJS as my mock library. It’s really easy to use.

Let’s see how to unit testing my components.

Testing html

Create a html that includes qunit,sinon,emberjs and your source codes. Remeber to use debug Emberjs(ember-1.x.x.js) not production(ember-1.x.x.min.js)
Make sure you invoke setupForTesting and injectTestHelpers after Application created.

test.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<head>
  <!-- vendor libs -->
  <script src="js/vendor/jquery-1.9.1.min.js"></script>
  <script src="js/vendor/handlebars-1.0.0.js"></script>
  <script src="lib/ember-1.0.1.js"></script>

  <!-- testing libs -->
  <script src="lib/qunit-1.14.0.js"></script>
  <link rel="stylesheet" href="lib/qunit-1.14.0.css">

  <!-- mock libs -->
  <script src="lib/sinon-1.8.2.js"></script>

  <!-- source codes -->
  <script src="js/components/lobby.component.js"></script>
</head>
<body>
  <div id="application"></div>

  <script>
    var MyApp = Ember.Application.create({
      rootElement: '#application',
    });
    MyApp.setupForTesting();
    MyApp.injectTestHelpers();
  </script>

  <!-- spec codes -->
  <script src="spec/lobby.spec.js"></script>
</body>

Route

I have a lobby route.

LobbyRoute
1
2
3
4
5
MyApp.LobbyRoute = Ember.Route.extend({
  renderTemplate : function(){
    this.render("lobby", { controller: "lobby" } );
  }
});

In QUnit we could use module() to group test cases. test() after module() will be grouped. Then we could add setup and teardown methods. They will be invoked in each test case.

lobby.spec.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
module("LobbyRoute", {
  setup: function() {
    MyApp.reset();
  },
  teardown: function() {
    //do something here.
  }
});

test("should render lobby template", function() {
  Ember.run(this,function(){
    route = MyApp.__container__.lookup('route:lobby');

    sinon.spy(route, "render");
    route.renderTemplate();
    ok(route.render.calledOnce, "render should be calledOnce");
  });
});

Above example uses Ember’s __container__.lookup to find specific component and testing it.
But how we make sure the specified method is called? Use sinon.spy to spy a object/method and check it.

Continue reading →

Refactor Ruby Code by Meta Programming

最近重構了一些自動化部署的程式碼,因為發現程式碼裡面大部份的邏輯是一樣的。 決定使用meta programming來重構這些程式碼順便學習ruby的meta programming。

dynamic define method

看看底下的程式碼,想取得使用者輸入的版號來進行自動部署。

before_refactor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  def get_gc_version_from_user
    puts "Type a version d.d.d or leave blank to get the latest gc:"
    @gc_version = get_user_input
  end

  def get_gv_version_from_user
    return unless @config[:import_gv]
    puts "Type a version d.d.d or leave blank to get the latest gv:"
    @gv_version = get_user_input
  end

  def get_game_combo_version_from_user
    return unless @config[:import_game_combo]
    puts "Type a version d.d.d or leave blank to get the latest game combo:"
    @game_combo_version = get_user_input
  end

After refactor, use array to create methods. define_method is used to define new methods dynamically. do |argument| means your could pass parameters in. instance_variable_set is used to set a instance varaible

after_refactor
1
2
3
4
5
6
7
  ['gc','gv','game_combo'].each do |item|
    define_method("get_#{item}_version_from_user") do |argument|
      return unless argument
      puts "Type a version d.d.d or leave blank to get the #{item}"
      instance_variable_set("@#{item}_version", get_user_input)
    end
  end
Continue reading →

Nginx Configuration

As a web developer, nginx is often used in deployment architecture. Write down the useful nginx configuration examples here.

Intall nginx is very easy and not describe here. Configuration good reference here

底下的設定值是用最基本的nginx install, 沒有額外的module

Basic configuration

/etc/nginx/nginx.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# 最基本的設定,不詳述
user  nginx;
worker_processes  1;
pid        /var/run/nginx.pid;

events {
  worker_connections  1024;
}

http {
  # 載入支援的mime type
  include       /etc/nginx/mime.types;

  # 當有不認識的mime type時就用octet-stream當一般binary檔案處理
  default_type  application/octet-stream;

  # 定義自己的log format
  log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';

  # access log存放位置並且使用 main 這個log format,也就是上述定義的格式
  # buffer=8k 表示不會一直寫log到disk,等到buffer滿了才寫,避免I/O過重
  access_log  /var/log/nginx/access.log main buffer=8k;


  ## Size Limits
  # 當request body超過這個body size時會被先寫進暫存檔處理
  client_body_buffer_size   8k;

  # 限制http header的大小
  client_header_buffer_size 1k;

  # 限制request body大小,超過server會回傳413(Request Entity Too Large)
  client_max_body_size      1m;

  ## Timeouts, do not keep connections open longer then necessary to reduce
  # 讀取request body timeout時間,超過時間會回傳408(Request time out)
  client_body_timeout     4s;

  # 讀取request header timeout時間(e.g GET HTTP/1.1),超過時間回傳408(Request time out)
  client_header_timeout   2s;

  # HTTP 1.1 keepalive的timeout時間, 各家browser各有不同時間.
  keepalive_timeout       65s;

  # 送資料給client的timeout時間(指得是兩次資料讀取時間), 降低這個值可以避免Slowloris DoS attack
  send_timeout            5s;

  ## General Options
  # 設定值 >=1 的話讓NGINX支持下載部份檔。 如果你的server有要讓人下載大檔續傳的話..
  max_ranges 0;

  # keepalive的連線最大數量
  keepalive_requests        5;

  # Browder cache 失效時間, 負值的話表示不要有任何cache.
  expires 1d;

  # Request limits
  # 限制一個IP在1秒內只處理5個requests
  # myzone是自行定義的名稱,用來存放IP, 2m表示最大不超過2M記憶體
  limit_req_zone  $binary_remote_addr  zone=myzone:2m  rate=5r/s;


  # 載入其它設定檔
  include /etc/nginx/conf.d/*.conf;

}

How to reverse proxy?

Continue reading →

Emberjs-data Trying

In my old emberjs project(money,food-ntpc) i didn’t use ember-data to handle data processing with backend server. The ember-data is closed to 1.0.0(now is 1.0.0.beta4) so i give it a try. The official document is quite less and some posts in StackOverflow are out of date. Checkout their source code is the quickest way to understand how they work and do customized. I write down tips here for my reference in the future.

Define your store to use ember-data

1
2
3
4
5
MyAdapter = DS.RESTAdapter.extend

MyApp.Store = DS.Store.extend
  revision: 11
  adapter: MyAdapter

Assume your have a record object and json format is

1
2
3
4
5
6
7
8
{
  "record": {
    "id":"1",
    "amount":150,
    "category_id":1,
    "description":"dinner"
  }
}

Define your model object.

1
2
3
4
MyApp.Record = DS.Model.extend
  category_id: DS.attr 'number'
  amount: DS.attr 'number'
  description: DS.attr 'string'
Continue reading →

UI Automation

This article is the last one in 2013. In the rails project we use capybara to do UI testing. Like click a button/link or fill the form. But i will show how to use capybara without rails in UI automation today.

Sometimes we have to repeat trivial works and these require user to interactive with browser. For instance, upload file to server or input necessary data.

Install below gems and Firefox

1
2
gem install capybara
gem install selenium-webdriver

In orer to use capybara without rails. You need to require capybara manually. In default capybara will launch rack server, change run_server to false. Because we assume your server is running. Use selenium as driver.

1
2
3
4
5
6
7
8
require 'capybara'
require 'capybara/dsl'

Capybara.run_server = false
Capybara.current_driver = :selenium

# it will open the browser to 127.0.0.1:8888/admin
Capybara.app_host = 'http://127.0.0.1:8888/admin'
Continue reading →

Useful Methods in Ruby

This week i write automatic utility tools for colleague. It saves time and reduce trivial works. Learn useful ruby built-in methods.

List all under a folder

1
2
3
4
5
6
7
8
9
10
11
12
13
# lists all files and folders under target/ folder
Dir.glob('target/**/**').select do |f|
  if File.directory? f
    # do something
  else
    # do something
  end
end

# ONLY list all in target/ , doesn't list subfolders like target/subfolders/
Dir.glob('target/**').select do |f|

end
Continue reading →

New Rails Project for Fun

Today i push a new rails project to heroku called lunch. I also push to github. Every week day i and colleague have to discuss which lunch to eat. It will be very helpful if a site can random pick a lunch restaurant for us. So i create a new rails project for this purpose. The basic idea is simple, everybody can share a restaurant and add others’ restaurants to favorites list. Then site pick a restaurant for you. I can study new stuff when i practice rails each time. I try to write down here.

Continue reading →

Open Source Project Studying

I study the open source project gitlab. It’s a well coding rails structure. Here are some examples i think they are good.

Don’t query data in View/Helper

Avoid perform query in view/helper in Xdite’s Maintainable Rails View.

dashboard_controller.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def show
  @projects_limit = 30
  @groups = current_user.authorized_groups.sort_by(&:human_name)
  @has_authorized_projects = @projects.count > 0
  @projects_count = @projects.count
  @projects = @projects.limit(@projects_limit)

  @events = Event.in_projects(current_user.authorized_projects.pluck(:id))
  @events = @event_filter.apply_filter(@events)
  @events = @events.limit(20).offset(params[:offset] || 0)

  @last_push = current_user.recent_push
  respond_to do |format|
    format.html
    format.js
    format.atom { render layout: false }
  end
end
Continue reading →

Mongodb in Rails4

In the past week i had tried mongodb in rails. I rewrite my previous project money to use mongodb instead of active record. The source code is in branch money-mongodb. Here i record the steps of changing active record to mongodb

Continue reading →

Design Pattern in Javascript

I read the javascript design pattern from Addy Osmani. There’re code examples in the book and i try to re-write it by coffeescript. It helps me understand pattern more and clear. During writing i found a good coffeescript cookbook and very helpful. I put the final result to gist