Trở lại…


2009, một năm đầy thất vọng. Tôi đã chứng kiến cảnh những người âm thầm cất bước ra đi, những lời la lối khi không thể chịu đựng, những sự bất công,…Nhưng nơi nào không thế nhỉ? Và trong lúc khó khăn ấy, có mấy người sếp nhận ra, mình đã mất đi một tài năng?



Trong lúc khó khăn ấy, cũng tình cờ tôi được chọn đi học, đó cũng là lúc có thể tránh đi một mớ bòng bong đang tồn tại. Và cũng vì tôi là một người mê học. 9 tháng trôi qua, cũng là sau đúng một năm khủng hoảng. Tôi trở lại với cty, với code, và dĩ nhiên không thể thiếu blog. Tôi sẽ không viết khô khan, không phải toàn code và kỹ thuật. Tôi sẽ viết nhiều, rất nhiều những vấn đề mà một dev có thể sẽ gặp phải. Chờ tôi, và đừng quên ủng hộ nhé.

Đọc tiếp >>

Cao Trong Hien

Xử lý Exceptions


Chương trình nào cũng có lỗi, khác nhau ở chổ ít hay nhiều. Để viết một chương trình có ít lỗi nhất đến cho người dùng là điều mà các LTV luôn mong muốn. Bài viết này cung cấp một số thông tin về Exception. Exception là gì? Tại sao ta lại cần quan tâm? Làm thế nào khi bạn lần đầu tiên đối mặt với java exception?

1. Giới thiệu


Một điều mà các LTV cần nắm được đó là: Lỗi xuất hiện trong chương trình.
Chúng ta cần biết điều này. Nhưng cái gì sẽ xảy ra tiếp theo khi lỗi xuất hiện. Nó sẽ được điều hướng như thế nào? Ai quản lý việc này? Và chương trình sẽ được khôi phục, hay sẽ bị ngưng?

2. Tại sao ta cần quan tâm?


Từ Exception là chữ viết tắt của cụm từ "exceptional event." Và được định nghĩa như sau:
Một exception là một event xảy ra trong quá trình thực thi của một chương trình khi quá trình thực hiện không theo đúng luồng đã được xây dựng.
Có nhiều loại lỗi có thể là lí do của Exception – bao gồm từ lỗi của phần cứng, như hỏng ổ cứng, đến những lỗi do lập trình, như là truy cập đến một phần tử ngoài giới hạn đã khai báo. Khi một lỗi xảy ra trong một phương thức trong Java, nó sẽ tạo ra một đối tượng Exception và chuyển đến “runtime system”. Đối tượng Exception này mang thông tin về lỗi bao gồm kiểu và trạng thái của chương trình khi sảy ra lỗi. Runtime system chịu trách nhiệm tìm kiếm đoạn code để xử lý cho lỗi này. Trong Java, tạo một đối tượng Exception và điều hướng nó đến runtime system gọi là “throw exception”.
Những đối tượng xử lý exception(gọi là exception handler) là danh sách các phương thức đã thực hiện trước khi có lỗi. Runtime system tìm đến nơi gọi, bắt đầu với phương thức nơi gặp lỗi, cho đến nơi mà nó tìm thấy một nơi xử lý cho exception. Một exception handler thích hợp khi kiểu của exception tung ta cùng kiểu với kiểu của exception trong exception handler. Quá trình lựa chọn exception handler gọi là “catch exception”.
Nếu runtime system không thể tìm thấy nơi xử lý exception trong tất cả các phương thức, thì runtime system sẽ hủy thực thi.
Bằng cách xử dụng exceptions để quản lý lỗi, ngôn ngữ Java cung cấp một chiến lược quản lý lỗi rất tốt.

3. Lợi thế của Java


Lợi thế 1: Tách riêng phần code điều khiển lỗi


Theo truyền thống, việc tìm ra lỗi, báo cáo lỗi, điều khiển code khi có lỗi đểu gom lại một nơi define trong code. Ví dụ, giả sử bạn có một chức năng đọc một file lưu vào trong memory. Viết theo mã giả, code của bạn sẽ thế này:

readFile {
open the file;
determine its size;
allocate that much memory;
read the file into memory;
close the file;
}

Ta nhận thấy một số lỗi tiềm ẩn sau:
• File sẽ có thể không mở được?
• Không xác định được chiều dài của file?
• Không đủ memory để lưu trữ?
• File không thể đọc được?
• File không thể đóng được?
Để thực hiện được các việc này, thì trong chức năng readFile, bạn cần thêm một số code để thực hiện công việc khi có lỗi, và điều hướng. Có thể nhìn thấy như sau:


errorCodeType readFile {
initialize errorCode = 0;
open the file;
if (theFileIsOpen) {
determine the length of the file;
if (gotTheFileLength) {
allocate that much memory;
if (gotEnoughMemory) {
read the file into memory;
if (readFailed) {
errorCode = -1;
}
} else {
errorCode = -2;
}
} else {
errorCode = -3;
}
close the file;
if (theFileDidntClose && errorCode == 0) {
errorCode = -4;
} else {
errorCode = errorCode and -4;
}
} else {
errorCode = -5;
}
return errorCode;
}

