??xml version="1.0" encoding="utf-8" standalone="yes"?>亚洲AV无码无限在线观看不卡,亚洲成人黄色在线观看,亚洲一区二区三区在线http://www.tkk7.com/raimundox/知天之所为,知h之所,至矣。知天之所,天而生也;知h之所,以其知之所知以d知之所不知Q终其天q而不中道夭者:(x)是知之盛也?/description>zh-cnFri, 09 May 2025 18:35:39 GMTFri, 09 May 2025 18:35:39 GMT60Add primitive Progression Enhancement support of Railshttp://www.tkk7.com/raimundox/archive/2009/03/18/260475.htmlRaimundoxRaimundoxWed, 18 Mar 2009 06:38:00 GMThttp://www.tkk7.com/raimundox/archive/2009/03/18/260475.htmlhttp://www.tkk7.com/raimundox/comments/260475.htmlhttp://www.tkk7.com/raimundox/archive/2009/03/18/260475.html#Feedback0http://www.tkk7.com/raimundox/comments/commentRss/260475.htmlhttp://www.tkk7.com/raimundox/services/trackbacks/260475.htmlCurrently the concept of Progressive Enhancement is getting hotter and hotter. It emphasizes accessibility, semantic markup, and the importance of separating the complex rich interaction logic into well modularized javascript files. It's really a usefully way of thinking about how to modularize and manage web presentation components. But the Rails framework doesn't have good support for PE, so we have to define our own convention and helpers to make our life easier.

Usually, I'd like to organize js files in the Rails convention, which means we'll have something like this:

app
  |
  - views
      |
      - admin
          |
          _  new.html.erb
          -  index.html.erb
public
  |
  - javascripts
       |
       - admin
           |
           - new.js
           - index.js

And new.js looks similar to:
$(document).ready(function() {
  enhanceInteractionOnElements();
  
});

function helper_methods() {
  
}


Then, add the follow method to ApplicationHelper module:

def page_javascript_include_tag
  file 
= "#{params[:controller]}/#{params[:action]}.js"
  File.exist
?("#{RAILS_ROOT}/public/javascripts/#{file}"? javascript_include_tag(file) : ""
end

this method will look for js file for a particular page. And in you layout file, add one line in the head sectin:
<%= page_javascript_include_tag %>

That's it. Whenever you request an action of a particular controller, it will find and include the PE js files automatically. Now we've very very primitive support of PE in Rails framework now.

 


 


Raimundox 2009-03-18 14:38 发表评论
]]>
A very brief introduction to Aurumhttp://www.tkk7.com/raimundox/archive/2007/09/05/143029.htmlRaimundoxRaimundoxWed, 05 Sep 2007 15:21:00 GMThttp://www.tkk7.com/raimundox/archive/2007/09/05/143029.htmlhttp://www.tkk7.com/raimundox/comments/143029.htmlhttp://www.tkk7.com/raimundox/archive/2007/09/05/143029.html#Feedback0http://www.tkk7.com/raimundox/comments/commentRss/143029.htmlhttp://www.tkk7.com/raimundox/services/trackbacks/143029.html Aurum is a Ruby-based LALR(n) parser generator that you can use to develop your own domain specified languages, scripting languages and programming languages.Although it's just yet another parser generator, Aurum is slightly different from other widely used parser generators:

  1. One of major targets of Aurum is to simplify external DSL development, espectually Ruby external DSL.
  2. Aurum uses incremental LALR(n) algorithm instead of the common used LALR(1)/Full LALR(n) algorithm. That means:
    •   Allowing the user to express grammars in a more intuitive mannar.
    •   Making it easier to handle complicated grammars. For exmaple, COBOL(LALR(2 or 3)), simplified nature language(LALR(3+)) and etc.
    •   Closer to Generalized LR in language recognizing but much more faster.
    •   Smaller parsing table comparing to Full LALR/LR(n) algorithm.
  3. Aurum supports grammar reuse, and itslef'll be shipped with some pre-defined common structures. One of the pain points of external DSL is that you have to re-define lots of common structures, such as if statements, block structure and etc. With Aurum, you could simply reuse them.
  4. Aurum uses a Ruby interal DSL as meta-language, and provides a generic lexer/parser as well. You could test your grammar by the comprehensive testing libraries Ruby has(you could even develop your lexer/parser in the TDD fashion).
  5. As the name suggested, Aurum, the Latin word for Gold, is partially inspired by the GOLD Parsing System. The grammar you created with Aurum could be completely independent of any implementation language,even Ruby.(not implemented yet :) )

Ok, let's start from the 'Hello World in Compiler Construction' —?Expression Evaluation

 1 require 'aurum'
 2 
 3 class ExpressionGrammar < Aurum::Grammar
 4   tokens do
 5     ignore string(' ').one_or_more     # <= a
 6     _number range(?0?9).one_or_more  # <= b
 7   end
 8 
 9   precedences do  # <= c
10     left '*''/'
11     left '+''-'
12   end
13 
14   productions do # <= d
15     expression expression, '+', expression {expression.value = expression1.value + expression2.value} # <= e
16     expression expression, '-', expression {expression.value = expression1.value - expression2.value}
17     expression expression, '*', expression {expression.value = expression1.value * expression2.value}
18     expression expression, '/', expression {expression.value = expression1.value / expression2.value}
19     expression '(', expression, ')'        do expression.value = expression1.value end # <= f
20     expression _number                     {expression.value = _number.value.to_i}
21     expression '+', _number                {expression.value = _number.value.to_i}
22     expression '-', _number                {expression.value = -_number.value.to_i}
23   end
24 end

