Wednesday, May 9, 2007

15 TDD steps to create a Rails application




Hi,

Testing Rails applications is my passion for over 7 years now. If you sign up to this newsletter you will receive exclusive information about everything related to Ruby unit testing, Rails acceptance testing, JavaScript testing, testable architectures, TDD, BDD and good OO design. 


  Subscribe to the Testing Rails mailing list


Introduction

Several times recently, I have been asked how to develop a Rails application using the Test Driven Development approach. I'm not an expert here, but I've put together some notes on how to start working on a Rails application whilst being test-driven all the time.
As an example I will use a word-learning web application. The simplest use case is to display a random word object (with its Polish translation) from the database.
Every time we refresh we want to see a different word.

1. Create a new Rails application
rails my_app
cd my_app

Run tests with 'rake test'. It fails due to missing database configuration.

2. Set up the databases - config/database.yml
The code below assumes sqlite3 databases.
development:
adapter: sqlite3
database: db/my_app_development.sqlite

test:
adapter: sqlite3
database: db/my_app_test.sqlite

'rake test' now runs fine.

3. Create a Word class with a corresponding unit test
script/generate model Word

4. Write a unit test for the Word class. Edit the test/unit/word_test.rb.
def test_word_is_english_and_polish
word = Word.new :eng=>'never', :pl=>'nigdy'
assert_equal 'never', word.eng
assert_equal 'nigdy', word.pl
end

'rake test' now fails due to missing words table.

5. Edit db/migrate/001_create_words.rb
We are using a migration here in order to create a table. It's a recommended way of dealing with database changes.

def self.up
create_table :words do |t|
t.column :eng, :string
t.column :pl, :string
end

Word.new(:eng=>'yes', :pl=>'tak').save
Word.new(:eng=>'no', :pl=>'nie').save
Word.new(:eng=>'everything', :pl=>'wszystko').save
end

def self.down
drop_table :words
end

The sample words that we are adding use Word.new .. lines, will be added to the development database. It's important to distinguish the 'test' and 'development' database. The first one is only used during tests. The latter is used by default when you start the application.

Apply the migration with 'rake db:migrate'.

'rake test' now succeeds with the following:
'1 tests, 2 assertions, 0 failures, 0 errors'


6. Fixtures and test for word.random. Edit word_test again.
It's not easy to test a method which behaves randomly. Let's assume that it's enough to test that if we have only two words in our database then one of them should be called at least once per 10 calls.
fixtures :words
def test_random
results = []
10.times {results << Word.random.eng}
assert results.include?("yes")
end
Note the 'fixtures :words' line. Edit the 'words.yml' file.
yes:
id: 1
pl: 'tak'
eng: 'yes'
no:
id: 2
pl: 'nie'
eng: 'no'
This will be loaded to the test database before every run of tests. 7. Implement the Word.random method
def self.random
all = Word.find :all
all[rand(all.size)]
end
Warning: The code above could be slow for many words in a database (we retrieve all words only for selecting a random element). It's good enough for our needs. 8. Generate the Words controller with a 'learn' action
script/generate controller Words learn
9. Write a test for the learn method Just as there is a one-to-one ratio between unit tests and models, so there is between functional tests and controllers. The Controller's responsibility is to retrieve objects from the Model layer and pass them to the View. Let's test the View part first. We use the 'assigns' collection which contains all the objects passed to the View.
def test_learn_passes_a_random_word
get 'learn'
assert_kind_of Word, assigns('word')
end
10. Make the Test Pass
def learn
@word = Word.new
end
11. Write more tests in the words_controller_test How can we test that controller uses the Word.random method? We don't want to duplicate the tests for the Word.random method. Mocks to the rescue! We will only test that the controller calls the Word.random method. The returned value will be faked with a prepared word. Let's install the mocha framework:
gem install mocha
Now we can use 'expects' and 'returns' methods. 'expects' is used for setting an expectation on an object or a class. In this case we expect that the 'random' method will be called. We also set a return value by using 'returns' method. Setting a return value means faking (stubbing) the real method. The real Word.random won't be called. If an expectation isn't met the test fails.
require 'mocha'