Với code xử lý lỗi, từ 7 dòng trở thành 29 dòng code – tỷ lệ là 400 %. Tệ hơn, trong đó có nhiều dòng chỉ để tìm ra lỗi, báo lỗi, trong 7 dòng code chính, làm cho ta khó đọc code, và vì vậy việc chỉnh sửa sau này sẽ rất khó khăn. Một vấn đề khác nữa là: Có chắc rằng file đã được đóng nếu memory không đủ để lưu file?
Exceptions cho phép bạn viết luồng chính của code, và ứng xử với lỗi trong một đoạn xử lý khác.
Do đó, code của bạn sẽ thế này:


readFile {
try {
open the file;
determine its size;
allocate that much memory;
read the file into memory;
close the file;
} catch (fileOpenFailed) {
doSomething;
} catch (sizeDeterminationFailed) {
doSomething;
} catch (memoryAllocationFailed) {
doSomething;
} catch (readFailed) {
doSomething;
} catch (fileCloseFailed) {
doSomething;
}
}

Lưu ý là exceptions không giúp bạn tìm ra lỗi, đưa ra thông tin lỗi, và xử lý lỗi. Những gì exceptions làm cho bạn là đưa ra một phương pháp để tách biệt tất cả các xử lý cho exception ra khỏi luồng chính của bạn.
Thêm nữa, nó còn làm cho tổng cộng code xử lý của bạn giảm đáng kế, chỉ còn 250% - so với 400% trong ví dụ trước.

Lợi thế 2: Tung lỗi chồng


Lợi thế thứ 2 của exception là khả năng truyền lỗi bằng phương pháp gọi chồng. Giả xử phương thức readFile là phương thức thứ 4 trong chuổi các phương thức trong luồng chính: phương thức 1 gọi phương thức 2, phương thức này lại gọi phương thức 3, và cuối cùng là gọi phương thức readFile.

method1 {
call method2;
} method2 {
call method3;
} method3 {
call readFile;
}

Giả xử rằng chỉ có method1 là quan tâm đến lỗi sảy ra trong readFile. Theo như cách truyền thống thì method2 và method3 chịu trách nhiệm truyền mã của error được trả về bởi readFile lên đến method1 – phương thức duy nhất quan tâm đến xử lý cho lổi này:

method1 {
errorCodeType error = call method2;
if (error)
doErrorProcessing;
else
proceed;
}

errorCodeType method2 {
errorCodeType error = call method3;
if (error)
return error;
else
proceed;
}
errorCodeType method3 {
errorCodeType error = call readFile;
if (error)
return error;
else
proceed;
}

Như đã biết ở trên, Java runtime sẽ tìm kiếm trong quá trình gọi, để tìm ra nơi xử lý cho lỗi nó bắt gặp. Một phương thức Java có thể tổng hợp xử lý nhiều exceptions được tung ra trong nó, theo cách đó, ta chỉ cần định nghĩa một phương thức để có thể xử lý cho các exception. Vì vậy chỉ phương thức có xử lý cho lỗi là ta quan tâm.

method1 {
try {
call method2;
} catch (exception) {
doErrorProcessing;
}
}
method2 throws exception {
call method3;
}
method3 throws exception {
call readFile;
}

Trong đoạn code giả trên, để nhúng một exception vào một phương thức trung gian thì ta cần chỉ nó ra trong phương thức đó. Do đó, khi định nghĩa một phương thức thì cần định nghĩa luôn những exeption có thể có xảy ra trong nó, họ sẽ có cách xử lý cho các exceptions này một cách tốt nhất.

Lợi thế 3: Gom nhóm và phân biệt lỗi


Thường thì exceptions đều thuộc về categories hay groups. Giả xử rằng ta có một group exceptions, mỗi một lỗi sẽ có một lý do khác nhau, lấy ví dụ như thao tác trên một array: khi giá trị truy xuất ra khỏi phạm vi của array, thành phần đã thêm vô array không đúng kiểu, hay thành phần tìm kiếm không thấy trong array. Cũng có thể gom nhóm to hơn bằng cách: chi làm 2 loại là Array exception, và một exceptions định sẵn cho trường hợp vượt ra ngoài phạm vi truy xuất.
Java exceptions phải là một hiện thực của Throwable hay Throwable. Cho các class khác, bạn có thể tạo một subclasses của Throwable. Mỗi "nhánh" class (một class không có subclasses) miêu tả một kiểu của exception và mỗi "nút" class (một class với một hoặc nhiều subclasses) miêu tả một group exceptions đã khai báo.
Ví dụ, trong diagram sau, ArrayException là subclass của Exception (một subclass của Throwable) và có 3 subclasses.

