2011-07-02 148 views
4

我想讓我的一些javascript更具可測試性,作爲這個的一部分,我在類中封裝了某些功能,所以我可以在我的測試中嘲笑它。在返回函數中包裝jquery ajax

無論如何,我想包裝我的ajax,所以就我的應用程序而言,它是要求服務的對象。在內部它會發出一個ajax請求,然後對數據做一些處理然後返回。

所以會像下面的例子是可能的? (我在此刻的舉動使斜面嘗試爲自己)

function SomeAjaxService(webServiceUrl) 
{ 
    this.getSomeModel = function(someUniqueId){ 
     var ajaxOptions = { 
      url: webServiceUrl, 
      data: {id : someUniqueId}, 
      Success: function(data) { return new SomeModel(data); } 
     }; 
     $.ajax(ajaxOptions); 
    }; 
} 

var ajaxService = new SomeAjaxService("http://someurl"); 
var myModel = ajaxService.getSomeModel(1); 

隨着Ajax在本質上是異步的,我認爲你可以將其設置爲同步,但我只是想檢查哪些選項是。 ..

+0

[見這個答案](https://stackoverflow.com/ a/32136316/7735285),通過一些定製,你將得到一個美妙的** ajax包裝**從任何頁面的任何地方打電話。 – wpcoder

回答

3

將它設置爲同步是一個壞主意,因爲您將鎖定JavaScript(甚至瀏覽器),直到請求返回。您的代碼將不起作用,因爲成功函數是從不同的上下文調用到您的直接getSomeModel方法調用。最好的方法是給你打電話getSomeModel並傳遞一個回調函數,這個函數將在模型被接收時執行。

function SomeAjaxService(webServiceUrl) 
{ 
    this.getSomeModel = function(someUniqueId, callback){ 
     var ajaxOptions = { 
      url: webServiceUrl, 
      data: {id : someUniqueId}, 
      // calls the callback you passed 
      Success: function(data) { callback(new SomeModel(data)); } 
     }; 
     // this is asynchronous and doesn't return anything 
     $.ajax(ajaxOptions); 
    }; 
} 

var ajaxService = new SomeAjaxService("http://someurl"); 
// your callback down here 
var myModel; 
ajaxService.getSomeModel(1, function(model) { 
    myModel = model; 
    // do something with model 
}); 
+0

哦,好吧,回調將是唯一的方法,雖然這很好。 – somemvcperson

+0

JavaScript的美妙之處在於它的異步性。充分利用它! – Finbarr

+1

@Finbarr:確實,雖然技術上它不是JavaScript的本質, JavaScript本身根本沒有定義任何異步特性,它只具有非常強大的特性(函數是第一類對象,閉包),非常適合事件驅動的環境。它是允許事件驅動的環境(瀏覽器,NodeJS等)(或者不像Windows Script Host那樣)。 –

1

可以設置它通過將async: false選項,您的來電是同步的,但你的函數仍然不會返回任何東西,因爲在成功回調return語句從回調返回,而不是主功能。

如果我讀你的權利,然後(與*標記的變化):

function SomeAjaxService(webServiceUrl) 
{ 
    this.getSomeModel = function(someUniqueId){ 
     var rv;             // * 
     var ajaxOptions = { 
      url: webServiceUrl, 
      async: false,           // * 
      data: {id : someUniqueId}, 
      Success: function(data) { rv = new SomeModel(data); } // * 
     }; 
     $.ajax(ajaxOptions); 
     return rv;            // * 
    }; 
} 

但是,而不是讓你的AJAX測試要求同步,這可能也有副作用,使測試無效,我強烈建議讓你的測試框架異步。一個異步測試框架可以執行同步測試;一個同步測試框架不能執行異步測試。所以框架應該是異步的...

我也想強烈建議不要滾動自己的測試框架。有沒有建立自己的a bunch of ones you can choose from


更新:...但如果你真的想建立自己的,下面是我的意思是不是很難使框架異步(live copy)一個很簡單的例子:

jQuery(function($) { 

    $("#theButton").click(function() { 
    var tests, testIndex, nesting; 

    tests = [ 
     function(cb) { display("I'm test1: sync"); cb(1); }, 
     function(cb) { display("I'm test2: async"); setTimeout(function() { 
     cb(2); 
     }, 0); }, 
     function(cb) { display("I'm test3: sync"); cb(3); }, 
     function(cb) { display("I'm test4: async"); setTimeout(function() { 
     cb(4); 
     }, 0); } 
    ]; 

    nesting = 0; 
    testIndex = 0; 
    doTest(); 

    function doTest() { 
     if (testIndex >= tests.length) { 
     return true; // Done 
     } 
     ++nesting; 
     oneTest(tests[testIndex++]); 
     --nesting; 
     return false; 
    } 

    function testDone(result) { 
     display("Result: " + result); 
     if (nesting > 0) { 
     // Completion was called synchronously; schedule next 
     // test asynchronously to avoid recursing too deeply. 
     // You could use a higher number than 0 to allow *some* 
     // recursion for efficiency but avoid letting too much 
     // occur. 
     display("r"); // Just for demonstrating that it happens 
     setTimeout(doTest, 0); 
     } 
     else { 
     // We were already called asynchronously, start next 
     doTest(); 
     } 
    } 

    function oneTest(test) { 
     test(testDone); 
    } 

    }); 

    function display(msg) { 
    $("<p>").html(msg).appendTo(document.body); 
    } 

}); 
0

ajax函數將被調用,但只要getSomeModel不返回任何東西,您將不會獲得成功的someModel實例。解決方案是使用觸發功能。

function SomeAjaxService(webServiceUrl) 
{ 
    this.getSomeModel = function(someUniqueId){ 
     var ajaxOptions = { 
      url: webServiceUrl, 
      data: {id : someUniqueId}, 
      Success: function(data) { model = new SomeModel(data); $.trigger("SomeModelInit", model) } 
     }; 
     $.ajax(ajaxOptions); 
    }; 
} 

var ajaxService = new SomeAjaxService("http://someurl"); 
$.bind("SomeModelInit", function (model) { 
//process 
}) 

另一種方式來測試HTML/JavaScript的/ CSS是selenium

2

我也曾經碰到過這個問題,並明確了回調的方式工作,與從$就返回的jqXHR對象()調用實現Promise接口,因此我可以對done方法進行延遲調用(或者甚至在稍後的時間點更改done方法的功能。這是一個工作示例:

//some separate js file to be included via a <script src=...></script> 
//suppose it's named simpleWrappers.js 
function wrapAjax(dataURL,postData) { 
    postData = (typeof postData === 'string') ? postData : ""; 
    respObj = $.ajax({ 
     url: dataURL 
    , data: postData 
    , dataType: 'json' 
    , type: 'POST' 
    }); 
    return respObj 
} 

現在讓我們包括simpleWrappers.js在一些網頁文件都有自己的文檔準備功能

//... 
<script src="js/simpleWrappers.js"></script> 
//... 
<script> 
    $(document).ready(function() { 
    //... 
    myData = wrapAjax("scripts/returnCoolData.php?fname=fetchSuperCoolData"); 
    //now attach a done handler! 
    myData.done(function(data,statusText,jqXHR) { 
     //we'll do some trivial logging 
     console.log("the status of the request was: " + statusText); 
     //note, my data comes back in JSON, so I JSON.stringify() the data 
     console.log("data returned is: " + JSON.stringify(data)); 
     //Note: don't expect responseText to be available, as in 
     //console.log("responseText was: " + myData.responseText) 
     //may well return "responseText was: undefined, see above answers 
     //to grasp the calling context issue 
    }); 
    } 
</script> 
+0

非常有用和直截了當 – Arnoldiusss