If you has any experience with other compiler compiler/parser generator, you probably could understand what happens above quite easily. Instead of explaining things like token, character class, and production, I'd like to emphasise some Aurum conventions:
  1. At point a, we use 'ignore' directive to declare the ignored pattern, such as whitespaces etc.'string' is one of the helper methods(others are enum, range and concat), which is used to define lexical patterns. It will create a pattern matching the given string exactly.
  2. At point b, we declare a lexical token named '_number'. In Aurum, lexical tokens, or terminals from syntax perspective, always start with '_'. The expression '_token_name pattern' is equivalent to 'match pattern, :recognized => :_toke_name'. The 'match' directive is a common way to associate lexical action with leixcal pattern.
  3. At point c, we declare operator precedences of the Expression grammar.The eariler the operators definied, the higher precedence they will have.
  4. At point d, we declare syntax rules of Expression grammar. According to Aurum naming convention, all terminals should start with '_' while all nontermainls start with lower case alphabet character. String literals will be interpreted as reserve words, and added to lexer automatically.
  5. At point e, we define a semantic action to the Addition rule. In semantic action, you could access to the objects in value stack via the name of corresponding symbols.If there are more than one symbol with the same name, you could differentiate them by the order they appered in the production.
  6. At point f, we use do..end instead of {..}. Using Ruby internal DSL as meta-langauge is a double-side sword, you have to bear its flaws while enjoying the remaining parts. There is no perfect world, isn't it?

Now, let's find out how we could use this expression grammar. You could use the helper method as below(it will recalcuate lexical table and parsing table for every call, could be quite slow):

1 puts ExpressionGrammar.parse_expression('1+1').value

or use the lexical table and parsing table to create your own lexer & parser:
 
1   lexer = Aurum::Engine::Lexer.new(ExpressionGrammar.lexical_table, '1+1')
2   parser = Aurum::Engine::Parser.new(ExpressionGrammar.parsing_table(:expression))
3   puts parser.parse(lexer).value


At the end of this post, I'd like to give another grammar example coming from Martin Fowler's HelloParserGenerator series:

 1 require 'aurum'
 2 
 3 Item = Struct.new(:name)
 4 
 5 class Catalog < Aurum::Grammar
 6   tokens do
 7     ignore enum(" \r\n").one_or_more
 8     _item range(?a,?z).one_or_more
 9   end
10 
11   productions do
12     configuration configuration, item {configuration.value = configuration1.value.merge({item.value.name => item.value})}
13     configuration _                   {configuration.value = {}}
14     item 'item', _item                {item.value = Item.new(_item.value)}
15   end
16 end
17 
18 config = Catalog.parse_configuration(<<EndOfDSL).value
19   item camera
20   item laser
21 EndOfDSL
22 
23 puts config['camera'].name


P.S.:The post is based on the developing version of Aurum(0.2.0). You could get it from the svn repository.
P.S.P.S.: There is a more complicated example in the examples directory, a simple Smalltalk interpreter. Have fun:)


Raimundox 2007-09-05 23:21 发表评论
]]>
A very brief introduction to Aurumhttp://www.tkk7.com/raimundox/archive/2007/09/05/143028.htmlRaimundoxRaimundoxWed, 05 Sep 2007 15:12:00 GMThttp://www.tkk7.com/raimundox/archive/2007/09/05/143028.htmlhttp://www.tkk7.com/raimundox/comments/143028.htmlhttp://www.tkk7.com/raimundox/archive/2007/09/05/143028.html#Feedback0http://www.tkk7.com/raimundox/comments/commentRss/143028.htmlhttp://www.tkk7.com/raimundox/services/trackbacks/143028.html
Aurum是一个用Ruby实现的LALR(n) parser generatorQ是的,又是一个parser generatorQ,不过它和其他一些广泛应用的parser generator相比略有不同的:(x)

1.Aurum的主要目标之一Q是化external DSL的开发(其是ruby external DSLQ?br /> 2.Aurum采用增量LALR(n)法Q而不是通常的LALR(1)。这意味着Q?br />   a.不必׃LALR(1)能力的限Ӟ而改写语法,很多在LALR(1)中冲H的语法在LALR(n)中可以比较自然地表达?br />   b.׃识别能力的增强,可以处理一些比较复杂的语法Q比如COBOL(LALR(2)或LALR(3))Q比如一些简化的自然语言(LALR(3+))?br />   c.处理能力接近Generalized LRQ却快很?br />   d.比vFull LALR/LR(n)Q增量算法生成的语法表更?br /> 3.Z化external DSL实现的考虑QAurum支持语法重用?br /> 4.Aurum采用Ruby internal DSL作ؓ(f)语法声明的元语言Q可以利用Ruby丰富的测试框Ӟ有效地对~译Q解释/分析器进行测试?br /> 5.正如名字所暗示的,AurumQGold的化学名Uͼ(j)的一部分灉|来自GOLD parsing systemQ它?yu)支持独立于q_和语a的编译器开发?br />
好,闲话说Q看一个例子,~译原理中的Hello World —?表达式求|(x)
 1 require 'aurum'
 2 
 3 class ExpressionGrammar < Aurum::Grammar
 4   tokens do
 5     ignore string(' ').one_or_more     # <= a
 6     _number range(?0?9).one_or_more  # <= b
 7   end
 8 
 9   precedences do  # <= c