InvalidIndexException, ElementTypeException, và NoSuchElementException là các leaf classes. Mỗi class là một thể hiện của một lỗi có thể xuất hiện khi thao tác trên một array. Một cách để một phương thức có thể catch exceptions là chỉ catch những instances của một leaf class. Ví dụ, một exception handler điều khiển chỉ invalid index exceptions, phương thức đó sẽ như sau:


catch (InvalidIndexException e) {
. . .
}

ArrayException là một node class và thể hiện của các lỗi có thể xuất hiện khi tương tác với một array, bao gồm các errors đã được khai báo trong các subclasses của nó. Một phương thức có thể catch một exception trong một group hay cũng có thể catch nhiều exception con.
Ví dụ, để catch tất cả array exceptions mà không quan tâm đến kiểu được khai báo, một exception handler sẽ khai báo một ArrayException:


catch (ArrayException e) {
. . .
}


Đoạn handler trên sẽ catch tất cả array exceptions bao gồm InvalidIndexException, ElementTypeException, và NoSuchElementException. Bạn có thể tìm kiếm chính xác exception nào xuất hiện bằng cách tìm exception handler với tham số e. Bạn cũng có thể cài đặt một exception handler để handles tất cả exception với đoạn handler sau:


catch (Exception e) {
. . .
}

Exception handlers là quá chung chung, như ta thấy ở trên, bạn có thể làm cho code bạn hoàn toàn không có lỗi bằng cách catching và handling exceptions mà bạn không biết trước và không có một xử lý cụ thể cho exception. Tôi thì không khuyên dùng exception handlers như thế.
Như bạn đã thấy, bạn có thể tạo một groups exceptions và handle exceptions chung, hay có thể khai báo exception riêng rẽ.

Đọc tiếp >>

Cao Trong Hien

Happy new year 2009


Đã hơn 2 năm từ khi Hiendt's blog ra đời. Có những lúc tôi như muốn buông việc update cho blog, vì quá bận. Rất nhiều bạn đã động viên tôi, và cũng xin cảm ơn các bạn rất nhiều về điều đó.
Năm 2008 đã sắp qua đi, và ta cùng chào đón một năm mới. Tôi xin cầu chúc cho các bạn và gia đình luôn khỏe mạnh, và một năm mới thành công. Sau đây là một số thông tin thu thập của blog này.

Hiendt's blog - Top keyword



Hiendt's blog - Top pages view



Hiendt's blog - Top refering



Hiendt's blog - Top visitor



Đọc tiếp >>

Cao Trong Hien

,

Ca sĩ Phạm Quỳnh Anh: Bonjour Vietnam

Chúng ta đều biết đến ca sĩ người Bỉ gốc Việt này qua ca khúc “Bonjour Vietnam”. Nhân dịp cô ca sĩ rất Việt này về thăm quê, thân gởi đến các bạn một bài PV được sưu tầm trên blog của PQA.


Đọc tiếp >>

Cao Trong Hien

Tester xuất sắc, hắn là ai ?

Một người bạn gởi cho tôi. Tôi đã cố tìm kiếm nguồn chính xác trên internet, và có một số thông tin bên dưới, nhưng cũng chưa rỏ tác giả thực sự.

Tuy nhiên, nó sẽ đem đến cho bạn những kinh nghiệm khi test.

Nếu bạn hỏi tôi, tôi sẽ nói ả - một tester xuất sắc (đôi khi còn bị gọi là “Test dị nhân”) - là một người mà:

Lạng lách

Tester xuất sắc phải là tay chuyên “lạng lách”. “Lạng lách” là ở chỗ: ai cũng có thể làm theo danh sách dài dằng dặc những test cases có đầy rẫy trong các sách dạy testing, nhưng tester xuất sắc lại có thể đi xa hơn cả cái danh sách này, và với tới một seri bất tận những phương pháp xương xẩu để acttack một chương trình.
Tester xuất sắc thường được giới developers phán là “đáng ớn” và “chập cheng”!

Tò mò

Cái gì cũng có thể khiến một tester xuất sắc quan tâm. Mụ ta luôn muốn hiểu tại sao mọi thứ lại diễn ra theo cách mà nó đang diễn ra. Các bug tốt nhất (hay tệ nhất, tuỳ xem anh ở phe nào) là kết quả của sự tương tác giữa hai cái gì đó của phần mềm (ứng dụng, mô đun, component, hay bất cứ cái gì). Mụ biết rõ: việc hiểu cách mà cái gì đó hoạt động sẽ trực dẫn đến việc hiểu cách mà nó tương tác với một cái gì khác, và hiểu tương tác nào trực tiếp đưa đến bug. Mụ thường bộc lộ tính tò mò trong muôn mặt đời thường: thị trường hoạt động thế nào? Dàn giáo được xây dựng ra sao? Sao lại cho chất phụ gia vào bêtông? Bút chì màu được sản xuất thế nào? v.v.
Tính ham hiểu biết của một tester xuất sắc là không có giới hạn.