def test_learn_passes_a_random_word
random_word = Word.new
Word.expects(:random).returns(random_word)
get 'learn'
assert_equal random_word, assigns('word')
end
'rake test' now fails. The Word.method wasn't called. 12. Rewrite the implementation
def learn
@word = Word.random
end
'rake test' now passes. 13. Test that a word is displayed: Extend the existing test with assert_tag calls.
def test_learn_passes_a_random_word
random_word = Word.new(:pl=>'czesc', :eng=>'hello')
Word.expects(:random).returns(random_word)
get 'learn'
assert_equal random_word, assigns('word')
assert_tag :tag=>'div', :child => /czesc/
assert_tag :tag=>'div', :child => /hello/
end
14. Implement the view - learn.rhtml
<div>
<%= @word.eng %>
<%= @word.pl %>
</div>
15. Manual testing
script/server
Go to 'http://localhost:3000/words/learn'. Refresh several times. Related articles ... and some more TDD steps with Rails Testing Rails controllers with mock objects If you want to read more about testing in Rails go to the Guide To Testing The Rails.

If you read this far you should Follow andrzejkrzywda on Twitter and subscribe to my RSS

60 comments:

Anonymous said...

Your code font is extremely hard to read... really detracts from what might be interesting reading. Perhaps it works on some platforms, certainly not on mine (unix with a reasonable selection of fonts)

Fuzzyman said...

Fine from Windoze. :-)

Anonymous said...

I don't think its a platform specific issue so much as it is a readability issue caused by choice of font, size, colour and background colour.

IMO too many people's blogs are a mess of too-tiny fonts. Case in point, this blog. If you changed your CSS to resize pre.code to 120 or 130%, and most importantly, changed the background and foreground colour, your code display would be far more readable to all - not just those using Cleartext.

pre.code {
font-size: 120%;
border: solid 1px #aaa;
padding: 6px;
background-color: #eee;
color: inherit;
overflow:auto;
margin: 10px 0px;
}

The above or color: #333; is far better IMO.

FYI, unrelated to this issue, your CSS includes various references to a font named "Vedana"; I'm sure you mean "Verdana".

Not all readers have the eyes of a 20 something any more.

Speaking of readability, fuzzyman: waiting for google adverts to pop up before getting to your content does nothing for the readibility of your content. Sorry to be so grumbly but I frequently avoid reading your material because I can't stand waiting for the google ad to clear.

Perhaps the google advert revenue is worth it to you but if that's your primary motivation, why not write directly for ad sales then?

Fuzzyman said...

Do you have a name 'Anonymous'.

As I said before - the combination of font size, family and colour (etc) is perfectly readable on Windows. So there must be *some* platform issue if you're finding it hard to read.

I don't think it is the adsense ads which are slow to load on my blog, I think it is a couple of the other javascript snippets (including amazon which I make very little revenue from - the google adverts I do quite well from).

There is a way to speed them up, simply loading the javascript *after* the content has loaded. I keep meaning to make the change - but haven't yet. Maybe I'll try and make time tonight.

Andrzej Krzywda said...

Anonymous,

Thanks for the css hint. I changed the pre.code style to the one you suggested and it looks fine. I hope it's now better for you.
All the 'Vedana' stuff is generated by blogspot. I didn't change the template except for adding the pre.code style. It's a pain to nicely display any code here.

Anonymous said...

andrzej,

Thanks for the CSS update ... although I didn't intend for you to take my suggestion completely literally, down to the colour choices. The real issue is some combos - green on black might have been popular when display terminals attached to mainframes (and early PC's) had nothing but that to offer, but its never been the most readable, especially for aging eyes.

fuzzyman, as for "its perfectly readable on windows", there is an implied "for me" at the end of that statement.

I'm sure you aren't suggesting that just because *you* can read it (I have windows machines in my test lab, rest assured) that everything must be ok.

There's nothing wrong with the font rendering on my platform; I'd not checked which font had been employed when I first made the comment; its clear that its fg/bg colour combo that is the factor here.

fg/bg colour combos do matter:

http://hubel.sfasu.edu/research/survreslts.html

Generally speaking, dark on light is more readable. Having said that, I code in vim in a light on dark screen, but in order to make it work for me, I had to do some extensive testing on fonts and sizing.