10     left '*''/'
11     left '+''-'
12   end
13 
14   productions do # <= d
15     expression expression, '+', expression {expression.value = expression1.value + expression2.value} # <= e
16     expression expression, '-', expression {expression.value = expression1.value - expression2.value}
17     expression expression, '*', expression {expression.value = expression1.value * expression2.value}
18     expression expression, '/', expression {expression.value = expression1.value / expression2.value}
19     expression '(', expression, ')'        do expression.value = expression1.value end # <= f
20     expression _number                     {expression.value = _number.value.to_i}
21     expression '+', _number                {expression.value = _number.value.to_i}
22     expression '-', _number                {expression.value = -_number.value.to_i}
23   end
24 end


如果怽对之前有用过compiler compiler或者parser generator的话Q应该能看个七七八八吧。我大概解释一下:(x)
 a.q里定义?jin)文法空白,也就是被lexer忽略的部分,在通常的语a中,是空格回车换行之cȝ字符Qstring是用于定义lexical pattern的helperҎ(gu)Q出?jin)string之外Q还有range, enum和concatQ;ignore是一个预定义的说明指令,表示若文本匹配给定模式则该文本会(x)被lexer自动忽略Q其格式为:(x)
    ignore pattern {//lexical action}
 b.此处为lexical token声明Q所有lexical token必须以_开_(d)其格式ؓ(f)Q?br />     _token_name pattern {//lexical action}
   q里其实是一个简略写法,{h(hun)?br />     match pattern, :recognize => Q_token_name
 c.此处符优先U声明,支持?右结合运符Q无l合属性运符开发中Q;每一行中所有运符h相同优先U;比它下一行的q算W高?sh)个优先。比如在q个例子中,'*'?/'h相同优先U,但是?+'?-'的优先别高?br />  d.此处法规则声明,所使用的symbol主要有三U,nonterminal(写字母开?Qterminal(其实是lexical tokenQ以_开?和literal(字符串常?Q其中所有literal都会(x)被自动声明ؓ(f)保留字?br />  e.此处定义?jin)一条文法规则(加法Q,以及(qing)对应的semantic action。在semantic action中可以直接通过symbol的名字来获取值栈中的对象。如遇到同名symbolQ则按照出现序q行~号卛_?br />  f.其实q个没啥Q只不过׃我们使用的是Ruby DSLQ所以有时候不能都用{}Q需要do endQ这是一个例子?br />
最后测试一下实际中如何使用定义好的语法Q用helper methodQ注意由于分析表没有~存Q每ơ都?x)重语法表Q仅仅适用于debug mode。)(j)
  puts ExpressionGrammar.parse_expression('1+1').value
或者通过分析表自己构造lexer和parser
  lexer = Aurum::Engine::Lexer.new(ExpressionGrammar.lexical_table, '1+1')
  parser = Aurum::Engine::Parser.new(ExpressionGrammar.parsing_table(:expression))
  puts parser.parse(lexer).value

最后最后,l另外一个例子,是Martin Fowler Blog上的HelloParserGeneratorpd中所用的语法Q?br />
 1 require 'aurum'
 2 
 3 Item = Struct.new(:name)
 4 
 5 class Catalog < Aurum::Grammar
 6   tokens do
 7     ignore enum(" \r\n").one_or_more
 8     _item range(?a,?z).one_or_more
 9   end
10 
11   productions do
12     configuration configuration, item {configuration.value = configuration1.value.merge({item.value.name => item.value})}
13     configuration _                   {configuration.value = {}}
14     item 'item', _item                {item.value = Item.new(_item.value)}
15   end
16 end
17 
18 config = Catalog.parse_configuration(<<EndOfDSL).value
19   item camera
20   item laser
21 EndOfDSL
22 
23 puts config['camera'].name


P.S.:本文是根据Aurum0.2.0写成的,你可以从rubyforge的svn上得到它?br /> P.S.P.S.: 在exmaples目录里有一个更复杂一些的例子Q是一个简单的Smalltalk解释器?br />



Raimundox 2007-09-05 23:12 发表评论
]]>
Erlang Ring Benchmarkhttp://www.tkk7.com/raimundox/archive/2007/08/01/133863.htmlRaimundoxRaimundoxWed, 01 Aug 2007 12:34:00 GMThttp://www.tkk7.com/raimundox/archive/2007/08/01/133863.htmlhttp://www.tkk7.com/raimundox/comments/133863.htmlhttp://www.tkk7.com/raimundox/archive/2007/08/01/133863.html#Feedback4http://www.tkk7.com/raimundox/comments/commentRss/133863.htmlhttp://www.tkk7.com/raimundox/services/trackbacks/133863.html
 1 -module(ring_benchmark). 
 2 -export([start/2]).
 3 
 4 start(N, M) -> 
 5   Pid = create_process(self(), N - 1, M),
 6   time(fun() -> Pid ! start, loop(Pid, M) end).
 7 
 8 time(Fun) -> 
 9   statistics(wall_clock),
10   Fun(),
11   {_,Time} = statistics(wall_clock),
12   io:format("Run : ~w s ~n", [Time/1000]).  
13 
14 create_process(Pid, 0, _) -> Pid;
15 create_process(Pid, N, M) -> create_process(spawn(fun() -> loop(Pid, M) end),  N - 1, M).
16 
17 loop(_, 0-> void;
18 loop(Next, M) -> 
19   receive
20     Message -> Next ! Message,
21     loop(Next, M - 1)
22   end.
23 
24 

有意思是它还有一个第二问Q让你用另外一U熟(zhn)的语言实现同样的功能,发送同样多的消息,也把旉记录下来Q然后写一blog来publish你的l果。其实,大家?j)知肚明Q这Ulightweight process啊,message passing concurrency啊都是Erlang的强,而且实测l果也着实颇为恐怖,一般也没那闲?j)拿别的东西来陪衬一把了(jin)QArmstrong同学自己实现?jin)一个Java versionQ效率大U能差到癑ր吧Q。不q还真有那写不信邪的老大Q?a >用stackless python实现?jin)同L(fng)ring benchmarkQ发现比erlangq快...后来修改代码Lio操作QErlang倒是比stackless python快些Q但也只是一些而已?br>