Phấn khích bởi bugs

Với một tester xuất sắc, bug là liều thuốc bổ. Mụ ló mặt vào bàn của developer, cười ngoác đến tận mang tai mà phô diễn cái lỗi kinh dị sắc bén nhất mới tìm ra. Mụ khoe khoang về bug của mình với các tester khác và háo hức đón nghe những thành tích chói lọi của đồng ... bọn :-)

Biết rằng luôn luôn còn lỗi

Một tester xuất sắc biết rõ: không có lúc nào mà một ứng dụng lại hoàn toàn không có lỗi. Mụ hiểu rằng một ứng dụng trông như không có lỗi lại chính là một ứng dụng đầy những lỗi chưa được tìm ra. Mụ luôn hăm hở tìm ra những kiểu bug mới. Mụ xem mỗi bug khách hàng tìm ra như một một dấu hiệu của một lớp bug mới mà mụ đã bỏ sót mất.
Vui duyên mới không quên nhiệm vụ (Stays on track)
Tester xuất sắc biết rằng phải làm việc tập trung mới có thể tìm ra và cô lập các bug để lần tới tận nguyên nhân gốc rễ. Mụ ta không bỏ qua những lỗi nhâm tiện tìm ra trên đường đi “truy sát” một bug nào đó, nhưng biết hoãn chỉ tay day mặt chúng cho tới khi cái bug hiện tại bị nắm chắc trong tay. (Và, dĩ nhiên là, sẽ háo hức nói lại với tay developer liên quan, rồi khoe khoang cả với các ả test khác)

Khoanh vùng thoả đáng (Scopes appropriately)

Tester xuất sắc hiểu rõ là không thể có đủ thời gian để thực hiện hết mọi test case muốn thực hiện. Mụ bèn phân mức ưu tiên và khoanh vùng việc test sao cho đảm bảo rằng những lỗi dễ ảnh hưởng tới khách hàng nhất sẽ được tìm ra trước tiên.

Bắt nhầm còn hơn bỏ sót (Investigates weird behavior)

Tester xuất sắc chủ động theo dõi những sự kiện lạ. Các icons không xuất hiện đúng vị trí? Các radio buttons không nằm cùng cụm? Đó chỉ là những lỗi lập trình rất đỗi phổ thông! Nhưng với tester xuất sắc thì: những sự lạ như vậy, tuy có thể xảy ra, nhưng mà là dấu hiệu của một loạt lỗi cẩu thả, không dung thứ được. Với mụ, không phải “Đời lúc nào chả lắm sự bất thường” mà “Á chà, tình hình lại ra nông nỗi này đây!”. Phải chặn ngay!

Tả lỗi chính xác

Tester xuất sắc cặm cụi chăm chỉ để mô tả một cách sáng sủa nhất, ít thao tác nhất cái tiến trình sinh ra một bug. Mụ test đi test lại để xem thực sự thì cái bug đó là gì. Lời mụ tả lỗi chỉ ra rành mạch và chính xác: đâu là sự thật đã được kiểm chứng và đâu là phần ước đoán của tester.

Đồng cảm với khách hàng

Tester xuất sắc khắc cốt ghi tâm cái sứ mệnh làm tầng rào chắn cuối cùng để bảo vệ khách hàng khỏi một sản phẩm không đáp ứng được nhu cầu. Tester xuất sắc hiểu mọi lĩnh vực của khách hàng. Hiểu khách hàng cần làm gì và hiểu khách hàng muốn sử dụng sản phẩm theo cách nào. Tester xuất sắc vượt lên trên cả nhu cầu của khách hàng để nhìn thấy khả năng một sản phẩm có thể cải tiến hoạt động của khách hàng ra sao. Tester xuất sắc truyền bá quan điểm của khách hàng qua suốt vòng đời của sản phẩm, từ vision ban đầu đến pha phân tích yêu cầu, xây dựng chức năng, thay đổi chức năng, chữa lỗi rồi phát hành sản phẩm và đi vào bảo trì. Tester xuất sắc giúp nhóm phát triển sản phẩm hiểu khách hàng như chính họ.

Ưu tiên đại cục

Tester xuất sắc thực sự thân thuộc với mọi chi tiết của mỗi tính năng. Và cũng hiểu mỗi tính năng phù hợp hay ảnh hưởng đến toàn bộ sản phẩm như thế nào? Tester xuất sắc sẵn sàng thay đổi hoặc thậm chí cắt bỏ một số tính năng để làm cho sản phẩm tốt hơn về tổng thể.

Bỏ con săn sắt bắt con cá rô (Picks their fights)

Tester xuất sắc thừa nhận rằng: fix tất cả các lỗi thường là việc quá tốn kém, so với các nguồn lực phải bỏ ra. Thế là mụ cân đo lỗi này so với lỗi khác, cho phép một số lỗi bị ỉm để các lỗi khác được fixed xong.