http://www.lighthouse.org/accessibility/effective-color-contrast/

Here's a useful aide: Check the bottom of this post for a helpful bookmarklet which I've just dragged to my own toolbar...

http://www.456bereastreet.com/archive/200608/light_text_on_dark_background_vs_readability/

Fuzzyman said...

Sorry anonymous, I didn't read the bit about your eyes properly when I responded to your comment (although I wish I was still in my twenties)...

Mr Interested said...

Nicely done introduction to testing in Rails. I've been getting familiar with Rails recently but have skipped testing until now. This post served as a perfect "getting feet wet" tutorial.

Andrzej Krzywda said...

Mr Interested,

Thanks for a positive comment! I'm going to blog more about testing.

Antonio said...

Looks shiny, but your example uses assert_tag, whereas nowadays assert_select is typically a much better option (and I believe assert_tag is deprecated).

In this case, I believe the code would be:

assert_select 'div', /czesc/
assert_select 'div', /hello/

But I may be misunderstanding what the assert_tag code is doing (this code checks for a div with content that matches /czesc/, and a div with content that matches /hello/).

Brian said...

Terrific tutorial.

To fix in Step 14: the rhtml is missing ampersands before "word".

matte said...

Thanks for your tutorial! I live very much TDD!

Aaron K. Hawkins said...

I like your post. Testing seems to be easiest when you start with it from day one of an application. I like how your tests drive your development. (Your first step was to call rake test - which told you your database wasn't configured.)

Posts like these will only help increase the practice of writing good tests. Thats great because we all have a vested interests in good tests - you never know when you'll be called upon to work on an application someone else has built.

I carpool with a good friend and we 've made some tshirts that promote good programming practices like testing. Check out this shirt design and let us know what you think.

Happy testing - and thanks for the article.

Anonymous said...

Can you elaborate on how using a code generator fits with the TDD approach of "write the test, let it fail, then write the code to make the test pass", and having that drive the API design and the choice of what application behavior is implemented?

For example, why is the application even trying to connect to a database? What test code has driven the need to add DB code?

Andrzej Krzywda said...
This comment has been removed by the author.
Andrzej Krzywda said...

Aaron,

Cool t-shirts!
It took ma a while to explain my friend (a non-IT person) why a piece of code can be "guilty" :-)

Andrzej Krzywda said...

matte, brian,

Thanks for nice comments!
Typo fixed.

Andrzej Krzywda said...

Anonymous,

I see your point here.

First, this article doesn't cover all aspects of testing an application. Normally, I would start with an acceptance test (acceptance meaning something like a Selenium test). The acceptance test would describe how the user interacts with the system. This is a good place to test (indirectly) that we use something that stores data (like a relational database).

The second part of creating the Words application covers the Word.add_content method which is more related to databases.

As for the code generation, except for the application skeleton (rake file, logs directories) I don't really generate code here. All the script/generator calls generate only stubs for classes (class and method declaration).

Does it answer your questions?

fertile said...

Great tutorial. This is sound advice for starting some good habits with rails. You're right about so many blogs emphasizing the quickness of rails and neglecting the testing.

Tomasz said...

Congrats Andrew!

Thank to this tutor this is my first hand on experience with Rails/Ruby/Rake/Gem/Sqlite/mocha - Oh My! :-)

BuyLevitra said...
This comment has been removed by a blog administrator.
Phentermine said...
This comment has been removed by a blog administrator.
Viagra said...
This comment has been removed by a blog administrator.
Anonimous said...

Excellent website. Good work. Very useful. I will bookmark!

Vitor said...

Hello, Andrzej!
I find your post, as well as your blog, very interesting.
I'm starting to write a blog about rails and TDD as well.
I think your article it would be a great start for me.
My blog is at http://www.vip2web.com and is written in portuguese, so i'm here to ask you if i could translate this post and put into my blog?

Thanks in advance!

Best regards,

Vitor

Andrzej Krzywda said...

Hi Vitor,

Sure, you can translate it to portuguese! Send me a link when it's done :)

Andrzej

viagra said...
This comment has been removed by a blog administrator.
D. said...

On step 5, I'm getting this error:

[root@remandev migrate]# rake db:migrate
(in /usr/local/apache2/htdocs/testrails)
rake aborted!
uninitialized constant CreateWords

(See full trace by running task with --trace)

Just to verify, I'm supposed to replace the entire contents of the file db/migrate/001_create_words.rb with what is in the text field?

- Dave

plush said...
This comment has been removed by a blog administrator.
black mold said...
This comment has been removed by a blog administrator.
Kleidung Klamotten said...
This comment has been removed by a blog administrator.
herbal store online said...

Insomnia... tension... irritability... weight gain... nervousness... fatigue... these are just a few of the uncomfortable and even painful symptoms most quitters experience. Just one or two of these is enough to have most people crying "uncle" and racing toward their hidden "emergency" pack of cigarettes. Once that happens, all hope of quitting smoking is lost. Nicocure® is a new generation aid to help you give up smoking. Nicocure® gives you the best possible chance of success and our workplace trials with thousands of smokers over ten years have demonstrated this. Bowtrol for Sensitive Digestion is an all-natural probiotics system that supports healthy colon function. Bowtrol helps you maintain a healthy

digestive tract, supports regularity, and promotes overall health and wellbeing.
Bowtrol Colon Cleanse is an all-natural herbal colon cleansing system that supports your body's natural design for eliminating

toxins. Gentle, effective colon cleansing today will have you feeling wonderful tomorrow.
Herbal pharmacy store provides You biggest selection of natural herbal products for skin care, male enhancement, stress management, anxiety treatment, weight loss, colon cleasing, quit smoking, pain, menopause relief, hair loss, hemorrhoids, wrinkle treatment, stretch mark removal, sleeping aid, bladder control, human growth hormones, human pheromones, lubricants, celllulite treatment and more. All major credit cards accepted and worldwide delivery!


Colon cleansing treatment product Bowtrol is an all natural herbal colon cleanser medication that has changed hundreds of thousands of people's lives. It's effective whole body safe and effective internal colon cleansing. Colon cleanser Bowtrol is formulated to maximize one's elimination without causing loose stools or uncomfortable cramping via frequent healthy bowel movements while assisting in cleansing the vital organs and lymphatic system.

Anonymous said...

nice web...
Propecia

How Propecia works

Propecia precautions

Propecia instructions

Propecia side effects

Hair loss guide

Hair care handbook

Hair disorders

Hair loss articles

buy silagra said...

Good web..........


rx pharmacy

said...

群馬 ハウスメーカー
埼玉 ハウスメーカー
松山市 不動産
香川県 不動産
輸入雑貨
プレゼント 男の子用
出産祝い
プリンセスルーム
toefl
自動車教習所 東京
ダイビングショップ
ローン
ジュエリー通販
クレジットカード決済
カード決済
カラコン
キャッシュバック 即日
クレジットカード 海外旅行保険
治験ボランティア
電話会議
不動産 東京
医療支援

said...

婚約指輪
結婚指輪
知多半島 ホテル
知多半島 温泉
知多半島 旅館
コンタクトレンズ
カラーコンタクト
カーボンオフセット
ゼネラリ
チューリッヒ
不動産
不動産投資
お見合いパーティー
浮気調査
賃貸
埼玉 不動産
群馬 不動産
海外推广
国际推广
网络营销
网络推广

said...

自動車保険 比較
自動車 保険 見積
出会い
出会い系
出会い系サイト
国際協力
治験
人権問題
盲導犬
自動車 保険 見積
三井ダイレクト
24そんぽ24
スニーカー
フランチャイズ
セルライト
タイ古式マッサージ
ソニー損保

said...

障害者
結婚相談所 横浜
結婚相談所 東京
広島 不動産
アクサダイレクト
野生動物
自動車保険
出会いサイト
アメリカンホームダイレクト
募金
不動産
アスクル

said...
This comment has been removed by a blog administrator.
said...

出会い
投資
データ復旧
出会い系サイト
不動産
コンタクトレンズ
アフィリエイト

said...

派遣
不動産
インプラント
出会いサイト
クレジットカード 現金化
FX

said...