Raimundox 2007-08-01 20:34 发表评论
]]>
Feng Shui for Standard ML Programmershttp://www.tkk7.com/raimundox/archive/2007/07/10/129436.htmlRaimundoxRaimundoxTue, 10 Jul 2007 13:53:00 GMThttp://www.tkk7.com/raimundox/archive/2007/07/10/129436.htmlhttp://www.tkk7.com/raimundox/comments/129436.htmlhttp://www.tkk7.com/raimundox/archive/2007/07/10/129436.html#Feedback4http://www.tkk7.com/raimundox/comments/commentRss/129436.htmlhttp://www.tkk7.com/raimundox/services/trackbacks/129436.htmlhttp://www.dcs.ed.ac.uk/home/stg/fengshui.ps.gz

今天早上打开Google Readerq见这么一,内容倒也|了(jin)Q不q是bad smell的另一个名字而已Q硬要扯上分水也只能是勉勉强强。不q郁L(fng)是,竟然是个zh的手W,国学不昌实不能不令我辈心(j)忧啊?br>
p.s. 预计未来6个月口头:(x)"你这写当?j)坏了(jin)项目的风?


Raimundox 2007-07-10 21:53 发表评论
]]>
Domain Oriented Web Testing with Selenium & Ruby (Posted@InfoQ China)http://www.tkk7.com/raimundox/archive/2007/06/13/123772.htmlRaimundoxRaimundoxWed, 13 Jun 2007 01:44:00 GMThttp://www.tkk7.com/raimundox/archive/2007/06/13/123772.htmlhttp://www.tkk7.com/raimundox/comments/123772.htmlhttp://www.tkk7.com/raimundox/archive/2007/06/13/123772.html#Feedback1http://www.tkk7.com/raimundox/comments/commentRss/123772.htmlhttp://www.tkk7.com/raimundox/services/trackbacks/123772.html
应用Seleniumq行W(xu)eb试往往?x)存在几个bad smellQ?br>1.大量使用name, id, xpath{页面元素。无论是功能修改、UI重构q是交互性改q都?x)?jing)响到q些元素Q这使得Selenium试变得非常脆弱?br>2.q于l节的页面操作不Ҏ(gu)体现?gu)为的意图Q一D|间之后就很难真正把握试原有的目的了(jin)Q这使得Selenium试变得难于l护?br>3.对具体数据取值的存在依赖Q当个别数据不再合法的时候,试׃(x)p|Q但q样的失败ƈ不能标识功能的缺失,q得Selenium试变得脆弱且难以维护?br>
而这几点直接衍生的结果就是不断地d新的试Q而极地去重构、利用原有测试。其实这C是正常,单元试试写多?jin),也有会(x)有q样的问题。不q比较要命的是,Selenium的执行速度比较慢(相对单元试Q,随着试逐渐的增多,q行旉?x)逐渐增加C可忍受的E度。一l意图不明难以维护的Selenium试Q可以很L地在每次build的时候杀?0分钟甚至2个小时的旉Q在下就有花2个小时坐在电(sh)脑前面等?50个Selenium试q行通过的?zhn)惨经历。因此合理有效地规划Selenium试显得格外的q切和重要了(jin)。而目前比较行之有效的办法Q往大了(jin)_(d)可以叫domain based web testingQ具体来Ԍ是Page Object Pattern?br>
Page Object Pattern里有四个基本概念QDriver, Page, Navigator和Shortcut。Driver是测试真正的实现机制Q比如SeleniumQ比如WatirQ比如HttpUnit。它们懂得如何去真正执行一个web行ؓ(f)Q通常包含像clickQselectQtypeq样的表C具体行为的Ҏ(gu)QPage是对一个具体页面的装Q它们了(jin)解页面的l构Q知道诸如idQ?nameQ?classQxpathq类实现l节Qƈ描述用户可以在其上进行何U操作;Navigator则代表了(jin)URLQ表CZ些不l页面操作的直接跌{Q最后Shortcut是helperҎ(gu)?jin),需要看具体的需要了(jin)。下面来看一个超U简单的例子——测试登录页面?br>
1. Page Object

假设我们使用一个单独的Login Pageq行dQ那么我们可能会(x)登录的操作装在一个名为LoginPage的page object里:(x)

 1 class LoginPage
 2   def initialize driver
 3     @driver = driver
 4   end
 5  
 6   def login_as user
 7     @driver.type 'id=', user[:name]
 8     @driver.type 'xpath=', user[:password]
 9     @driver.click 'name='
10     @driver.wait_for_page_to_load
11   end
12 end


login_as是一个具有业务含义的面行ؓ(f)。在login_asҎ(gu)中,page object负责通过依靠idQxpathQname{信息完成登录操作。在试中,我们可以q样来用这个page objectQ?br>
1 page = LoginPage.new $selenium
2 page.login_as :name => 'xxx', :password => 'xxx'
3 

不过既然用了(jin)rubyQ总要用一些ruby sugar吧,我们定义一个onҎ(gu)来表N面操作的环境Q?br>
1 def on page_type, &block
2   page = page_type.new $selenium
3   page.instance_eval &block if block_given?
4 end

之后我们可以用page object的类名常量和block描述在某个特定页面上操作?jin)?x)