Giữ vững lập trường (Stands their ground)

Tester xuất sắc biết rõ đâu là những lỗi buộc phải fixed. Mụ sẵn sàng trở nên ngoan cố, bướng bỉnh, xù lông nhím khi cần thiết để đảm bảo rằng một lỗi cần fixed phải được fixed thực sự. Mụ bình tĩnh chỉ ra tại sao lỗi đó phải được fixed và thuyết phục đội phát triển rằng thực tình lỗi đó không thể để lờ đi.

Có thể hỏi developer rằng nhà vệ sinh ở đâu

Những người khách ngoại quốc thường được khuyên làm quen với ngôn ngữ và phong tục của người bản xứ, tới mức đủ để có thể gọi xe về khách sạn, hỏi đường tới nhà vệ sinh, và biết rằng hành động bắt tay ở nước sở tại là lịch thiệp hay bất nhã. Cũng như vậy, tester xuất sắc quen thuộc với ngôn ngữ và tập quán của developer. Tester xuất sắc biết UML đủ nhiều để hiểu được các biểu đồ, để vẽ một class hay vẽ một một biểu đồ tuần tự mà không khiến developer cười sằng sặc. Một tester xuất sắc cũng có thể viết code với trình độ ít nhất là bằng sinh viên năm thứ nhất. Và hiểu các thuật ngữ trong thiết kế ở mức đủ tốt để tham dự được các buổi thảo luận và review thiết kế (chứ không phải xin phép rời phòng họp).

Biết rằng tính khả test (testability) chỉ là một trong nhiều ràng buộc

Tester xuất sắc biết rõ cách duy nhất để thực sự test được một ứng dụng là đưa tính khả test vào mọi khía cạnh của sản phẩm. Mụ phân tích tính năng, thiết kế, kiến trúc phần mềm, và phát triển hàng loạt các ý tưởng để đảm bảo rằng sản phẩm có thể test. Tuy nhiên, mụ cũng biết tính khả test không phải là yếu tố duy nhất ảnh hưởng tới kiến trúc, thiết kế và tính năng. Mụ cân bằng tính khả test với các yếu tố khác và giúp đội phát triển tạo ra giải pháp dung hoà tốt nhất.

Biết khi nào cần sự hỗ trợ

Tester xuất sắc thích thử thách, thích húc đầu vào tường và từ từ đâm thủng nó. Tất nhiên, có một số bức tường dày hơn hẳn những bức khác, và đôi lúc cái lỗ thủng thử thách của nó khiến tester liên tục thất bại. Đó là lúc tester xuất sắc nhận ra cần có sự giúp đỡ và mạnh dạn đề nghị được giúp đỡ. Một tester xuất sắc biết chọn ai để nhờ giúp đỡ, và hiểu rõ không có gì phải xấu hổ khi kêu cầu sự giúp đỡ.

Dành thời gian học tập

Tester xuất sắc biết rõ cách duy nhất để tiếp tục trở thành một tester xuất sắc là không bao giờ ngừng học. Không giới hạn sự học của mình trong ngành test, mụ còn mày mò nghiên cứu về lập trình, về quản lý dự án, về marketing, và về bất cứ thứ gì khác, miễn là có mối quan hệ xa gần tới qui trình sản xuất phần mềm.

Không bao giờ dừng test

Tester xuất sắc vượt quá cả biên giới của những tính năng hiện có để liên tục test sản phẩm được giao. Test cả các sản phẩm khác. Test cả sách, cả tủ lạnh, cả đèn điện, cả cửa ra vào ..., test bất cứ thứ gì trong bất cứ lĩnh vực nào của cuộc sống, rồi phán “Thế là không đúng rồi!”

Đọc tiếp >>

Cao Trong Hien

Unit Testing: 12 lời khuyên khi dùng


Unit Testing là một trong các thành phần chính của Agile Software Development. Được giới thiệu đầu tiên bởi Kent Beck, unit testing đã trở thành một thành phần không thể thiếu trong các hệ thống của các tổ chứ lớn nhỏ. Unit tests giúp các kỹ sư giảm thiểu số lỗi, thời gian để debugging, góp phần làm tăng sự ổn định, bền vững.
Trong bài viết này, chúng tôi sẽ tìm hiểu các bước để các LTV dùng unit testing trong các hệ thống phần mềm, không liên quan đến ngôn ngữ lập trình hay môi trường phát triển.

1. Unit Test quản lý rủi ro của bạn


