Saturday, December 24, 2005

Canon in D

"Canon in D" คือชื่อเพลงครับ
เป็นเพลงโปรดของผม
ชนิดที่ฟังทีไร น้ำตามันจะซืมออกมานิดๆ
อารมณ์ประมาณ อิ่มเอิบ

วันนี้ไปเห็นใน Del.icio.us
Canon in D Guitar
หนุ่มญี่ปุ่นเล่นได้ถึงใจมาก

ก่อนที่จะ click เข้าไปฟัง
คนที่ไม่เคยฟังเพลงนี้ ลองฟัง version ปกติก่อนครับ
Canon in D (piano)
จะได้อรรถรสมากขึ้น


Note: เพลงนี้เป็นเพลงของ Johann Pachelbel

Related link from Roti

Friday, December 23, 2005

VW Factory, Photo tour

A photo tour of the Transparent Factory in Dresden

ทำไมมันเนี๊ยบอย่างนี้หนอ

Related link from Roti

Yahoo Web Services + JSON + Cross domain javascript

สิ่งที่เราจะทดลองทำกัน ก็คือ ทดลองใช้ javascript
ที่ load จาก domain หนึ่ง access ไปยังอีก domain หนึ่ง
โดยมี JSON เป็น format ในการรับส่งข้อมูล

service ที่จะทดลองเรียกใช้ก็คือ Yahoo! Search Web Services
search service ของ yahoo ปกติจะ return เป็น xml format
แต่เราสามารถระบุ option เพื่อให้ใช้ return เป็น JSON format ได้
(อ่านเอกสาร Using JSON with Yahoo!)

ขั้นแรกก่อนที่จะใช้ search service
เราก็ต้องสมัครขอใช้ service ก่อน
หลังจากสมัครแล้ว เราจะได้ appid มาตัวหนึ่ง
ซึ่งจะใช้เป็น parameter ในการเรียกใช้ service

หน้าจอที่ทดลองทำจะเป็นแบบนี้



เริ่มด้วย html body ของเราหน้าตาแบบนี้
<body>
<form action="#">
<input type="text" name="qry" id="qry"/>
<input type="submit" value="search" onclick="search();return false;"/>
</form>
<div id="list">
</div>
</body>

โดยเมื่อ submit ก็จะเรียกใช้ function search
function search() {
var id='yahoo';
var url='http://api.search.yahoo.com/WebSearchService/V1/webSearch?output=js
on&callback=updateList&appid=YOUR_APPID&query=';

oScript = document.getElementById(id);
var head = document.getElementsByTagName("head").item(0);
if (oScript) {
head.removeChild(oScript);
}
var qry = document.getElementById('qry');
var reqURL = url + encodeURIComponent(qry.value);
oScript = document.createElement("script");
oScript.type = 'text/javascript';
oScript.setAttribute("src", reqURL);
oScript.setAttribute("id", id);
head.appendChild(oScript);
}

สิ่งที่พิเศษใน function search ก็คือ วิธีการ call
เนื่องจากเราไม่สามารถใช้ xmlhttprequest connect ไปยัง domain อื่นๆ
นอกเหนือจาก domain ที่ javascript นั้น load มา

trick หนึ่งท่ีนิยมใช้ ก็คือ การใช้ javascript
dynamic สร้าง <script> dom object ขึ้นมา
และ add มันเข้ากับ <head> element
โดย web service ที่ให้บริการแบบนี้ได้ จะต้องผ่านข้อมูลกลับมา
ในรูปแบบของ javascript
ซึ่งก็เข้าทางกับ JSON notaion พอดี