派遣情報サイトには、魅力的なお仕事をたくさん掲載しています。派遣お仕事をお探しの皆さまにとって、より使いやすく便利なサイトにするべく、アデコ派遣情報サイトをリニューアルしました。

said...

FX・外国為替証拠金取引の比較サイト「FX-外為比較.com」では、複数の条件からFX・外国為替会社の比較!また資料請求、口座開設もできます。

said...

美容整形することによって絶対的な美を得られるわけではありません。美容整形『自分は変わった』という事実を物理的に確認することで、気になって仕方がなかった自分 の体に対するコンプレックスから解放される。美容整形そこではじめて心を研ぎ澄まし、自分の内面を磨いていくことができるようになるのです。そうして人は美しく なっていく。美容整形外見だけ磨こうとする人は美しくなれない、というのが私の持論です」

said...

不動産 広島,岡山/四国(香川,徳島,愛媛,高知) 不動産 -あなぶき不産ナビ不動産四国4県、岡山の不動産、不動産広島の不動産など不動産情報検索(マンション・一戸建て・土地・収益物件等)サイトです不動産。穴吹不動産流通株式会社"

said...

外国為替証拠金取引は元本や利益を保証するものではなく、外国為替相場の変動や金利差により損失が生じる場合がございます。外国為替お取引の前に十分内容を理解し、外国為替ご自身の判断でお取り組みください

thanh99 said...

モバイルSEO
携帯SEO
SEO
SEO対策
SEO
携帯サイト 作成
モバイルサイト 作成
携帯ホームページ 作成
高収入 アルバイト
高収入 アルバイト
カップリングパーティー
カップリングパーティー
モバイルSEO
携帯SEO
携帯サイト 作成
モバイルサイト 作成
モバイルホームページ 作成
ウェルカムボード
まつげエクステ
まつげ エクステ
結婚式 ウェルカムボード
電話占い
電話 占い
カップリングパーティー
カップリング パーティー
結婚式
電報
競馬
競馬予想
税理士 東京
税理士東京
札幌競馬場
福島競馬場
東京競馬場
中京競馬場
阪神競馬場
函館競馬場
新潟競馬場
中山競馬場
京都競馬場
小倉競馬場
二人だけの結婚式
写真だけの結婚式
南青山 エステ
エステ 表参道
まつげエクステ
まつげカール
まつげエクステンション
まつげエクステ講習
システムキッチン
ガレージ
水栓
洗面台
ウォシュレット
調査会社
調査 会社
妻 浮気
妻浮気
探偵調査
探偵 調査
夫浮気
夫 浮気
アロマオイル
エッセンシャルオイル
オイル販売
翻訳会社
ビジネス翻訳

VPXL said...

What is best Viagra or Viagra Super Active???
Generic Viagra Super Active
Online Generic Viagra Super Active
Buy viagra super active
Buy fda approved viagra super active
Buy fda approved VPXL
Buy FDA approved tablets viagra super active
Buy fda approved Viagra super active Michigan
Buy generic fda approved tablets
Viagra super active Michigan
Cheap Viagra super active
Buy cheap viagra super active
Viagra super active US
FDA Viagra super active
Viagra super active UK
Buy generic Viagra super active online

Anonymous said...

EH株式会社
エクセルヒューマン
EH株式会社
エクセルヒューマン
EH株式会社
エクセルヒューマン
EH株式会社
エクセルヒューマン
EH株式会社
エクセルヒューマン
EH株式会社
エクセルヒューマン
EH株式会社
エクセルヒューマン
株式会社
エクセルヒューマン
エクセルヒューマン
EH株式会社
エクセルヒューマン
EH株式会社

Anonymous said...

EH株式会社
エクセルヒューマン
EH株式会社
エクセルヒューマン
EH株式会社
エクセルヒューマン
EH株式会社
エクセルヒューマン
EH株式会社
エクセルヒューマン
EH株式会社
エクセルヒューマン
EH株式会社
エクセルヒューマン
株式会社
エクセルヒューマン
エクセルヒューマン
EH株式会社
エクセルヒューマン
EH株式会社

Anonymous said...