Với một newbie, có lẽ sẽ hỏi Tại sao tôi phải viết test? Suy nghĩ Unit test là một việc làm tẻ nhạt, những kỹ sư phần mềm muốn outsource để tránh nó đi. Đó là tâm lý mà không còn chỗ trong công nghệ phần mềm hiện đại. Mục đích của những nhóm phần mềm là sản xuất phần mềm có chất lượng cao nhất.
Khách hàng và những nhà doanh nghiệp luôn than phiền về những lỗi của các phần mềm trong những năm 80 và 90. Nhưng với sự phong phú của các thư viện, dịch vụ web và các môi trường phát triển cung cấp các tính năng như refactoring và unit testing, sẽ không cònn chỗ đứng cho phần mềm có sai sót.
Ý tưởng đằng sau của Unit Test là tạo ra một tập hợp những lớp test cho mỗi thành phần của phần mềm. Unit Test tạo điều kiện thuận lợi cho việc test phần mềm liên tục, không giống những tài liệu về test, sẽ ít tốn kém khi thực hiện chúng nhiều lần.
Unit test sẽ lớn lên theo hệ thống. Mỗi test là một hợp đồng bảo hiểm mà hệ thống làm việc. Việc dùng một tập hợp unit test, những kỹ sư có thể giảm bớt đáng kể số lượng lỗi và nguy cơ với code chưa được test.

2. Viết Test Case trên thành phần cơ bản


Khi bạn bắt đầu sử dụng unit testing, luôn luôn phải hỏi Test là cái gì mà tôi phải viết?
Có một suy nghĩ sai là viết cụm của các hàm test dùng để thăm dò chức năng nào đó trong hệ thống. Bạn phải suy nghĩ rằng: cần tạo một test case (tập hợp các test) cho mỗi thành phần cơ bản nhất.
Tiêu điểm của test là một thành phần tại một thời điểm. Bên trong mỗi thành phần, tìm kiếm một thành phần có thể tương tác – bao gồm các thuộc tính được truy xuất ra bên ngoài. Bạn cần phải viết ít nhất một test trên mỗi phương thức public.

3. Tạo Abstract Test Case và Test Utilities


Với bất kỳ đoạn code nào, ở đây sẽ có những thứ chung mà tất cả các test của bạn cần làm. Bắt đầu với việc tìm thấy unit testing cho ngôn ngữ của bạn. Ví dụ trong java, những kỹ sư dùng Junit - một framework đơn giản nhưng mạnh để viết test trong java. Framework có lớp TestCase, lớp căn bản cho tất cả các test. Thêm vào những phương thức tiện ích thích hợp với môi trường của bạn. Tất cả các trường hợp test đều có thể chia sẽ tài nguyên chung này.

4. Viết những lớp test khôn khéo


Test thì cần nhiều thời gian, vì thế cần đảm bảo là test của bạn hiệu quả. Một lớp test tốt sẽ thăm dò ứng sử chính của mỗi thành phần, nhưng làm việc đó với code ít nhất có thể. Ví dụ, bạn chẳng cần viết test cho phương thức getter và setter trong Java Bean, nhưng nó sẽ vẫn được test thông qua các test khác cần thiết hơn.
Thay vào đó, viết test tập trung vào các ứng xử của hệ thống. Bạn không cần viết một cách hoàn thiện, hãy tạo những test có ý nghĩa bây giờ, rồi để thêm về sau nữa.

5. Thiết lập môi trường trong sạch cho mỗi test


Người ta luôn quan tâm đến hiệu quả. Vì vậy khi nghe rằng test cần thiết lập môi trường riêng rẽ, họ thường lo lắng về cách thực hiện. Tuy thế thiết lập mỗi test chính xác và từ đầu là quan trọng. Đảm bảo rằng mỗi test được thiết lập đúng mức và không lo lắng về hiệu quả.
Trong trường hợp khi bạn có môi trường chung cho tất cả các test - không cần thay đổi khi chạy – bạn có thể thêm static vào lớp test căn bản của bạn

6. Dùng Mock object để test có hiệu quả


Thiết lập test thì ko đơn giản, và cái nhìn đầu tiên thỉnh thoảng dường như không thể đạt được. Ví dụ, nếu sử dụng Amazon Web Services trong code của bạn, bạn có thể mô phỏng nó như thế nào trong lớp test mà không có sự tác động hệ thống thật sự.
Có một số ít cách. Bạn có thể tạo ra dữ liệu giả và sử dụng nó trong những lớp test. Trong hệ thống mà có những người sử dụng, một tập hợp riêng biệt những tài khoản có thể được dùng riêng cho việc test, được gọi là những mẫu hay những Mock object.
Việc chạy test thường xuyên có thể ảnh hưởng đến hệ thống: nếu điều gì đó trục trặc và bạn xoá dữ liệu người dùng thực tế, giải pháp là dùng dữ liệu giả
Một đối tượng giả thi hành interface đặc biệt, nhưng trả về kết quả đựơc xác định trứơc. Ví dụ, bạn có thể tạo ra đối tượng giả cho Amazon S3 mà luôn luôn đọc đựơc file từ đĩa local của bạn. Đối tượng giả có ích khi test hệ thống phức tạp với nhiều thành phần. Trong Java, có vài framework giúp tạo những đối tượng giả, đáng chú ý là JMock