1 on LoginPage do
2   login_as :name => 'xxx', :password => 'xxx'
3 end
4 

除了(jin)行ؓ(f)Ҏ(gu)之外Q我们还需要在page object上定义一些获取页面信息的Ҏ(gu)Q比如获取登录页面的Ƣ迎词的Ҏ(gu)Q?br>
def welcome_message
  @driver.get_text 
'xpath='
end


q样试也可表达得更生动一些:(x)

1 on LoginPage do
2   assert_equal 'Welcome!', welcome_message
3   login_as :name => 'xxx', :password => 'xxx'
4 end

当你把所有的面都用Page Object装?jin)之后,有效地分离了(jin)测试和面l构的耦合。在试中,只需使用诸如login_as, add_product_to_cartq样的业务行为,而不必依靠像idQnameq些具体且易变的面元素?jin)。当q些面元素发生变化Ӟ只需修改相应的page object可以了(jin)Q而原有测试基本不需要太大或太多的改动?br>
2. Assertation

只有行ؓ(f)q够不成试Q我们还要判断行为结果,q进行一些断a。简单回一下上面的例子Q会(x)发现q有一些很重要的问题没有解冻I(x)我怎么判断d成功?jin)呢Q我如何才能知道真的是处在登录页面了(jin)呢?如果我调用下面的代码?x)怎样呢?

1 $selenium.open url_of_any_page_but_not_login
2 on LoginPage {}

因此我们q需要向page object增加一些断a性方法。至,每个面都应该有一个方法用于判断是否真正地辑ֈ?jin)这个页面,如果不处在这个页面中的话Q就不能q行M的业务行为。下面修改LoginPage使之包含q样一个方法:(x)

1 LoginPage.class_eval do
2   include Test::Unit::Asseration
3   def visible?
4     @driver.is_text_present(&& @driver.get_location == 
5   end
6 end


在visible?Ҏ(gu)中,我们通过对一些特定的面元素Q比如URL地址Q特定的UIl构或元素)(j)q行判断Q从而可以得之是否真正地处在某个面上。而我们目前表达测试的基本l构是由onҎ(gu)来完成,我们也就理成章地在onҎ(gu)中增加一个断aQ来判断是否真的处在某个面上,如果不处在这个页面则不进行Q何的业务操作Q?br>
1 def on page_type, &block
2   page = page_type.new $selenium
3   assert page.visible?"not on #{page_type}"
4   page.instance_eval &block if block_given?
5   page
6 end
7 

q个Ҏ(gu)秘地返回了(jin)page对象Q这里是一个比较tricky的技巧。实际上Q我们只惛_用page != nilq个事实来断a面的流转,比如Q下面的代码描述d成功的页面流转过E:(x)

on LoginPage do
  assert_equal 
'Welcome!', welcome_message
  login_as :name 
=> 'xxx', :password => 'xxx'
end
assert on WelcomeRegisteredUserPage

除了(jin)q个基本断言之外Q我们还可以定义一些业务相关的断言Q比如在购物车页面里Q我们可以定义一个判断购物R是否为空的断aQ?br>
1 def cart_empty?
2   @driver.get_text('xpath='== 'Shopping Cart(0)'
3 end

需要注意的是,虽然我们在page object里引入了(jin)Test::Unit::Asseration模块Q但是ƈ没有在断aҎ(gu)里用Q何assert*Ҏ(gu)。这是因为,概念上来讲page objectq不是测试。之包含一些真正的断言Q一则概忉|؜乱,二则Ҏ(gu)使page object变成针对某些场景的test helperQ不利于以后试的维护,因此我们往往們֐于将断言Ҏ(gu)实现Z个普通的q回gؓ(f)boolean的方法?br>
3. Test Data

试意图的体C仅仅是在行ؓ(f)的描qCQ同栯有测试数据,比如如下两段代码Q?br>
 1 on LoginPage do
 2   login_as :name => 'userA', :password => 'password'
 3 end
 4 assert on WelcomeRegisteredUserPage
 5 
 6 registered_user = {:name => 'userA', :password => 'password'}
 7 on LoginPage do
 8   login_as registered_user
 9 end
10 assert on WelcomeRegisteredUserPage


试的是同一个东西,但是昄W二个测试更好的体现?jin)测试意图?x)使用一个已注册的用L(fng)录,应该q入Ƣ迎面。我们看q个试的时候,往往不会(x)兛_(j)用户名啊密码啊具体是什么,我们兛_(j)它们表达?jin)怎样的测试案例。我们可以通过DataFixture来实现这一点:(x)

 1 module DataFixture
 2   USER_A = {:name => 'userA', :password => 'password'}
 3   USER_B = {:name => 'userB', :password => 'password'}
 4 
 5   def get_user identifier
 6     case identifier
 7     when :registered then return USER_A
 8     when :not_registered then return USER_B
 9     end
10   end
11 end


在这里,我们测试案例和具体数据做了(jin)一个对应:(x)userA是注册过的用P而userB是没注册的用戗当有一天,我们需要将d用户名改为邮q时候,只需要修改DataFixture模块可以了(jin)Q而不必修改相应的试Q?br>
1 include DataFixtureDat
2 
3 user = get_user :registered
4 on LoginPage do
5   login_as user
6 end
7 assert on WelcomeRegisteredUserPage