クレジットカード現金化
賃貸 大田区
賃貸 北区
賃貸 江東区
賃貸 品川
賃貸 渋谷
賃貸 新宿
賃貸 杉並
賃貸 世田谷
賃貸 中央区
賃貸 千代田区
賃貸 池袋
賃貸 中野
賃貸 文京区
賃貸 港区
賃貸 目黒
賃貸 新築
賃貸 ペット可
賃貸 楽器可
賃貸 手数料なし
賃貸 保証人不要
賃貸 駅5分以内
賃貸 部屋探し
東京 部屋探し
デザイナーズ 賃貸
賃貸 分譲仕様
賃貸 中央線
賃貸 京浜東北線
賃貸 京王線
賃貸 東横線
賃貸 丸ノ内線

wooaini said...

铜米机
碳雕
炭雕
活性炭
活性炭雕
空气净化产品
好想你枣
北京好想你枣
轴承
进口轴承
FAG轴承
NTN轴承
NSK轴承
SKF轴承
网站建设
网站推广
googel左侧优化
googel左侧推广
搜索引擎优化
仓壁振动器
给料机
分子蒸馏
短程蒸馏
薄膜蒸发器
导热油
真空泵油
胎毛笔
手足印
婴儿纪念品
婴幼儿纪念品
园林机械
草坪机
油锯
小型收割机
收割机
割灌机
割草机
电动喷雾器
地钻
采茶机
婚纱
北京婚纱
婚纱礼服
北京婚纱店
个性婚纱
礼服
北京礼服
礼服定制
礼服出租
飘人|飘人2008|云淡风清
铣刀
意大利留学
留学意大利
钢管舞
钢管舞培训
北京钢管舞
爵士舞
北京音皇国际
泳池设备
桑拿设备
印刷厂
油锯
割草机
绿篱机
风力灭火机
留学意大利
意大利留学
好日子小吃车
好日子烧烤小吃车
好日子多功能小吃车
好日子烧烤车
中频感应熔炼锻造设备
高频感应加热钎焊设备
保护膜
佛具
律师事务所
北京律师事务所
法律咨询
北京律师
北京法律咨询
小吃车
多功能小吃车
烧烤小吃车
烧烤车
拓展训练
水泥艺术围栏
水泥艺术围栏设备
水泥艺术围栏机械
水泥栅栏设备
艺术护栏
艺术栏杆
环保艺术围栏
环保围栏
环保围栏机械
环保围栏设备
彩色艺术围栏
花瓶柱
阳台柱
阳台护栏设备
阳台护栏
北京写字楼
写字楼出租
写字楼租赁
塑料轴承
陶瓷轴承
破碎镐
铣刨机

Anonymous said...

賃貸の部屋探しならジョイント・ルームピア!賃貸マンション・賃貸アパートなどの賃貸情報が満載。さらにペット可賃貸・デザイナーズマンションなど多数ご紹介。

Anonymous said...

新宿 賃貸マンション・物件情報なら首都圏中心のマイスタイルにお任せ下さい!新宿 賃貸・渋谷・恵比寿の賃貸マンション・物件情報なら首都圏中心のマイスタイルで!

Anonymous said...

東京・大阪・名古屋を中心とした高級マンスリーマンション、D-Room Stay。30日からの賃貸マンション・サービスアパートメントに、厳選された上質な家具家電、ゆとりある居住空間をプロデュースしています

fds said...

shanghai hotel-guangzhou hotel- shengzhen hotel- beijing hotel Resorts with good discount and accept online reservation. 3000 hotels and resorts with up to 75% discount off published rates for your preview and online reservation. guangzhou hotel Shenzhen hotel shanghai hotel beijing hotel

徐州回转支承 公司提供转盘轴承 --slewing ring slewing bearing slewing bearings服务.

fds said...

物流网是现代物流产品设备资讯传媒. 中国水工业自动化网面向给排水领域设计院所、自来水厂、污水处理厂及市政管理部门,面向工业污水处理、-机器视觉
-传感器
-现场仪表
-显示控制仪表
-分析测试仪表
-执行机构
-工业安全
-低压电器
-电源
工业制水、水文水利、楼宇供水及水泵应用等水工业领域用户,发布和交流各种传感器、检测分析仪表、SCADA设备、监控系统及调速装置的产品、技术、应用、解决方案及市场信息;探讨、推进我国水工业自动化技术、节能技术应用发展。视频,多媒体,自动化,工控视频,自动化视频, PLC教程,变频器教程,软件教程,自动化行业视频新媒体的创造者和领先者-工控TV,教程,播客, PLC,可编程序控制器,自动化软件。同时产品频道有DCS -PAC- PC-BASED-CPCI- PXI-嵌入式系统-
SCADA

