Testing Dojo web applications with Selenium

Tháng Mười 29, 2014 Để lại bình luận

I jot down here some findings about testing Dojo web applications (with a lot of Dijit Widget!) using Selenium, just in case someone will need them.

The first thing that you should never ever try to do is simulating events using JavaScript. Some libraries like jQuery provide convenient ways to click, focus, or type on HTML elements. But these “synthetic” events have two important limitations:

  • You have to find the right element to send an event. The DOM trees generated by Widgets are huge, with layer under layer. Sometimes it is necessary to read the code and decide which element is used by Dojo to receive which kind of event. Moreover, synthetic events are bad as it not be able to consistently updating the focused elements the way human interactions do, while Widgets heavily rely on this behavior. Some Widgets even refuse to react up on faux clicks.
  • They can be sent to invisible elements, while human obviously cannot manipulate with such things. Dijit Widgets consist of many HTML elements that are carefully arranged on different positions. Any wrongly placed element can render the Widget unusable, while JavaScript does not care about (and understand) position. Therefore, synthetic events are not reliable at all, which would render your tests useless.

Without JavaScript, we have two more effective ways to trigger actions on Widget. They both simulate human interactions in the most native way.

The first one is to utilize native events implemented by all major web browsers. This approach should work well unless you encounter a bug in their implementation, or you are testing against an unpopular browser. It is quite straightforward to enable native events:

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability("nativeEvents", true);

The second one, which will work for edge cases, is to use a robot. A robot can simulate human action as Operating System level. This means if the robot is trying to move the mouse, you will see on your screen that the pointer is moving. The drawback is that the executing machine need to have a display device regardless of being a physical machine or a virtual one (in other word, headless machine will not work). Another disadvantage is that you have to convert coordinates from window-relative into device-relative. To overcome this limitation, just run the browser in full screen mode. For example, with Chrome, we use “kiosk” command switch for full screen mode:

ChromeOptions options = new ChromeOptions();
options.addArguments("—kiosk");

And we control the robot like this:

Point location = input.getLocation();
int x = location.getX();
int y = location.getY();
Robot r = new Robot();
r.mouseMove(x + 10, y + 10);

