测试JS API Lib的好方法

时间:2014-02-20 16:08:57

标签: javascript web-services api unit-testing testing

实际上我正在编写一个小的JS lib来封装一些GET和POST调用到外部Web服务。有没有一种好方法/最佳实践如何测试这样的库? 我应该针对GET函数针对API编写测试吗?我应该在测试目录中放置示例响应吗?

P.S。:我无法向API发送POST请求,因为它是“LIVE”系统。

1 个答案:

答案 0 :(得分:0)

你会想要使用某种单元测试框架,正如@BrianDriscoll评论的那样。那里有很多。

我也遇到了这个问题,我没有找到适合JavaScript的AJAX模拟框架。我最终创建了 Mocking Bird

使用MockingBird模拟请求的优势

  • 它为您提供了对URL,HTTP方法,状态,请求标头,响应标头和响应正文的精细控制
  • 很容易禁用所有本机XMLHttpRequest调用并插入支持相同接口的模拟对象而不更改测试中的代码
  • 启用原生XMLHttpRequest很容易,但创建模拟请求并将其注入测试方法
  • AJAX变得同步,使单元测试更容易。
  • MockingBird可以与任何单元测试框架一起使用
  • MockingBird没有外部依赖

<强>缺点

  • AJAX变得同步。被测代码有可能表现不同
  • 目前无法测试连接超时
  • 目前无法测试分块的HTTP响应
  • 您实际上并没有使用Web服务进行端到端测试,这需要进行额外的集成测试(无论如何您应该这样做)。
  • 使用MockingBird禁用网络连接时,任何AJAX请求都不起作用。
  • 它不会强制执行相同的域策略。您可以模拟通过单元测试的跨域AJAX调用,但在集成测试中会抛出错误。

一个快速而又肮脏的例子:

var request = new MockingBird.XMLHttpRequest()
    .returnsStatus(200)
    .returnsBody('{"message":"I am a teapot"}')
    .returnsHeaders({
        "Content-Type": "text/json"
    });

callSomeFunctionThatUsesAjax(request);

function callSomeFunctionThatUsesAjax(xhr) {
    xhr.onreadystatechange = function() {
        ...
    };

    xhr.open("POST", "/foo");
    xhr.send(null);
}

如果无法提供自己的AJAX对象,也可以为案例设置模拟调用:

MockingBird.XMLHttpRequest.disableNetworkConnections()
    .mock("/posts/123", "GET", {
        status: 404,
        body: "Page not found"
    })
    .mock("/posts/321/comments", "POST", {
        status: 201,
        responseHeaders: {
            "Content-Type": "text/json"
        },
        body: {
            post: {
                id: 321,
                comment: {
                    id: 1234
                }
            }
        }
    });

现在,直接实例化XMLHttpRequest对象的代码甚至不需要知道MockingBird存在:

function createComment(postId, text) {
    var xhr = new XMLHttpRequest();

    xhr.onreadystatechange = function() {
        if (this.readyState !== 4) return;

        if (this.status === 201) {
            var data = JSON.parse(this.responseText);

            alert("Created comment: " + data.post.comment.id);
        }
        else if (this.status === 404) {
            alert("Post " + postId + " not found!");
        }
    };

    xhr.open("POST", "/posts/" + postId);
    xhr.send("post[id]=" + postId + "&post[comment][text]=" + escape(text));
}

createComment(123, "Test Test"); // alert's "Post 123 not found!"
createComment(321, "Test Test"); // alert's "Created comment: 1234"