-自动化软件
-人机界面
-工业以太网
-现场总线
-无线通讯
-低压变频器
-高压变频器
-运动控制
-机械传动

wooaini said...

铜米机
碳雕
炭雕
活性炭
活性炭雕
空气净化产品
好想你枣
北京好想你枣
轴承
进口轴承
FAG轴承
NTN轴承
NSK轴承
SKF轴承
网站建设
网站推广
googel左侧优化
googel左侧推广
搜索引擎优化
仓壁振动器
给料机
分子蒸馏
短程蒸馏
薄膜蒸发器
导热油
真空泵油
胎毛笔
手足印
婴儿纪念品
婴幼儿纪念品
园林机械
草坪机
油锯
小型收割机
收割机
割灌机
割草机
电动喷雾器
地钻
采茶机
婚纱
北京婚纱
婚纱礼服
北京婚纱店
个性婚纱
礼服
北京礼服
礼服定制
礼服出租
飘人|飘人2008|云淡风清
铣刀
意大利留学
留学意大利
钢管舞
钢管舞培训
北京钢管舞
爵士舞
北京音皇国际
泳池设备
桑拿设备
印刷厂
油锯
割草机
绿篱机
风力灭火机
留学意大利
意大利留学
好日子小吃车
好日子烧烤小吃车
好日子多功能小吃车
好日子烧烤车
中频感应熔炼锻造设备
高频感应加热钎焊设备
保护膜
佛具
律师事务所
北京律师事务所
法律咨询
北京律师
北京法律咨询
小吃车
多功能小吃车
烧烤小吃车
烧烤车
拓展训练
水泥艺术围栏
水泥艺术围栏设备
水泥艺术围栏机械
水泥栅栏设备
艺术护栏
艺术栏杆
环保艺术围栏
环保围栏
环保围栏机械
环保围栏设备
彩色艺术围栏
花瓶柱
阳台柱
阳台护栏设备
阳台护栏
北京写字楼
写字楼出租
写字楼租赁
塑料轴承
陶瓷轴承
破碎镐
铣刨机
china tours
china travel
china tour packages
tibet tour
泳池设备
桑拿设备

Anonymous said...

情趣用品,情趣用品,情趣用品,情趣用品,情趣,情趣,情趣,情趣,按摩棒,震動按摩棒,微調按摩棒,情趣按摩棒,逼真按摩棒,G點,跳蛋,跳蛋,跳蛋,性感內衣,飛機杯,充氣娃娃,情趣娃娃,角色扮演,性感睡衣,SM,潤滑液,威而柔,香水,精油,芳香精油,自慰套,自慰,性感吊帶襪,吊帶襪,情趣用品加盟AIO交友愛情館,情人歡愉用品,美女視訊,情色交友,視訊交友,辣妹視訊,美女交友,嘟嘟成人網,成人網站,A片,A片下載,免費A片,免費A片下載愛情公寓,情色,舊情人,情色貼圖,情色文學,情色交友,色情聊天室,色情小說,一葉情貼圖片區,情色小說,色情,色情遊戲,情色視訊,情色電影,aio交友愛情館,色情a片,一夜情,辣妹視訊,視訊聊天室,免費視訊聊天,免費視訊,視訊,視訊美女,美女視訊,視訊交友,視訊聊天,免費視訊聊天室,情人視訊網,影音視訊聊天室,視訊交友90739,成人影片,成人交友,美女交友,微風成人,嘟嘟成人網,成人貼圖,成人電影,A片,豆豆聊天室,聊天室,UT聊天室,尋夢園聊天室,男同志聊天室,UT男同志聊天室,聊天室尋夢園,080聊天室,080苗栗人聊天室,6K聊天室,女同志聊天室,小高聊天室,上班族聊天室,080中部人聊天室,同志聊天室,聊天室交友,中部人聊天室,成人聊天室,一夜情聊天室,情色聊天室,寄情築園小遊戲情境坊歡愉用品,情趣用品,成人網站,情人節禮物,情人節,AIO交友愛情館,情色,情色貼圖,情色文學,情色交友,色情聊天室,色情小說,七夕情人節,色情,情色電影,色情網站,辣妹視訊,視訊聊天室,情色視訊,免費視訊聊天,美女視訊,視訊美女,美女交友,美女,情色交友,成人交友,自拍,本土自拍,情人視訊網,視訊交友90739,生日禮物,情色論壇,正妹牆,免費A片下載,AV女優,成人影片,色情A片,成人論壇,情趣,免費成人影片,成人電影,成人影城,愛情公寓,成人影片,保險套,舊情人,微風成人,成人,成人遊戲,成人光碟,色情遊戲,跳蛋,按摩棒,一夜情,男同志聊天室,肛交,口交,性交,援交,免費視訊交友,視訊交友,一葉情貼圖片區,性愛,視訊,視訊聊天,A片,A片下載,免費A片,嘟嘟成人網,寄情築園小遊戲,女同志聊天室,免費視訊聊天室,一夜情聊天室,聊天室