Finally, Dijit Widget have a bug with touch event (Dojo manipulate browser’s events a lot, it even managing it own focusing behavior so it is really prone to bugs and browser’s compatibility. Make sure you have latest fixes and if things still happen, disable touch events in Chrome.

  1. From Google Chrome, type chrome://flags in the address bar, then press Enter.
  2. Scroll down to the "Enable Touch Events".
  3. Select the drop-down menu and select Disabled.
  4. Restart and enjoy Chrome!
Thẻ:

Autocomplete plugin for TinyMCE 3

Tháng Bảy 28, 2013 Để lại bình luận

I’ve made a fork at https://github.com/minhhai2209/TinyMCE-Autocomplete-Plugin from https://github.com/abrimo/TinyMCE-Autocomplete-Plugin.

This fork improves the method to calculate the caret’s current position so that the suggestion list can work as expected on Internet Explorer, Firefox, and Chrome. Also the suggested options as well as their labels are retrieved via callback functions in order to increase the flexibility.

Untitled

Example code:

$(document).ready(function() {

  var autocomplete_options = [
    {
      homepage: 'http://google.com',
      fullname: 'Google Inc.',
      description: 'Google'
    },
    {
      homepage: 'http://google.com',
      fullname: ' GitHub Inc.',
      description: 'GitHub'
    },
    {
      homepage: 'http://yahoo.com',
      fullname: 'Yahoo Inc.',
      description: 'Yahoo'
    },
    {
      homepage: 'http://yelp.com',
      fullname: 'Yelp Inc.',
      description: 'Yelp'
    },
    {
      homepage: 'http://microsoft.com',
      fullname: 'Microsoft Inc.',
      description: 'Microsoft'
    }
  ];

  var matchingOptions = function(currentWord) {
    var matches = [];
    for (var i in autocomplete_options) {
      var option = autocomplete_options[i];
      if (currentWord.length == 0 || beginningOfWordMatches(currentWord, option.description)) {
        matches.push(option);
      }
    }
    return matches;
  };

  var beginningOfWordMatches = function(beginning, option) {
    var test = new RegExp("^" + beginning, "i");
    return (option.match(test));
  };

  var asString = function(value) {
    var a = $('<a/>', { href: value.homepage }).text(value.fullname);
    return $('<div/>').append(a).html();
  };

  $('.tinymce').tinymce({
    theme : 'advanced',
    plugins : 'autocomplete',

    theme_advanced_buttons1 : '',
    theme_advanced_toolbar_location : 'top',
    theme_advanced_toolbar_align : 'left',
    theme_advanced_statusbar_location : 'none',

    autocomplete_matching_options: matchingOptions,
    autocomplete_as_string: asString
  });
});
Thẻ:

Thing

Tháng Bảy 28, 2013 Để lại bình luận

Tôi vẫn loanh quanh với cái ý này nên phải tiếp tục.

Các hệ thống thông tin thường định nghĩa các loại đối tượng mang nội dung, với một số thuộc tính.Chẳng hạn một hệ thống quản lí dự án (tôi đang làm một cái đại loại vậy, nên các ví dụ sẽ dính tới nó) có thể có các đối tượng test case, requirement. Với một số hệ thống người dùng có thể thêm các thuộc tính mới vào. Điều này giúp tăng cường khả năng thích nghi của hệ thống với những yêu cầu khác nhau. Các hệ thống dạng này có những điểm chung và riêng, nhưng bất kể là chung hay riêng thì khi phát triển chúng ta cứ phải làm đi làm lại, và giải quyết những vấn đề giống nhau. Tôi muốn tìm kiếm một cách nhìn cho những điểm chung và riêng như vậy, theo một hướng khác, biết đâu sẽ giúp ích cho những dự án sau này.

Thường thì các hệ thống sẽ đi từ loại đối tượng (từ giờ tôi sẽ gọi ngắn gọn là type, như test case, requirement, bug, note, …). Hệ thống hoặc người dùng sẽ định nghĩa các type, và khi tạo một đối tượng người ta phải chọn type trước. Tôi muốn thử đi ngược lại, đối tượng có trước, type có sau (lí do sẽ được trình bày dần). Chúng ta sẽ gọi các đối tượng mang nội dung trong hệ thống là thing (tiếng Anh cho dễ viết mã). Mỗi thing thì gồm nhiều tính chất (gọi là property). Điều này ứng với thực tế là khi nhìn một đồ vật người ta sẽ ghi nhận tính chất của chúng. Property là một bộ gồm tên (key) và giá trị (value), key thì phải là dạng chuỗi, còn value thì có thể là chữ, số, ngày, hay một dạng phức tạp hơn, tương tự như mỗi tính chất của đồ vật thì có rất nhiều từ để mô tả nó.

Khi người dùng tạo thing thì họ có thể tạo và điền các property của nó theo ý của mình. Đôi khi chúng ta muốn thing tạo ra có sẵn các property và value nhất định. Cách tiếp cận thông thường là các hệ thống cho người dùng tạo một type mới, định nghĩa các property và value mặc định của chúng. Tuy nhiên, khi làm như vậy, các value mặc định sẽ đứng đơn lẻ chứ không phải hợp với nhau để thể hiện một thing. Chẳng hạn những ràng buộc như “ngày bắt đầu” phải nhỏ hơn “ngày kết thúc” sẽ không thể được định nghĩa hay kiểm tra khi tách riêng hai property này. Nói cách khác, cái chúng ta cần không phải là giá trị mặc định của một property, mà là một thing mặc định. Một hạn chế nhỏ nữa là với thiết kế dạng này, chúng ta không thể tạo sẵn nhiều hơn một khuôn mẫu (những bộ value mặc định) cho thing. Để giải quyết vấn đề này chúng ta sẽ đưa ra khái niệm sao chép (clone) và khuôn mẫu (template).

Với các thing đã tạo, người dùng có thể clone để tạo ra các thing mới. Tất nhiên, thing tạo ra bởi clone sẽ có property và value giống như thing được clone. Chúng ta sẽ gọi thing được clone là template. Template chỉ là một cách gọi, còn về bản chất chúng cũng là thing. Như vậy chúng ta có thể đưa ra định nghĩa type mới. Type chính là template của một thing. Ví dụ, thing C và thing D được clone từ template thing B thì chúng cùng có type A. Type ở đây không cần là khái niệm chính thức, mà chỉ để giúp chúng ta đối chiếu với các hệ thống đã có. Type có thể thành một chuỗi, ví dụ template thing B có thể được tạo từ template thing A. Có thể chúng ta sẽ có một thing gốc, ví dụ như THING. Từ THING chúng ta sẽ tạo ra các thing và các thing này có thể được dùng làm template để tạo ra các thing khác.

Có thể xét một ví dụ để làm rõ ý này. Cho một hệ thống quản lí dự án. Chúng ta sẽ tạo một thing là Artifact với một property là name. Dùng Artifact làm template chúng ta clone ra Test Case, Requirement và thêm những property khác. Khi đấy việc hiện thực chứng năng tìm kiếm sẽ được đơn giản hóa, vì tìm kiếm trên Test Case, Requirement, hay toàn bộ Artifact thì cũng chỉ là tìm kiếm trên thing mà thôi. Hơn nữa, nếu sau này tất cả các đối tượng trong dự án phải có thêm property mới như ID chẳng hạn, chúng ta chỉ cần cập nhật thing Artifact.

Tôi đã mượn từ clone trong JavaScript rồi, tôi sẽ mượn thêm ý tưởng duck typing (“When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck”). Bởi vì chúng ta không có type, mọi chức năng sẽ khá mềm dẻo. Những chức năng nghiệp vụ giờ đây không cần quan tâm đến type của thing, mà chỉ cần biết chúng thỏa mãn một tập điều kiện cho trước. Chẳng hạn, chức năng report chỉ cần quan tâm là tập thing có property status hay không, và status có giá trị gì, v.v là có thể đưa ra thống kê cho tập thing này rồi. Việc giới hạn chức năng hoạt động theo type (hay chính xác hơn là template mà một thing được clone từ đó) vẫn có thể thực hiện được, nhưng nó chỉ là một tham số mà thôi. Cách tiếp cận này sẽ giúp chúng ta xây dựng nhanh chóng những chức năng giống nhau cho các type khác nhau.

Về mặt hiện thực, cần chú ý là các thing có thể hoàn toàn khác nhau về số lượng hay kiểu property, nên cấu trúc database phải loại bỏ hoàn toàn khái niệm về type như thông thường. Về việc thiết kế kiến trúc, có lẽ phải tìm cách để module hóa càng nhiều càng tốt, khi đó các khái niệm xung quanh thing có thể được hiện thực thêm mà ít gây ảnh hưởng cho toàn hệ thống. Có thể là thing sẽ được thể hiện trong database hay mã dưới dạng associate array, gồm các cặp key và value. Các cặp key và value này không nhất thiết phản ánh 1:1 các property. Chẳng hạn cho một property dạng tag với value “new, unfinished”, chúng ta sẽ có trong thing các cặp (key, value) là (p_count, 2), (p_tag_1, “new”), (p_tag_2, “value”). Cách thể hiện này ít nhất sẽ giúp việc đọc ghi với database được thống nhất, và các module sẽ không cần tạo ra những cấu trúc dữ liệu phức tạp. Tôi sẽ quay lại chuyện này nếu tôi thực sự bắt đầu hiện thực :D.

Hiện tại chỉ có thế. Còn nhiều vấn đề nữa mà tôi sẽ phải suy nghĩ, trước mắt là:

  • Xem xét khái niệm actor dựa trên thing. Actor là một dạng thing đặc biệt có khả năng kích thích một hành động của hệ thống. Actor có thể là người dùng, một hệ thống khác, hoặc chính bản thân hệ thống này.
  • Xem xét một cơ chế để chia vùng cho thing (zone). Chẳng hạn hệ thống có thể phân cấp theo công ty (company), dự án (project), và nhân viên (user). Một hệ thống khác lại muốn phân chia theo nhóm (group) và thành viên (member/user). Có lẽ các cấp này sẽ được quy về zone, với company, project, user, group, v.v là các property của zone.
  • Xây dựng một cơ chế nào đó cho chức năng security. Mục tiêu là có thể tắt bật “bức tường” security này tùy ý mà hệ thống vẫn hoạt động bình thường.
  • Và mở rộng hơn, là một cơ chế module phù hợp để xây dựng các chức năng dựa trên nền cơ bản này.

Tôi nghĩ xây dựng khái niệm là điều quan trọng khi bắt đầu xây dựng hệ thống. Cho dù cấu trúc của nó có thế nào, thì việc đầu tiên có lẽ là phải gọi tên được một cách chính xác và ngắn gọn các khái niệm sẽ xuất hiện trong mã nguồn của bạn.

Linh tinh về datagrid

Tháng Bảy 27, 2013 Để lại bình luận

Table, datagrid (không biết từ này có trong từ điển không, tránh nhầm với data grid), bảng dữ liệu, v.v là thứ không thể tránh khỏi khi làm mấy thứ hệ thống thông tin :(. Trên thị trường hiện nay có rất nhiều loại datagrid cho web. Hàng bán cả nghìn USD cũng có, hàng trôi nổi cũng không thiếu, mẫu mã đẹp, chức năng đa dạng, nhưng đã dùng là phải hối hận. Chúng ta sẽ xem xét một vài tiêu chí để chọn hoặc xây dựng datagrid.

Nhưng mà trước khi bắt đầu có lẽ tôi phải nói điều này. Nếu bạn định dùng DataGrid của Dojo, hay đang cố gắng chỉnh sửa nói, hay đang cố gắng khắc phục lỗi của nó, thì bạn nên cân nhắc việc bỏ hẳn nó để dùng một thứ khác, có thể là HTML table, có thể là bạn tự viết, SlickGrid, jqGrid, v.v. Nói rộng hơn, nếu bạn đang định dùng hay mới dùng Dojo thì hãy cố gắng tránh xa nó ra trước khi quá muộn.

Quay lại bài.

Quan hệ giữa dữ liệu và việc hiển thị

Các datagrid nói chung thường hay cố gắng quá mức trong việc quản lí data, như xây dựng các Store hay Model để lấy dữ liệu từ server, phân trang, tìm kiếm, cập nhật động, v.v mà trong khi đó xem nhẹ các method để giao tiếp trực tiếp với việc hiển thị. Hệ quả là việc tổ chức dữ liệu cho datagrid sẽ bị couple một cách cứng nhắc, gây khó khăn trong việc tùy chỉnh datagrid hay chuyển đổi sang một loại datagrid mới. Ở mức tối thiểu, datagrid phải cho chúng ta thực hiện những hành động như:

  • thay đổi giá trị được hiển thị (chứ không phải giá trị của dữ liệu) trong một ô
  • render lại một ô, một dòng, hay một khu vực nào đó (chứ không phải cập nhật dữ liệu của datagrid)
  • thay đổi thứ tự các dòng (chứ không phải sắp xếp bản thân dữ liệu của datagrid)
  • v.v

Trong trường hợp tự viết datagrid, tôi nghĩ chúng ta sẽ có ba thành phần:

  • Thành phần hiển thị, đây chính là datagrid. Thành phần này chỉ yêu cầu dữ liệu với cấu trúc tối thiểu, không cần hiểu những chức năng như sắp xếp, tìm kiếm, v.v. Đối với nó, dữ liệu chỉ là để  hiển thị.
  • Thành phần quản lí dữ liệu, như Store hay Model tùy theo framework hay cách bạn viết. Thành phần này ngược lại, không cần biết và không nên biết rằng dữ liệu sẽ được hiển thị với datagrid hay bất kì cái gì.
  • Một datagrid builder để xây dựng cầu nối giữa hai thành phần trên.

Không may là nhiều datagrid có sẵn lại được cung cấp ở dạng datagrid builder, với hai thành phần đầu tiên trộn lẫn rất chặt với nhau.

Markup được sinh ra

Tùy vào mức độ phức tạp của bảng dữ liệu, datagrid có thể dùng thẻ table của HTML, hay dùng thẻ table cho mỗi một dòng, hoặc dùng div và CSS để xếp thành bảng. Riêng table có một ưu điểm là cấu trúc bảng sẽ luôn được bảo đảm bởi ngữ nghĩa của markup, sẽ không bao giờ có chuyện ô trên rộng, ô dưới hẹp, mất ô, v.v. Nói chung, mỗi kiểu có ưu nhược điểm khác nhau, tuy nhiên tốt hơn cả là việc render có thể thay đổi được, dựa vào chẳng hạn những hàm callback. Datagrid sẽ không cần biết dữ liệu được hiển ở dạng bảng hay dạng gì, chúng chỉ cần biết là phải render ô, một nhóm các ô (ví dụ gọi là dòng), và một nhóm các dòng (ví dụ gọi là trang), v.v. Khi đó với dạng đơn giản chúng ta có thể render bảng dùng thẻ table, với dạng phức tạp chúng ta dùng div, v.v. Nếu không thì ít nhất việc thay đổi style như chiều rộng, chiều cao có thể được thực hiện dễ dàng bởi một người chỉ biết CSS. Đồng thời chiều cao của datagrid không được phụ thuộc nhiều vào sự tính toán của JavaScript (như DataGrid của Dojo), mà phải phụ thuộc vào việc reflow của trình duyệt.

Cơ chế plugin

Datagrid nào cũng cho bạn tự viết các hàm callback để tạo giá trị hiển thị trong một ô, hay tạo các thành phần giao diện để người dùng có thể nhập dữ liệu. Tuy nhiên, một datagrid tốt thì nên để cho người dùng có thể tùy ý thay đổi markup. Lí do là việc hiển thị dữ liệu hay thay đổi theo yêu cầu của phần mềm, như tô màu, ẩn, nhập ô, v.v và đều khó đoán trước. Trong khi đó datagrid là một thành phần giao diện phức tạp và việc đọc hiểu cũng như sửa đổi từ bên trong sẽ đòi hỏi nhiều thời gian và công sức. Tốt nhất là datagrid nên cung cấp những hàm callback với tham số là bản thân DOM node của các thành phần trên datagrid. Các callback này có thể được thể hiện như những filter method nhằm cho phép các module khác nhau có thể chỉnh sửa cùng một bộ phận trong markup.

Bốn câu hỏi

Tháng Bảy 26, 2013 Để lại bình luận

Và những tuần qua tôi gặp rất nhiều rắc rối với việc làm theo cảm giác và sự mơ hồ.

Từ việc làm cái gì…

Có một vấn đề (tôi nghĩ là dự án nào cũng sẽ có thôi) là cấp dưới thì không biết rõ là làm một chức năng nào đó để làm gì, còn cấp trên thì không truyền đạt được một cách cố định là cấp dưới cần làm cái gì để làm gì. Trong trường hợp với dự án tôi tham gia, chúng tôi bỏ thời gian thảo luận những chi tiết nhỏ nhặt, tỉ mí hóa những thứ mà dù thế nào cũng có thể không ảnh hưởng tới mục tiêu cuối cùng. Và những lí do như “làm cho người dùng tiện dụng”, “Agile” được tung ra như những vũ khí nhằm bảo vệ một ý tưởng nào đó. Những cuộc tranh luận cứ đi xa dần, xa dần và kết quả là chúng tôi có nguy cơ làm một thứ lớn hơn rất nhiều thứ chúng tôi thực sự cần cho chức năng đó. Và những thứ thực sự cần thì có khi lại chưa hề nghĩ tới. (May mắn là tôi vẫn có một người cấp trên nhạy bén để ra những quyết định đúng đắn và nhanh chóng, nhưng không phải lúc nào chúng ta cũng may mắn như vậy phải không?)

…tới việc làm thế nào

Một trong những ưu điểm trong việc gói những công nghệ vào những từ ngữ đơn giản là nó giúp chúng ta tưởng tượng ra vấn đề rất nhanh, và nhược điểm là chúng ta tưởng tượng nó rập khuôn thay vì thực sự nghĩ về giải pháp hay lí do bên dưới. Chẳng hạn những từ như cloud, AOP, SOA, v.v vừa có vẻ rõ ràng vừa có vẻ mơ hồ. Sự mơ hồ sẽ khiến những suy luận có lí bị lấn át bởi cảm giác. Có lẽ tôi sẽ lấy luôn ví dụ mà tôi đã gặp. Pull và push bạn nghĩ cái nào tốt hơn? Tôi sẽ nói là không có cái nào. Pull khiến chúng ta liên tưởng tới poll. Và poll thì thường bị cho là dở. Nên pull là dở. Vậy cơ chế tự cập nhật phần mềm của Windows là pull hay push? Có thể nói là pull, vì máy tính luôn quyết định việc có tải xuống gói cập nhật hay không. Vậy là pull không hẳn là dở. Sự nhập nhằng là do chúng ta thực tế không bao giờ phải xem pull hay push cái nào tốt hơn, mà là những giải pháp cụ thể cái nào tốt hơn. Khi bạn gói những giải pháp vào những từ mơ hồ như pull và push là bạn đã bóp méo quá trình ra quyết định.

“Tôi thấy như vậy không hợp lí”

Và khi những lập luận mơ hồ như vậy được đưa ra để lấn át quá trình ra quyết định dựa vào những dữ kiện thực tế, thì mọi cuộc thảo luận xem như đã kết thúc. Những lập luận mà trong đó không có một lí do sẽ rất khó bị phản bác, vì thực sự là nó không cung cấp điều gì để mà phản bác. “Tôi thấy không hợp lí”, “làm sao cho tốt nhất”, “Agile”, “tôi thấy kì”, v.v sẽ là vũ khí bí mật của bạn. Và không, quan hệ không giải quyết vấn đề. Đi chơi, hội hè có thể giúp mọi người thân mật và hiểu nhau, nhưng nhiều nhất nó chỉ giúp mọi người thảo luận một cách hòa bình. Nếu không có một cơ chế thực sự khoa học và chặt chẽ để ra quyết định, thì từng cá nhân có cố gắng tới đâu cũng sẽ không thể đưa các quyết định đi theo con đường đúng đắn.

Bốn câu hỏi

Đây là bốn câu hỏi mà mấy hôm trước tôi học được của một người bạn để ra các quyết định. Không biết nó là của ai.

  1. Thực tế hiện tại là gì?
  2. Thực tế đó gây ra vấn đề gì?
  3. Lời giải của tôi là gì?
  4. Khi làm như vậy thì điều gì xảy ra?

Nếu câu 1 bị bỏ qua, chúng ta sẽ không thấy được nguồn gốc sâu xa của vấn đề. Nếu câu 2 bị bỏ qua, thì chúng ta sẽ đi tìm những câu trả lời cho những câu hỏi mà chúng ta thực ra không định hỏi. Nếu câu 4 bị bỏ qua, khả năng lời giải ở câu 3 không phải để trả lời cho câu 2. Và chúng ta tiến vào thế giới của sự mơ hồ.

Bốn câu hỏi có thể chỉ ra sự mơ hồ, nhưng có lẽ chúng ta cần thêm một cái gì đó.

Thẻ:

Tag

Tháng Bảy 15, 2013 1 Bình luận

Chỉ là một suy nghĩ chơi, có thể trùng với chỗ nào đó rồi.

Liệu chúng ta có nên nhìn một số kiểu input và property dưới dạng tag. Hãy lấy một ví dụ cụ thể. Tôi có một hệ thống quản lí test case và feature với những thuộc tính như:

  • Mỗi test case có một status, như new, abandoned, finished.
  • Mỗi test case có thể ứng (liên kết) với nhiều feature.
  • Mỗi test case có thể có nhiều tập tin đính kèm.
  • Mỗi feature có thể ứng (liên kết) với nhiều test case
  • v.v

Để ý rằng những tính chất này khá là động, theo nghĩa nó có thể thêm vào bớt đi tùy nhu cầu của người dùng hay người phát triển, và mỗi người dùng lại muốn một cách thiết lập khác nhau.

Chúng ta có thể nhìn các khái niệm trên như sau:

  • Mỗi test case hay feature đều là một sự vật, và đều được mô tả bởi các tính chất.
  • Mỗi property của test case hay feature, như status hay tập tin đính kèm, đều là những tính chất.
  • Các property đều có giá trị, tập giá trị có loại là vô hạn (như text), có loại là cố định (như một tập các check box). Những giá trị này là những “tính từ” mô tả tính chất.
  • Mỗi người dùng hay nhóm người dùng có thể tạo các tập property khác nhau. Ta sẽ coi như đây là các ngôn ngữ khác nhau. Mỗi ngôn ngữ có một bộ từ vựng riêng.

Trước hết, cần hiểu rằng mỗi sự vật đều có tính chất. Nhưng mối quan tâm và cách mô tả sự vật của mọi người đều khác nhau, nên phải tách tính chất chủ quan ra khỏi bản thân sự vật. Trong trường hợp này, chúng ta phải thiết kế để test case hay feature giữ những thuộc tính tối thiểu, và những tính chất cũng test case hay feature sẽ được giữ ở một nơi hay module khác.

Lúc này ta có thể tìm cách xây dựng một cấu trúc chung cho tất cả các property với tập giá trị hữu hạn (vô hạn cũng có thể, nhưng tạm lấy hữu hạn cho dễ nói). Những giá trị này giống như tag (nên tôi đặt tên bài viết là tag). Trong đó mấu chốt là xây dựng tính từ sao cho nó có thể đại diện cho mọi loại đối tượng, cụ thể như:

  • Những tính từ mà người dùng thoải mái thêm vào, như status của test case (new, abandoned, v.v).
  • Những tính từ ứng với đối tượng trong hệ thống. Ví dụ như property thể hiện các feature được liên kết với test case. Mỗi feature chính là một tính từ, cho nên tính từ được sinh ra theo đối tượng.

Khi ta xây dựng được một loại tính từ có thể đại diện cho mọi loại đối tượng, thì việc viết các input cho giao diện cũng sẽ được quy về những loại chung, chẳng hạn:

  • Input cho tối đa n giá trị, n từ 1 tới vô cùng.
  • Input dạng check box, radio, drop down list. Nói chung là dạng mà mọi giá trị được liệt kê.
  • Input dạng search, người dùng sẽ tìm kiếm tại chỗ các tính từ mà họ muốn, đặc biệt là những tính từ ứng với đối tượng trong hệ thống.

Điều này cũng sẽ giúp ta đơn giản hóa việc xây dựng một engine cho việc query dữ liệu. Ví dụ ta có thể đưa ra một cú pháp chung là “<property> has value <adjective>”. Việc query chỉ thực hiện trên tập tính chất và tính từ, nên ta sẽ không phải viết code để xử lí cho từng thuộc tính riêng.

Cuối cùng, chúng ta cần khả năng tách biệt giữa các loại ngôn ngữ và từ vựng, để đảm báo người dùng sẽ có được sự tự do khi làm việc với hệ thống, và người phát triển sẽ không phải nghĩ nhiều về việc người dùng cần gì.

URI và REST

Tháng Bảy 14, 2013 Để lại bình luận

Thêm một tí về REST.

Hiểu HTTP

Đúng ra bài viết  là về HTTP, vì REST không bắt buộc dùng HTTP. Tuy vậy những vấn đề về URI chủ yếu được để ý khi mọi người “viết REST”, còn lúc làm web bình thường thì URI kiểu nào cũng không bị để ý. Trong khi đó những khái niệm về URI đều từ HTTP mà ra cả, không hề liên quan tới REST.

Về HTTP thì đọc RFC là tốt nhất. RFC tương đối ngắn gọn, súc tích, dạng plain text rõ ràng, nhưng lại hay bị bỏ qua. Dưới đây tôi cũng chủ yếu tóm lược lại và link tới những tài liệu quan trọng để bạn có thể dễ dàng tìm đọc và hiểu đúng, chứ tôi viết thì cũng có thể sai sót.

POST, PUT, tạo và cập nhật

Thường thì hai loại method này được hiểu là POST dùng cho tạo, PUT dùng cho cập nhật. Điều này không hoàn toàn đúng. Có hai chỗ bạn có thể đọc thêm, một là RFC 2615, hai là trong quyển RESTful web services. RFC là chính xác nhất. Dịch thì cũng hơi khó, nhưng ý tưởng cơ bản là như sau:

  • POST là để cung cấp thành phần phụ cho một resoure xác định bởi URI, theo kiểu thêm một bài viết vào blog, hay thêm bình luận vào một bài viết (bài viết là resource), v.v. POST cũng có thể dùng để đẩy dữ liệu tới một quá trình xử lí nào đó. Kết quả của POST có thể không phải là một resource được xác định bởi một URI, chẳng hạn như khi bạn xem bình luận của một bài viết không phải là resource. Tất nhiên kết quả của POST cũng có thể là một resource với URI xác định. Điều này thường dùng trong viết REST khi chúng ta tạo một đơn vị resource mới trong một tập các resource cùng loại, ví dụ POST /article để tạo ra một bài báo mới ở article/{id}. Trong trường hợp có resource tạo ra như vậy thì phải trả về 201, chứ không phải 200 hay 204.
  • PUT là để đưa dữ liệu tới một resource xác định bởi URI. Tùy thuộc vào resource đã tồn tại hay chưa mà hành động này là create hay cập nhật. Chẳng hạn PUT /user/nmh để tạo hay cập nhật user nmh, chứ server không thể tạo user abc để trả về được. Chính vì vậy là không thể nói là POST dùng cho tạo, PUT dùng cho cập nhật.

Tóm lại có thể nói đơn giản là POST có thể dùng cho những việc hoàn toàn không liên quan đến tạo resource. Trong trường hợp POST dùng để tạo resource, thì resource sẽ được tạo ra chưa có URI xác định lúc gửi request. Với PUT thì resource sẽ được tạo ra hay cập nhật luôn có URI xác định ngay lúc gửi request.

Path variable, request parameter, và matrix URI

Dùng path variable hay request parameter? Câu trả lời là thế nào cũng được. Nhưng trong quyển RESTful web services có một đề xuất dựa trên RFC 3986. Dùng path variable nếu bạn muốn thể hiện sự phân cấp, ví dụ node/{id}/comment thay cho comment?node={id}. Trong trường hợp các biến không thể hiện sự phân cấp, hoặc có thể có có thể không, thì nên dùng request parameter.

Matrix URI thì là một thứ hơi khó hiểu và ít được nhắc đến. Về những ví dụ cụ thể có thể xem ở các web framework hỗ trợ nó, như Spring chẳng hạn. Về lí do ra đời thì có thể hiểu đơn giản là đâu đó có thể có nhu cầu dùng một associate array dưới dạng path variable, trong đó một số element có thể có hoặc không. Ví dụ (từ Restful web service) bạn muốn cho kinh độ và vĩ độ vào path variable để tạo URI thể hiện một điểm nào đó trên bản đồ, trong khi đó kinh độ và vĩ độ ngang hàng nhau nên không thể xếp một trong yếu tố đứng trước hoặc sau. Trong tình huống này bạn cần dùng matrix URI.

Thẻ:,
Theo dõi

Get every new post delivered to your Inbox.

Join 31 other followers