当然Q在更复杂的试中,DataFixture同样可以使用真实的数据库或是Rails Fixture来完成这L(fng)对应Q但是M的目的就是ɋ试和测试数据有效性的耦合分离Q?br>
1 def get_user identifier
2   case identifier
3   when :registered then return User.find '.' 
4   end
5 end


4.Navigator

与界面元素类|URL也是一cL变(sh)难以表达意图的元素,因此我们可以使用Navigator使之与测试解耦。具体做法和Test Data怼Q这里就不赘qC(jin)Q下面是一个例子:(x)

1 navigate_to detail_page_for @product
2 on ProductDetailPage do
3   .
4 end

5. Shortcut

前面我们已经有了(jin)一个很好的基础Q将Selenium试与各U脆׃意图不明的元素分d?jin),那么最后shortcut不过是在蛋糕上面最漂亮的奶油Ş?jin)——定义具有漂亮语法的helperQ?br>
1 def should_login_successfully user
2   on LoginPage do
3     assert_equal 'Welcome!', welcome_message
4     login_as user
5   end
6   assert on WelcomeRegisteredUserPage
7 end

然后是另外一个magicҎ(gu)Q?br>
1 def given identifer
2   words = identifier.to_s.split '_'
3   eval "get_#{words.last} :#{words[0..-2].join '_'}"
4 end

之前的测试就可以被改写ؓ(f)Q?br>
def test_should_xxxx
  should_login_successfully given :registered_user
end


q是一U结论性的shortcut描述Q我们还可以有更behaviour的写法:(x)

 1 def login_on page_type
 2   on page_type do
 3     assert_equal 'Welcome!', welcome_message
 4     login_as @user
 5   end
 6 end
 7 
 8 def login_successfully
 9   on WelcomeRegisteredUserPage
10 end
11 
12 def given identifer
13   words = identifier.to_s.split '_'
14   eval "@#{words.last} = get_#{words.last} :#{words[0..-2].join '_'}"
15 end


最后,试׃(x)变成cM验收条g的样子:(x)

1 def test_should_xxx
2   given :registered_user
3   login_on LoginPage
4   assert login_successfully
5 end

Mshortcut是一个无兛_坏,只关乎想象力的东西,情挥洒Ruby DSL?D

l论

Selenium是一个让人又爱又恨的东西Q错误地使用Selenium?x)给整个敏捷团队的开发节奏带来灾难性的影响。不q值得?jin)幸的是正确C用Selenium的原则也是相当的单:(x)

1.通过脆弱易变的面元素和测试分dQ得页面的变化不会(x)Ҏ(gu)试生太大的影响?br>2.明确指定试数据的意图,不在试用用Q何具体的数据?br>3.一切可能,明确地表辑և试的意图,使测试易于理解?br>
当然Q除?jin)遵循这几个基本原则之外Q用page object或其他domain based web testing技术是个不错的选择。它们将?x)帮助你更容易地控制Selenium试的规模,更好地^衡覆盖率和执行效率,从而更加有效地交付高质量的Web目?br>
鸣谢

此文中涉?qing)的都是我最q三周以来对Selenium试q行重构时所采用的真实技术。感谢Nick Drew帮助我清晰地划分?jin)Driver, Page, Nagivator和Shortcut的层ơ关p,它们构成我整个实늚基石Q感谢Chris LeishmanQ在和他pairing programming的过E中Q他帮助我锤g(jin)Ruby DSLQ还有Mark Ryall和AbhiQ是他们W一ơ在目中引入了(jin)Test Data FixtureQ得所有h的工作都变得单v来?br>


Raimundox 2007-06-13 09:44 发表评论
]]>
The Keyword 'end' Drives Me Crazyhttp://www.tkk7.com/raimundox/archive/2007/05/11/116864.htmlRaimundoxRaimundoxFri, 11 May 2007 12:32:00 GMThttp://www.tkk7.com/raimundox/archive/2007/05/11/116864.htmlhttp://www.tkk7.com/raimundox/comments/116864.htmlhttp://www.tkk7.com/raimundox/archive/2007/05/11/116864.html#Feedback1http://www.tkk7.com/raimundox/comments/commentRss/116864.htmlhttp://www.tkk7.com/raimundox/services/trackbacks/116864.html
1 while @productions.inject(false) {|c, p| c |= !nullable?(p.nonterminal) && p.symbols.all? {|s| nullable? s} && @nullables << p.nonterminal}

注意1不是行号...q句用的statement modifier, 1是我能想到的最ruby语句?..

p.s.
我现在已l恢复到OO保护模式?..刚才q求短小q了(jin)头的同时Q发C(jin)ruby bulid-in object的一个陷?..
a = Array.new 5, []
[[],[],[],[],[]]
a[0] << 1
[[1],[1],[1],[1],[1]]

想不到华丽的Array直接假设传进ȝ都是值对象了(jin)Q好Ҏ(gu)也调个dup?..




Raimundox 2007-05-11 20:32 发表评论
]]>
I'm Smalltalk, Which Programming Language are You?http://www.tkk7.com/raimundox/archive/2007/05/02/115053.htmlRaimundoxRaimundoxWed, 02 May 2007 09:48:00 GMThttp://www.tkk7.com/raimundox/archive/2007/05/02/115053.htmlhttp://www.tkk7.com/raimundox/comments/115053.htmlhttp://www.tkk7.com/raimundox/archive/2007/05/02/115053.html#Feedback3http://www.tkk7.com/raimundox/comments/commentRss/115053.htmlhttp://www.tkk7.com/raimundox/services/trackbacks/115053.html
Which Programming Language are You?