9999 said...

情趣用品,情趣用品,情趣用品,情趣用品,情趣,情趣,情趣,情趣,按摩棒,震動按摩棒,微調按摩棒,情趣按摩棒,逼真按摩棒,G點,跳蛋,跳蛋,跳蛋,性感內衣,飛機杯,充氣娃娃,情趣娃娃,角色扮演,性感睡衣,SM,潤滑液,威而柔,香水,精油,芳香精油,自慰套,自慰,性感吊帶襪,吊帶襪,情趣用品加盟AIO交友愛情館,情人歡愉用品,美女視訊,情色交友,視訊交友,辣妹視訊,美女交友,嘟嘟成人網,成人網站,A片,A片下載,免費A片,免費A片下載愛情公寓,情色,舊情人,情色貼圖,情色文學,情色交友,色情聊天室,色情小說,一葉情貼圖片區,情色小說,色情,色情遊戲,情色視訊,情色電影,aio交友愛情館,色情a片,一夜情,辣妹視訊,視訊聊天室,免費視訊聊天,免費視訊,視訊,視訊美女,美女視訊,視訊交友,視訊聊天,免費視訊聊天室,情人視訊網,影音視訊聊天室,視訊交友90739,成人影片,成人交友,美女交友,微風成人,嘟嘟成人網,成人貼圖,成人電影,A片,豆豆聊天室,聊天室,UT聊天室,尋夢園聊天室,男同志聊天室,UT男同志聊天室,聊天室尋夢園,080聊天室,080苗栗人聊天室,6K聊天室,女同志聊天室,小高聊天室,上班族聊天室,080中部人聊天室,同志聊天室,聊天室交友,中部人聊天室,成人聊天室,一夜情聊天室,情色聊天室,寄情築園小遊戲情境坊歡愉用品,情趣用品,成人網站,情人節禮物,情人節,AIO交友愛情館,情色,情色貼圖,情色文學,情色交友,色情聊天室,色情小說,七夕情人節,色情,情色電影,色情網站,辣妹視訊,視訊聊天室,情色視訊,免費視訊聊天,美女視訊,視訊美女,美女交友,美女,情色交友,成人交友,自拍,本土自拍,情人視訊網,視訊交友90739,生日禮物,情色論壇,正妹牆,免費A片下載,AV女優,成人影片,色情A片,成人論壇,情趣,免費成人影片,成人電影,成人影城,愛情公寓,成人影片,保險套,舊情人,微風成人,成人,成人遊戲,成人光碟,色情遊戲,跳蛋,按摩棒,一夜情,男同志聊天室,肛交,口交,性交,援交,免費視訊交友,視訊交友,一葉情貼圖片區,性愛,視訊,視訊聊天,A片,A片下載,免費A片,嘟嘟成人網,寄情築園小遊戲,女同志聊天室,免費視訊聊天室,一夜情聊天室,聊天室