ใน Yahoo Search Service เราสามารถระบุ parameter
output=json เพื่อให้ pass format กลับมาในรูป JSON
และระบุ callback=functionName
เพื่อให้ script ที่ pass กลับมาทำการเรียกใช้ javascript function ของเรา
ตัวอย่างของ ข้อมูลที่ส่งกลับมาจาก Yahoo Search Service
updateList({"ResultSet":
{"totalResultsAvailable":"17899065",
"totalResultsReturned":"10",
"firstResultPosition":"1",
"Result":[
{"Title":"Java.com",
"Summary":"official Java site from Sun. ....",
"Url":"http:\/\/www.java.com\/",
"ClickUrl":"http:\/\/www.java.com\/",
"ModificationDate":"1134806400",
"MimeType":"text\/html",
"Cache":{...}
},
....
}
}


callback function ของเรา หน้าตาแบบนี้
function updateList(results) {

var ul = document.createElement('ul');
for (var i=0, link; link = results.ResultSet.Result[i]; i++) {
var li = document.createElement('li')
var a = document.createElement('a')
a.setAttribute('href', link.ClickUrl)
a.appendChild(document.createTextNode(link.Title))

li.appendChild(a)
ul.appendChild(li)
}
var listBlock = document.getElementById('list');
if (listBlock.hasChildNodes()) {
listBlock.replaceChild(ul, listBlock.firstChild);
} else {
listBlock.appendChild(ul);
}

}

การทำงานก็คือ เมื่อได้ข้อมูลกลับมา ก็จัดการ render
ลงบน div block ที่ชื่อ list

Note:
  • ยังไม่ลองใน IE ,ลองแต่ใน firefox กับ safari

Related link from Roti

ทดลองใช้ Mylar

วันนี้ได้ฤกษ์ทดลองใช้ Mylar
Mylar คือ Eclipse Plugin ที่เข้ามาช่วยให้เราจัดการ
project หรือ workspace ที่มีขนาดใหญ่ๆได้ง่ายขึ้น
โดย Mylar จะช่วยกรอง information ให้เรา เพื่อลดเวลา
ในการ navigate หา Resources

ลองมาดูตัวอย่างทำงานจริง
เริ่มแรกสุดเวลาใช้ Mylar เราต้องเริ่มด้วยการสร้าง Mylar Task ขึ้นมาก่อน



ใน Mylar Task จะแบ่ง information ออกเป็น
  • Task Summary
    ตรงนี้สามารถ link ไปยัง Bugzila หรือ Issue Tracking ได้
  • Planning
    อันนี้ดีมากเลย ช่วย track เวลาทำงานจริงให้เราได้ด้วย
  • Documentation
    อันนี้เป็น free text ใส่ตามใจชอบ




เมื่อเริ่มต้นทำงานกับ Task หนึ่งๆ เราก็จะกดปุ่ม start (ปุ่มเทาๆด้านซ้ายของ task name)
MyLar จะปิด editor ที่เราเปิดค้างอยู่ทั้งหมด
กรณีที่เป็นการทำงานกับ Task นี้ครั้งแรก
มันจะรอให้เราเลือกเปิด file ที่ต้องการเอง
แต่ถ้าเป็น task ที่เคยทำมาแล้ว มันก็จะเปิด file
ที่เกี่ยวข้องให้อัตโนมัติ


ที่ชอบก็คือ Mylar จะซ่อน information ส่วนที่ไม่เกี่ยวกับ task ออกไป
เช่นในกรณี Package Explorer ก็จะแสดงให้เห็นแต่ resources ที่เรา edit
หรือถ้าเรา navigate ไปตาม tree ใน Explorer
ส่วนที่ไม่เกี่ยวข้องก็จะถูก render เป็นสีเทาจางๆ



กรณี Outline ก็เช่นกัน
method หรือ field ที่ไม่ได้ถูก edit ก็จะถูก render เป็นสีเทาด้วย



Feture อื่นๆที่น่าสนใจมาก ก็คือ
สามารถ integrate เข้ากับ Bugzilla ได้
ถ้าสนใจให้ลองดู flash demo ของ Mylar
ที่นี่ Link

สรุปผลการทดลองใช้
ต้องขอยกให้เป็น plugin ที่ดีมากเลย
ช่วยให้เรา focus ได้ดียิ่งขึ้น
จำได้ว่าเมื่อก่อนเวลาจะ edit project ใหญ่
จะเสียเวลาส่วนหนึ่งไปกับการ scan หาข้อมูลที่ต้องการ

ไว้อาทิตย์หน้าจะแวะเข้าไปบังคับให้น้องๆที่ทำงานใช้กัน

Related link from Roti

Thursday, December 22, 2005

JavaScriptGenerator (RJS Template)

JavaScriptGenerator เป็น Plugin ของ Rails
ที่ช่วยให้เราเขียน ajax ได้สะดวกขึ้น
ลองดูตัวอย่าง

สมมติว่าเราต้องการให้มีหน้าจอ Project Listing
ที่ user สามารถ add project ใหม่ๆได้



เมื่อ user กด add ก็จะแสดง form ขึ้นมา



เมื่อ user กด "add" ก็จะ validate
ว่า project name เป็นค่าว่างหรือไม่
ถ้า validate ผ่าน ก็จะ update "project listing" ให้



กรณีีที่ไม่ผ่าน
ก็จะ แสดง error message



ถ้า user กด cancel ก็จะซ่อน form
และ error message

มาดูว่า ถ้าเขียนโดยใช้ JavaScriptGenerator แล้ว
จะต้องทำอย่างไรบ้าง

ขั้นที่ 1 ไป download plugin มาติดตั้งให้เรียบร้อยก่อน
svn ของ RJS Template อยู่ที่
http://wiki.rubyonrails.org/rails/pages/Plugins

ขั้นที่ 2 implement controller
เริ่มทำหน้าจอ listing ก่อน
class RjsController < ApplicationController

def index
list
render :action => 'list'
end

def list
@projects = Project.find(:all)
end

end

โดย rhtml จะมีหน้าตาแบบนี้
<h4>Listing projects</h4>
<ul id="list">
<% for project in @projects %>
<li>
<%=h project.name %></td>
</li>
<% end %>
</ul>

<div id="post_error">
</div>

<div id="input" style="display:none">
<%= form_remote_tag (
:url => { :action => :new }) %>
<label for="name">Name :</label>
<%= text_field_tag ('name', '', :id=>'name-field') %>
<%= submit_tag "add", :id=>'project-add'%>
<%= end_form_tag %>
<%= link_to_remote ('Cancel',
:url => {:action => :cancel_new}) %>
</div>
<br />

<div id="add_link">
<%= link_to_remote ('New project',
:url => { :action => :display_new }) %>
</div>

จะสังเกตเห็นว่า เรามี ul
ที่มี id => "list"
และก็มี div block
id => "post_error" ไว้แสดง error message
id => "input" ส่วนของ ajax form
id => "add_link" ส่วน link ที่เป็นจุดเริ่มต้นการทำงาน

ขั้นที่ 3 เมื่อ user เลือก link "new project"
ก็จะเกิด ajax request "display_new" ไปที่ server
(จริงๆแล้ว ขั้นนี้ทำเป็น javascript ธรรมดาก็ได้
แต่อยากลอง feature ก็เลยทำเป็น ajax)
controller เราก็จะ define method ดังนี้
class RjsController < ApplicationController

...

def display_new
end

end

ส่วน view ที่ render กลับ
เราจะใช้ rjs template เข้ามา render แทน
โดยแทนที่จะใช้ file ที่นามสกุล rhtml
เราจะตั้งเป็นนามสกุล rjs แทน
page.show('input')
page.hide('add_link')
page.visual_effect :highlight, 'input', :duration => 3

จาก code ข้างบน rjs จะทำการ generate
javascript ตอบกลับไป
โดย page.show ก็คือสั่งให้ DOM Element ที่ต้องการ show ขึ้นมา
ส่วน visual effect ก็คือ ไปเรียกใช้ Mir.aculo.us script ให้ทำงาน

ขั้นที่ 4 ถ้า user submit form เข้ามา
class RjsController < ApplicationController

...

def new
if params[:name] == nil or params[:name] == ""
render :action => :new_error
else

## do some business logic here.

render :action => :new_success
end
end

end

โดย rjs template กรณี success จะเป็นดังนี้
page.insert_html :bottom, 'list',
content_tag("li", params[:name])
page.visual_effect :highlight, 'list', :duration => 3

กรณีที่มี error ก็จะเป็นดังนี้
page.replace_html 'post_error',
'Project Name must specific!!!'
page.visual_effect :highlight, 'post_error', :duration => 3


ดูๆแล้วก็ง่ายดีเหมือนกัน
ถ้าอยากรู้ว่าง่ายแค่ไหน ก็ต้องลองเขียน version ที่ไม่ใช้ rjs
เปรียบเทียบกันดู
ผมลองแล้ว แต่ไม่บอก :)

Related link from Roti

CLisp Package Installer?

วันนี้อยากลองเล่น Web Application Framework
ของ CLisp ดูบ้าง ตัวที่อยากลองเล่นมีี่ชื่อว่า KPAX
ก็เลยเข้าไปหาทาง download มาเล่นดู

ในคู่มือของ KPAX ก็อธิบายไว้ว่า คุณควรจะใช้ ASDF
("Another System Definition Facility") ในการติดตั้ง
ASDF ถ้าเปรียบเทียบให้เห็นภาพ ก็คือ software ประเภทเดียวกับ apt-get, fink, gems

เอาก็เอา ทดลอง install asdf
หมดเวลาไป ชั่วโมงกว่า
เพราะใส่ path ผิด ไม่มีเครื่องหมาย "/" ปิดท้าย

จากนั้นก็เริ่ม install
คำสั่ง install ก็ดูง่ายดี

(asdf-install:install "http://weitz.de/files/cl-ppcre.tar.gz")

(สั่ง run จากใน lisp เลย)

หลายจาก screen วูบวาบอยู่สักระยะหนึ่ง
ก็มี error ว่า

*** - Server responded 404 for GET http://www.cliki.net/s-http-server?download
The following restarts are available:
RETRY :R1 Retry installation


โฮ่ๆ dependency ของ CPAX มันอ้างถึง
s-http-server ที่ดันย้ายที่อยู่ของตัวเองไปแล้ว
จัดแจง search หาว่า มันย้ายไปไหน
ได้มาแล้ว ก็มานั่งคิดต่อว่า แล้วกูจะทำไงต่อดีวะ
เคยได้ยินมาว่า lisp เวลา error แล้ว เราสามารถ
เข้าไปเปลี่ยนแปลง definition หรือ code ส่วนที่ผิดได้
แล้วก็ให้มัน run ต่อได้เลย
(ไม่รู้จำผิดหรือเปล่า เคยได้ยินประมาณว่า
พวก software ในยานสำรวจอวกาศ
มันเกิด bug แล้วมาติดอยู่ที่หน้าจอแบบนี้เหมือนกัน
พวก programmer ก็เลยต้องเข้าไปแก้ แล้วก็ run ต่อจากจุดเดิมได้)

หลังจากนั่งจิ้มเป็น ลิงจิ้ม keyboard อยู่พักหนึ่ง
ก็ยอมแพ้ เปลี่ยนเป็นเลือก abort
แล้วก็สั่ง install s-http-server ก่อนแทน

(asdf-install:install "http://homepage.mac.com/svc/s-http-server/s-http-server.tar.gz")


ฮาๆ เหมือนเดิม s-http-server ก็มี dependency ที่ย้ายหนีไปแล้ว
อยู่เต็มไปหมดเลย
นั่งใล่ install dependency ไปเรื่อยๆ
สุดท้ายก็ค้นเจอ page นี้
page นี้มีตารางอธิบายว่า อะไร depend กับ อะไรบ้าง
คราวนี้ง่ายแล้ว ก็ใล่ install จากตัวย่อยสุดขึ้นมาทีละตัว

เฮ้อ! มิน่าไม่มีใครเขาใช้ clisp กัน
แค่ติดตั้ง ก็ต้องทดสอบความอดทนของผู้ใช้ด้วย

Related link from Roti

User User User

ตัดมาจาก "Creating Passionate Users"
เขาเขียนถึงเรื่อง "The Quantum Mechanics of Users"
ซึ่งเขาอ้างมาจากหนังสือ Blink ของ Malcolm Gladwell อีกที
But it's not just the subatomic world that gets weird when you look too closely--in some cases, asking a user to explain his choices changes his choices! In his book Blink, Malcolm Gladwell (author of The Tipping Point) gives an example where students were asked to rank order 44 different kinds of strawberry jams. When compared with the rankings of experts, the students did fairly well -- "even those of us who aren't jam experts known good jam when we taste it." But--and here's where it gets weird--when the students were asked in advance to provide not just the rankings but a written explanation of their choices, the student rankings lost virtually all correlation with that of the experts. As Gladwell puts it, "By making people think about jam, [the researchers] turned them into jam idiots."


อันบนนี้ไม่เลว น่าสนใจ
แต่เจอประโยคนี้ก่อน
The comments about listening to what the users are saying, what they're not saying, and how it's being said reminds me of the quote by Claude Debussy, "Music is the silence between the notes."

ไอ้ประโยคเรื่อง music นี้ผมเคยได้ยินมาก่อนนะ
แต่ไม่เคยคิดจะเอามาเกี่ยวโยงกับ user requirement ได้เลย

Related link from Roti

Wednesday, December 21, 2005

JGoodies, Swing Data Binding

สืบเนื่องจากเรื่อง JFace Data Binding
bact' ถามว่าไม่มี swing binding บ้างหรือ?

ขอแนะนำ JGoodies
ใน JGoodies จะมี subproject อยู่ดังนี้
  • animation - Time-based real-time animations in Java
  • binding - Data Binding Framework
  • forms - Forms layout system
  • looks - look & feel
  • validation - Validation Framework

ตัวที่เราสนใจก็คือ binding

ไม่พูดพล่ามทำเพลง ลองดูตัวอย่าง code ที่ทำการ binding
เริ่มที่ domain ก่อน
public class Person {

private String name;
private String address;
private String telNo;
private BigDecimal salary;

... // setter & getter

}

ให้ salary เป็น BigDecimal เพื่อที่จะได้ทดสอบ case ที่ต้อง convert ด้วย

Swing Form paint โดยใช้ Matisse ใน netbeans 5.0
โคตรใช้ง่ายเลย สวรรค์ดีๆนี่เอง



ลอง binding แบบง่ายๆดู
private javax.swing.JTextArea textAddress;
private javax.swing.JTextField textName;
private javax.swing.JTextField textSalary;
private javax.swing.JTextField textTelNo;
...
...
Bindings.bind(textName, new PropertyAdapter(person, "name"));
Bindings.bind(textAddress, new PropertyAdapter(person, "address"));
Bindings.bind(textTelNo, new PropertyAdapter(person, "telNo"));


ในส่วนของ salary ที่เป็น BigDecimal
ก็ต้องมีการเขียน Wrapper ขึ้นมา เพื่อใช้ convert
โดย JGoodies เตรียม class AbstractConverter ให้เราแล้ว
class SalaryConverter extends AbstractConverter {

public SalaryConverter(ValueModel subject) {
super(subject);
}

public Object convertFromSubject(Object object) {
BigDecimal value = (BigDecimal) object;
if (value == null) {
return "";
}
return value.toString();
}

public void setValue(Object object) {
if (object != null) {
BigDecimal tmp = new BigDecimal((String) object);
subject.setValue(tmp);
} else {
subject.setValue(new BigDecimal("0.00"));
}
}

}

Note: code นี้เป็นการทดลอง implement
ดังนั้นการทำงานจะไม่สมบูรณ์ ไม่สามารถรองรับได้ครบทุก case


เมื่อเตรียม converter ได้ ก็ทำการ binding ด้วยคำสั่งนี้
Bindings.bind(textSalary,
new SalaryConverter(
new PropertyAdapter(person, "salary")));

จะเห็นว่าสามารถนำ adpater มา chain ต่อกันได้

ลองทดสอบอีก โดยเพิ่มการ validate salary เข้าไป
คราวนี้เราจะใช้ class AbstractVetoableValueModel เข้ามาช่วย
Note: สมมติว่า JGoodies ไม่มี framework
ในส่วนของ validation เราก็เลยต้อง implement เอง

class ValidSalary extends AbstractVetoableValueModel {

public ValidSalary(ValueModel subject) {
super(subject);
}

public boolean proposedChange(Object oldValue, Object newValue) {
try {
BigDecimal tmp = new BigDecimal((String) newValue);
textSalary.setBackground(Color.white);

return true;
} catch (Exception e) {
textSalary.setBackground(Color.red);
}
return false;
}


}


code ในการ binding ของเรา ก็จะเปลี่ยนเป็น
Bindings.bind(textSalary,
new ValidSalary(
new SalaryConverter(
new PropertyAdapter(person, "salary"))));


จบดื้อๆ
ใครสนใจลองไปอ่านดูได้
ขอแนะนำ presentation นี้
แล้วจะเห็นว่า JGoodies มีอะไรหลายๆอย่างที่น่าสนใจทีเดียว
(รวมถึงเรื่อง architecture ของ framework
ที่ดูน่าสนใจทีเดียว)

Related link from Roti

Monday, December 19, 2005

Homebrew CPU

สุดยอด
http://www.homebrewcpu.com/

Magic-1 is a homebuilt minicomputer. It doesn't use an off-the-shelf microprocessor, but rather has a custom CPU made out of 74 Series TTL chips. Altogether there are more than 200 chips in Magic-1 connected together with thousands of individually wrapped wires. And, it works. Not only the hardware, but there's also a full ANSI C compiler for Magic-1 (retargeted LCC), and a rudimentary homebrew operating system.


ปล. ในหน้าแรกอย่าลืมอ่าน P.S. กับ P.P.S.
ตลกดี

Related link from Roti

Programming Language

อ่านเจอใน Jonas Boner
เรื่อง "Java Killed The Innovation of Computer Language"

Java killed the innovation of computer languages.


AspectJ is one of the few innovations in computer languages the last years.


in general nothing has really happened the last 10 years. We have seen many new scripting languages popping up lately (Ruby, Python, Jython, groovy etc.) and even though most of them are both fun and useful - non of them are really innovating, merely reusing and sometimes reshaping old ideas

Related link from Roti

JFace Data binding

ได้ยินข่าวแว่วๆมาสักพักแล้ว
อ่านเจอใน The visual Editor
ว่าตอนนี้ Eclipse3.2M4 มี api ของ JFace Data binding Framework แล้ว
โดยมี api ชุดนี้จะ finalize ที่ version3.2M5

ลองดูตัวอย่างว่า ถ้าเรา binding class person เข้ากับ textfield
แล้วจะต้องเขียนประมาณไหน

Composite top = new Composite(parent, SWT.NONE);
Text firstName = new Text(top, SWT.BORDER);
Person person = new Person();

IDataBindingContext dbc = DataBinding.createContext(top);
dbc.bind(firstName, new Property(person, "firstName"), null);

Related link from Roti

Sunday, December 18, 2005

Fitnesse

ว่าจะเขียนถึง Testing Framework ตัวนี้มานานแล้ว
วันนี้เห็นว่าเขาปรับปรุงหน้า web page ของเขาสวยงามดีแล้ว
ก็เลยได้ฤกษ์ทดลองเสียที

ถ้าสงสัยว่ามันต่างจากพวก xUnit อย่างไร
ก็ลองดู quote นี้

xUnit: Building the Code Right

Fitnesse: Building the Right Code

jUnit เป็น Tool ที่ programmer ใช้ช่วยให้
ตัวเอง(และคนอื่น) มั่นใจว่า code ทำงานได้ถูกต้อง
แต่คำว่าทำงานได้ถูกนั้น มันมีความหมายอื่นอีก
ก็คือ "ทำงานได้ถูกต้องตามที่ลูกค้าต้องการ"

FitNesse ก็คือ tool ที่เข้ามาช่วยทำ Acceptance test
โดยการใช้ wiki เข้ามาเป็นช่องทางในการสื่อสารระหว่าง
ลูกค้า กับ developer โดยลูกค้าจะเป็นคน
กำหนดข้อมูลตัวอย่างว่า อะไรที่ควรทำได้ และอะไรที่ไม่ควรทำ
ไว้ใน wiki page
จากนั้นก็สั่งให้ fitnesse run test (จากการ click ใน wiki นั้นเองเลย)
ซึ่งผลลัพท์ที่ได้จากการ run ก็จะนำมา render เปรียบเทียบกับ
ค่าที่ลูกค้าคาดหวังว่าจะได้ ซึ่งเป็นการ feedback
ถึง status ของโปรแกรม ว่าทำงานถูกต้องหรือไม่

ลองมาดูตัวอย่างการใช้งานแบบง่ายๆดู
  • เริ่มด้วย customer สร้าง page ที่กำหนด ค่า input และ output
    ที่คาดหวังว่าจะเป็นก่อน
    (สามารถสร้างใน excel แล้วค่อย import เข้ามาก็ได้)



  • tester จะ edit page เพื่อใส่ class ที่รับผิดชอบ testcase นี้
    รวมทั้งกำหนด classpath ด้วย



  • หน้าตา java code ที่อยู่เบื้องหลัง test case นี้
    import fit.ColumnFixture;

    public class Wage extends ColumnFixture {

    public String givenWage;

    private BigDecimal getGivenWage() {
    return new BigDecimal(givenWage).setScale(2);
    }

    public String roundWage() {
    BigDecimal tmp = getGivenWage();
    ...
    return tmp.toString();
    }

    }


  • เมื่อต้องการ จะ test ก็เพียงแต่กดปุ่ม "test" ที่อยู่ข้างซ้ายของจอ
    ลองดูตัวอย่างผลลัพท์ที่ได้



    สีแดงแสดง error, ส่วนสีเขียวแสดงว่าค่าถูกต้องตรงตามที่คาดหวังไว้


ตัวอย่างที่แสดง เป็นตัวอย่างง่ายๆ
ถ้าอยากเห็นตัวอย่างการใช้งานจริง
(ซึ่งต้องการอะไรที่ซับซ้อนกว่าตัวอย่างนี้)
ลองเข้าไปที่นี่
SuiteAcceptanceTests อันนี้เป็น test ที่ fitnesses test ตัวมันเอง

Update
fitnesse สามารถใช้กับ java, .net, ruby, Python
, Delphi, Smalltalk, Perl

Related link from Roti