p.s. q个可能不准...因ؓ(f)李默同学竟然是Lisp...怎么可能...


Raimundox 2007-05-02 17:48 发表评论
]]>
X-Fileshttp://www.tkk7.com/raimundox/archive/2007/03/30/107563.htmlRaimundoxRaimundoxFri, 30 Mar 2007 14:52:00 GMThttp://www.tkk7.com/raimundox/archive/2007/03/30/107563.htmlhttp://www.tkk7.com/raimundox/comments/107563.htmlhttp://www.tkk7.com/raimundox/archive/2007/03/30/107563.html#Feedback3http://www.tkk7.com/raimundox/comments/commentRss/107563.htmlhttp://www.tkk7.com/raimundox/services/trackbacks/107563.html
1. IEC61970 Metadata: Electricity Power Trading System

当时刚上班,team里有一个Doamin知识很厉害的清华的博士,毕业的论文就是电(sh)力市(jng)场,而清华又是国家引入IEC61970的五家之一。所以他很超前的把这两个东西l合在一P做成?jin)一个系l。说实话Q刚?jin)解IEC61970的时候,我是相当的震撼的Q有赶上那时候MDA风气刚vQIEC61970又是同时MOF(Meta Object Facility)和RDF basedQ华丽得不行。一下子我就变成?jin)一个MDA guyQ一个metadata guy...以至于,在BJUG最初的2q里QMDA/MOF/Metadata成ؓ(f)?jin)主旋?..

2. IEC61970 & CWM(Common Warehouse Metamodel) & Office Plugin : Data Warehouse Integration System

q是q今为止Q我最不愿意回忆的一个项?..因ؓ(f)Office Plugin...动辄蓝屏的遭遇让我心(j)有余(zhn)?..q是一个backend是J2EEQfrontend?Net的office插gpȝQ主要是报表...两边都用CWM作ؓ(f)数据l一的Ş?..基本上做C半我的意志就崩溃?..

3. DB Migration/Refactoring : Jyxpearl

q个目...是李默同学的U房最爱,从大学一直做?jin)很久,改版无数?..当时没有q么行的好词,什么DB Migration啊,DB Refactoring啊,那时候我们统U导数据...我导?jin)好多?x)...基本上线一回导一?..时至今日...李默同学L不无得意的说Q你看,你DB Migration的能力就是我培养?..

4. JMI(Java Metadata Interface) & Eclipse RCP : Multi/Rich Client ERP Product

q个team其实挺华丽的Q老栾的品经理,李默是开发经理,资深行业专家(人家实际做过生U长QMRPIIQERP都是人家玩剩下的)老齐做需求,俺是ArchitectQ还有动物园里的猪Senior DevQ我认识Z工能力W一交互设计能力W一的米c_做UI和交互。由于当时看?jin)netbeans和sun的官方JMI实现得太玩具。我们决定从自己的JMI实现开始,pȝl构要求多客L(fng)QwebQrcp都要...所以是轻http协议的b/sQc/s。结构还是不错的Q过E李默和我当然是敏捷?jin)。似乎一起都完美的时候,是要坏菜的时?..企业事业部解散了(jin)...

5. Java Communication & Eclipse RCP : IC Card Reader

上面那个目解散之后Q我跟李默赋闲在Ӟ有不忍心(j)打扰政府Q自谋生路找的项?..q个目要用IC卡读卡器Qؓ(f)?jin)锻炼我们的Eclipse RCP能力Q我们决定用eclipse rcp来做。于是问题就出来?..IC卡怎么办?google一把发现天无绝Z?..Java有一个Communication包,可以q接serial port...不过当时tricky的是...我的本子没有串口Q我们买?jin)一个串口到usb的{换器...发现Ҏ(gu)不能?..于是只好跑到李默家用他华丽的台式机(q厮当年誓言旦旦的说Qlaptop太慢Q一定要用台式机Q东借西借搞?jin)?G RAM SATAQL意,q是伏笔Q的机器Q。我当时p得,Java的这个东西基本就是充数的Q貌似完全没有h用过Q文档啥的都特少...只能自己摸烦(ch)。在l历?jin)无数次p|之后Q终于成功了(jin)。在showcase那天的上午,我最后实验了(jin)d什么的Q都没问题。兴高采烈的把jar拷到优盘?sh),刚插到usb口上...只见一道闪?..机器黑了(jin)...据李默后来分析是L烧了(jin)...我说没事Q拿上硬盘,土一点也不媄(jing)响showcase。李默说...q个...SATA?..q(sh)行?..我绿...此后很长旉Q我都怀疑是我跟李默同学范冲Q超U项目杀?..

6. RDF, Semantic Web, SparQL : Ontology-Relationship DB Mapping

q是在一家公司做产品Q当时我元数?MDA领域颇有U篏...跟这家公司做得类|p来负责研发本体到关系数据库的映射...兼带在D2RQ的基上实C个SparQL查询语言。怎么?..听上d华丽?..到现在我都认为,q个目是我最有潜力的牛皮Q不定那天web x.0?jin),我也老了(jin)Q我可以拉着朋友的手去吹牛b?05q我做semantic web,O/R mapping知道不?Ontology啊,你们啊,sometime too simple"...不过估计q一天还早得很呢

7. Agile Domain Specified Language : Goodhope

q个也是李默同学有䆾的项?..话里的敏捷DSL实践...不过说实话,也有点X...