7. Refactor test khi bạn refactor code


Việc test chỉ đựơc trả tiền nếu bạn thật sự đầu tư vào nó. Không chỉ bạn cần viết test, bạn cũng cần đảm bảo chúng được cập nhật. Khi thêm một phương pháp mới vào một thành phần, bạn cần thêm một hoặc nhiều test tương ứng. Bạn phải làm sạch những code không dùng bên ngoài , đồng thời loại bỏ những test không còn thích hợp nữa.
Unit test đặc biệt có ích khi làm refactoring trên diện rộng. Refactoring tập trung vào việc duy trì tính ổn định của code để giúp nó giữ ở trạng thái đúng. Sau khi bạn chỉnh sửa test, chạy lại các test có liên quan để đảm bảo bạn không làm hỏng bất cứ thứ gì khi thay đổi hệ thống.

8. Viết test trước khi sửa lỗi


Unit test là vũ khí hiệu quả trong sự đấu tranh chống lại những sai sót. Khi bạn sơ hở một vấn đề trong code của bạn, viết test trình bày vấn đề này trước khi chỉnh code. Theo cách này, nếu lỗi lại xuất hiện, nó sẽ bị bắt bởi test.
Điều này là quan trọng vì bạn không thể luôn luôn viết test đúng hoàn toàn. Khi bạn thêm test một sai sót, thì bạn đang lấp đầy lỗ hỏng trong test của chính bạn.

9. Unit Tests đảm bảo tính thực thi


Chẳng nhửng đảm bảo tính chính xác của code, unit tests còn chắc rằng thự thi của code không có suy biến, giúp cải thiện tốc độ thực thi của hệ thống.
Để viết một performance tests, bạn cần implement chức năng start và stop trong test class. Khi thích hợp, bạn có thể dùng một phương thức bất kỳ có liên quan đến thời gian hay code ước lượng thời gian chạy.

10. Test tính đồng thời


Code đồng thời rất khó khăn và là điển hình của source có nhiều lỗi. Đó cũng là lý do mà test này là rất quan trọng. Cách để làm là sử dụng sleep và locks. Bạn có thể viết trong sleep gọi test của bạn nếu bạn cần đợi một trạng thái đặc biệt của hệ thống. Trong khi đây không là giải pháp đúng 100%, chỉ đúng trong một số trường hợp. Để mô phỏng sự đồng thời trong một kịch bản phức tạp hơn, bạn cần lock thành công các đối tượng mà bạn đang test. Vì vậy bạn chỉ có thể mô phỏng hệ thống đồng thời nhưng tuần tự.

11. Chạy Test liên tục


Điểm cần chú ý của test là chạy chúng liên tục, đặc biệt là trong những team lớn, có nhiều người phát triển trên một code căn bản. Bạn có thể thiết lập test để chạy sau vài giờ một lần, hay bạn có thể chạy chúng trên mỗi lần check-in code, hay mỗi ngày (có thể là hằng đêm). Hãy quyết định phưong thức thích hợp nhất cho dự án của bạn và làm những test chạy tự động và liên tục.

12. Vui với Test!


Quan trọng là bạn vui với nó. Lần đầu tiên tôi gặp unit testing, tôi luôn nghi ngờ và nghĩ nó là công việc phụ. Nhưng tôi đã thay đổi cách nghĩ, vì một người rất thông minh mà tôi luôn tin cậy nói với tôi rằng nó rất hữu ích.
Unit testing đặt trí tuệ của bạn vào trong trạng thái mà nó rất khác với trạng thái code. Nó kích thích suy nghĩ của bạn về một việc đơn giản là làm sao tập hợp các test cần thiết cho thành phần này.

Nguồn: ReadWriteWeb

Đọc tiếp >>

Cao Trong Hien

Ý tưởng của Mocking

Đối với những người chưa từng dùng unit testing, ý tưởng của mock objects có thể gây khó hiểu. Tôi cũng có một bài viết về mock object, nhưng về cơ bản thì cũng khá lan man. Trong bài viết này, tôi sẽ nói về mục đích cơ bản của mocking. Mock object là gì? Nó dùng để làm gì? Vì sao ta không thể mock cho object XYZ? Trả lời những câu hỏi này sẽ lý giải một cách dể hiểu về mock objects.

Code không chỉ chứa bản thân


Khi học lập trình, các objects mà ta tạo ra chỉ chứa những gì là của nó. Thường thì các helloworld không có sự phụ thuộc vào bên ngoài (trừ System.out nhé). Tuy nhiên, trên thực tế, phần mềm là có lệ thuộc. Tôi viết một action classes phải phụ thuộc vào services, services phụ thuộc vào data access objects (DAOs) và những cái khác nữa.
Ý tưởng của unit testing là để test code ta viết mà không có phụ thuộc. Là một bước để chắc rằng code bạn viết đúng mà không tính đến các phụ thuộc. Giả thiết rằng code tôi viết là đúng như thiết kế và các phụ thuộc cũng vậy, vì vậy khi tích hợp sẽ làm việc tốt theo thiết kế. Lấy ví dụ một đoạn code sau:


import java.util.ArrayList;

public class Counter {
public Counter() {
}

public int count(ArrayList items) {
int results = 0;

for(Object curItem : items) {
results ++;
}

return results;
}
}

Là một ví dụ quá đơn giản, nhưng nó có thể minh họa cho điều này. Nếu bạn muốn test phương thức count, bạn sẽ viết một test để minh chứng cho phương thức count làm việc. Bạn sẽ không phải test ArrayList vì chúng ta đều thừa nhận rằng nó đã được test và làm việc đúng. Bạn chỉ cần test cách bạn dùng ArrayList.
Hãy nhìn vào một đoạn code phức tạp hơn một chút, nó là một đoạn của một ứng dụng trong thực tế:

public class MichaelsAction extends ActionSupport {

private LookupService service;

private String key;

public void setKey(String curKey) {
key = curKey;
}

public String getKey() {
return key;
}

public void setService(LookupService curService) {
service = curService;
}

public String doLookup() {

if(StringUtils.isBlank(key)) {
return FAILURE;
}

List results = service.lookupByKey(key);

if(results.size() > 0) {
return SUCCESS;
}

return FAILURE;
}
}

Nếu ta muốn test phương thức doLookup, ta chỉ cấn viết test cho nó mà không cần viết cho phương thức lookupByKey. Nhưng làm sao test doLookup mà không cần chạy lookupByKey?

Dùng Mock Objects


Mock objects dùng khi ta muốn tạo một object và đặt nó tại vị trí của object thực. Nó chắc chắn sẽ chứa phương thức sẽ được gọi với các tham số và cách ứng xử nhất định, nó sẽ trả về kết quả được mong chờ. Dùng đoạn code trên như một ví dụ, giả sử rằng khi tôi gọi và gởi khóa 1234 đến service.lookupByKey được gọi, tôi sẽ có một List được trả về với 4 giá trị. Khi đó, mock object của ta mong chờ rằng khi lookupByKey sẽ được gọi với tham số "1234", nó sẽ trả về một List với 4 objects trong đó.

Mock Objects làm việc thế nào?


Có nhiều mocking frameworks khác nhau trong Java. Tôi sẽ không nó cụ thể về chúng ở đây. Tuy nhiên, tôi sẽ nói về cách nó làm việc và ý tưởng thiết kế mà bạn cần quan tâm khi hiện thực nó.
Về cơ bản thì có hai kiểu của mock object frameworks, một được hiện thực thông qua proxy (bạn có thể hiểu nó như một class ủy nhiệm) và một qua class remapping (map phụ thuộc trực tiếp trong file .class ).
Ta xét cách đầu tiên và là cách được dùng nhiều trong thực tế, proxy.
Một proxy object là một object được dùng thay thế cho object thực. Trong trường hợp của mock objects, một proxy object được dùng để mô phỏng object thực mà code của bạn phụ thuộc vào. Tạo một proxy object với mocking framework, và tiêm nó vào trong object dùng setter. Đây là một điểm cần có trong mocking dùng proxy objects, bạn cần cho phép (public) set phụ thuộc. Nói cách khác, khi bạn tạo một sự phụ thuộc bằng cách gọi new MyObject() thì sẽ không còn chổ cho mocking với một proxy object. Đó cũng là lý do Dependency Injection frameworks như Spring ra đời. Nó cho phép tiêm proxy objects mà không cần phải thay đổi code.
Kiểu thứ hai của mocking là remap file class trong class loader. Tôi chỉ biết duy nhất có jmockit làm việt theo cách này. Nó khai thác một khái niệm mới (có trong JDK 1.5) và được cung cấp trong java.lang.Insturment. Nó sẽ tương tác trực tiếp với class loader để remap sự phụ thuộc đến class file mà nó sẽ load. Giả sử bạn có class MyDependency với tên tương ứng .class là MyDependency.class và tôi muốn viết một mock cho nó dùng MyMock để thay thế cho một object được khai báo sẳn. Bằng cách dùng mock objects, bạn sẽ trực tiếp remap trong classloader với phụ thuộc từ MyDependency đến MyMock.class. Sau đó, mock objects có thể được tạo bằng cách gọi new. Dù cho phương pháp này mạnh hơn là dùng proxy object, nhưng nó khó hiểu và có thê gây bối rối, bạn cần có một kiến thức tốt về classloaders nếu muốn dùng hết các tính năng trong nó.

Kết luận


Mock objects có một giá trị lớn trong testing. Cho bạn khả năng test những gì bạn viết mà không liên quan đến sự phụ thuộc.

Nguồn: http://www.michaelminella.com/testing/the-concept-of-mocking.html

Đọc tiếp >>

Cao Trong Hien

,