Archive for March 2014

Karma: JavaScript test runner


建議跟 grunt 一起用
然後把配置都寫在 Gruntfile.js 裡


$ npm install karma --save-dev

# 產生 karma.conf.js,你可以看一下默認的配置
$ ./node_modules/.bin/karma init


如果是用 karma 的方式跑 jasmine 的話
就不需要 SpecRunner.html
而是改用 Gruntfile.js 裡的 files

in Gruntfile.js

karma: {
    test: {
        options: {
            // base path, that will be used to resolve files and exclude
            basePath: '',
            // start these browsers
            browsers: [
            coverageReporter: {
                // multiple reporters
                reporters: [
                    {type: 'lcov', dir:'coverage/'},
                    {type: 'text'}
            // list of files to exclude
            exclude: [],
            // list of files / patterns to load in the browser
            files: [
                    pattern: 'tests/fixtures/**/*.html',
                    included: false,
                    served: true
            // frameworks to use
            frameworks: [
            plugins: [
            // preprocessors allow you to do some work with your files before they get served to the browser.
            preprocessors: {
                '**/*.html': [],
                // source files, that you wanna generate coverage for
                'dist/pangu.js': [
            // test results reporter to use, possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
            reporters: [
            // continuous Integration mode, if true, it capture browsers, run tests and exit
            singleRun: true



Fixture could not be loaded

用 jasmine-jquery loadFixtures 的時候出現這個錯誤
是因為 karma 的 preprocessors 在作怪

in Gruntfile.js

karma: {
  unit: {
    options: {
      preprocessors: {
        '**/*.html': []


而且 karma 會把檔案放在 base/ 路徑底下
所以 jasmine-jquery 還要額外設定:

in SpecHelper.js

beforeEach(function() {
  var path = '';
  if (typeof window.__karma__ !== 'undefined') {
    path += 'base/'
  jasmine.getFixtures().fixturesPath = path + 'test/fixture/';

Object Permission in Django


django 自己提供的 permission 系統是 "model permission"
那些 add, change, delete 的權限是認 model(也就是該 model 下的所有 objects)
因為這個 permission 系統是用在 admin 裡面的

如果要用 "object permission"
可以用 django-guardian
針對每個 object 設定不同的權限



如果是 superuser
對所有 permission 的 has_perm() 都會是 True

class Label(models.Model):

    user = models.ForeignKey(User, related_name='labels')
    name = models.CharField(_(u'name'), max_length=100)

    class Meta:
        verbose_name = pgettext_lazy(u'DPS model name', u'label')
        verbose_name_plural = pgettext_lazy(u'DPS model name', u'labels')
        permissions = (
            ('view_label', 'View label'),

django 默認會幫每個 model 建立三種 permission(所謂的 codename)

  • add_modelname
  • change_modelname
  • delete_modelname
# permission format: app_label.codename
user.has_perm('sites.change_site')  # by django
user.has_perm('sites.change_site', site)  # by django-guadian

每個 model object 被建立之後是沒有 object permission 的

通常會透過 signal 的方式去做

from django.db.models.signals import post_save
from django.dispatch import receiver

from guardian.shortcuts import assign_perm

@receiver(post_save, sender=Label)
def create_label_permission(instance, **kwargs):
    if not kwargs['created']:

    label = instance

    assign_perm('view_label', label.user, label)
    assign_perm('change_label', label.user, label)

get objects by permission

from guardian.shortcuts import get_objects_for_user

labels = get_objects_for_user(request.user, 'dps.view_label')

ref: Python code coverage 跑 python project 的覆蓋率就是用
nose 也是用
基本上 python 社群幾乎都是用這個



$ pip install coverage


in .coveragerc

[run] 底下的 include 和 omit 是用來指定 filename pattern
而 source 是用來指定 package 或目錄

omit 是「忽略」的意思



# 默認會一併測試所有包含第三方 lib 在內的 Python 程式碼
$ coverage run

# 你可以用 --source 指定只測試某個 packages 或目錄
$ coverage run --source=haul test
$ coverage run --source=haul tests/

# show report in terminal
$ coverage report

# generate html report in the same dir
$ coverage html


Usage with nose

$ pip install nose nose-cov

# output terminal
$ nosetests --with-cov --cov haul tests/

# output html
$ nosetests --with-cov --cov-report html --cov haul tests/