Raimundox 2007-03-30 22:52 发表评论
]]>
Agile 101: CoC & Why Agile is Hardhttp://www.tkk7.com/raimundox/archive/2007/03/30/107375.htmlRaimundoxRaimundoxThu, 29 Mar 2007 22:53:00 GMThttp://www.tkk7.com/raimundox/archive/2007/03/30/107375.htmlhttp://www.tkk7.com/raimundox/comments/107375.htmlhttp://www.tkk7.com/raimundox/archive/2007/03/30/107375.html#Feedback4http://www.tkk7.com/raimundox/comments/commentRss/107375.htmlhttp://www.tkk7.com/raimundox/services/trackbacks/107375.html要我_(d)q些么是不真的了(jin)解敏捷开发,没有认识到敏捷开发的革命性,只是用外在的形式来把它和其他Ҏ(gu)q行?jin)比较。有又或者是实施敏捷Ҏ(gu)的时候不dQ所以四处碰壁以至于搞v?jin)修正主义。最可怕的是某些大公司,看敏L(fng)?jin),L包装一下,到底q是要卖产品。敏捯Y件开发就是一个革命性的Ҏ(gu)Q只不过它要颠覆的不仅仅是低质量的Y件开发方式,更重要的是,它要颠覆软g生企业和Y件的使用企业之间的生产关p!!q一点在敏捷宣言里写得再明白不过?br />
Customer collaboration over Contract negotiation

敏捷软g开发,是要以一U更合理的共赢的合作关系Q代替以前畸形的采购式的合约关系。ؓ(f)什么合U关pd是畸形的Q我们来看看合约双方的处境?br />
首先软g团队斚w承担?jin)过多的风险Q业务变化,改代码!Q商业抉择{换,改代码!Q凭啥你甲方的缘故非要我承担额外的成本?你说我冤不冤Q冤Q但是h家甲方也冤!Qh家花?jin)大把的银子Q拿C堆不能用的YӞ你要是硬件h家还能{手卖炚wQ,像你要砍树(wi)别hl你把铲子,你要U树(wi)人家l了(jin)你把锯。搁你,你也不愿意。且不说博弈Q就双斚w有心(j)把事情做好,按合同来Q甲方不qԌ不按合同来,乙方不干Q最后变成“有?j)杀贼无力回天”,大家一h扯皮{二期算?jin)。lose-loseQ没有赢家?br />
那么合作的关pL什么呢Q合作的关系好比你去subwayC明治Q面包你自己选,要什么肉你来?xi),蔬菜QcheeseQ酱汁你也自q着办。技术我来,口味你选。技术失败我负责Q口味不合适你负责。你做你的强Ҏ(gu)来我的强,最l大安高兴兴嘻d哈不吵不闹,作出一可口午。这是时候,生关系变(sh)(jin)Q我不是你的冷冰冰的供应商,你也不是我邪恶的客户Q我们是拴在一根子上的蚂蚱。成功是我们的,p|也是我们的。荣׃共,携手q肩。听着有点耳熟Q没错,SaaS。敏捷宣a早就说了(jin)QCoC啊。从供应商变成服务商Q从服务商变成战略合作伙_(d)q是在给软g企业指出路,新的生关系已经在其中?jin)?br />
如果看不清敏L(fng)q个Ҏ(gu)革命点,以ؓ(f)q是开发方法的打闹Q那么敏h本实施不成。这话一般我不敢说的Q程序员自发实施敏捷Q只在一U情况下可能成功Q大企业的IT部门。再赶上个强力的IT领导Q自家h嘛,有什么不好谈的。一来二去,成功了(jin)Q看看C3Q说白了(jin)不就是IT部门和业务部门?Q但是,如果是做目的公司,你营销手段不改变,敏捷׃可能成功。你的客戯你不是合作关p,你通过敏捷增加质量Q符合性质量)(j)的工作就不会(x)被h可,那么׃能成为投资,只能是成本。当成本增加C可承担的时候,敏捷׃?jin)?jin)之了(jin)。ؓ(f)什么好多h说老板没有响应Q旧的生产关pM敏捷Ҏ(gu)是负担?br />
说道q里Q说一下以敏捷d的ThoughtWorks。其实很多h都以为ThougtWorks只有Ҏ(gu)论咨询,没错我们是有Ҏ(gu)论咨询,但是也有业务模式咨询Q客户业务模式不改变Q他怎么能彻底敏Pq点大家不可不查啊?img src ="http://www.tkk7.com/raimundox/aggbug/107375.html" width = "1" height = "1" />

Raimundox 2007-03-30 06:53 发表评论
]]>
վ֩ģ壺 ޹ۺϾƷĵһ| ..ŷһ| AVһδ| ͺˬӲƵ| Ʒþþþþþþ | þѹƵ| ȫAëƬѿվ| ޾Ʒþþþþο| ޽Сxxxx| 91ѸƵ| ˬˬձƵ| պߵӰ| ŮƷþþþ| 91㽶߹ۿѸ | ɫƷƵ| ޾ƷӰۺ| Ʒɫʹ| 99reƵ| þþþרav| ɫٸ߳18p| רAAAAAAƵ| ִӲˬƵ| avþDz| Ƶ| һƬƵѿ| Ļһ| ëƬڵ| ձ˳ɻҳۿƵ | Ƶ߹ۿ| ˳߹ۿ| ҹ޹Ƭ̨| þWWW˳һƬ| ޾Ʒ޿| Ƶѹۿ| ѧaëƬѹۿ| ޹ƷۺϾþþ| պƵ| ҹľƷ| Ʒާѡ벥鶹 | ҹƷƵ| ޹Ʒþþþվ|