如何最好地测试(单元测试)独立于 WL 应用程序的 WL 适配器? + Jasmine 测试:不能使用“in”运算符在 null 中搜索“SUPPORT_COOKIES”
Posted
技术标签:
【中文标题】如何最好地测试(单元测试)独立于 WL 应用程序的 WL 适配器? + Jasmine 测试:不能使用“in”运算符在 null 中搜索“SUPPORT_COOKIES”【英文标题】:How to best Test (Unit Test) WL Adapters independent from WL App? + Jasmine Tests: Cannot use 'in' operator to search for 'SUPPORT_COOKIES' in null 【发布时间】:2013-07-13 00:03:39 【问题描述】:与这个问题有关: Uncaught TypeError: Cannot use 'in' operator to search for 'SUPPORT_COOKIES' in null
但由于 Jasmine 测试工具和关于 JS-Java 适配器测试的最佳实践的一般问题而有所不同。
我们正在努力为一组开发人员建立一个开发环境,包括持续集成、构建和自动化测试。
为此,我们需要有一种方法可以从独立于实际 Worklight 应用程序或任何 Worklight 客户端运行的独立测试用例/客户端(测试代码)调用 WL 服务器上的 WL 适配器。 这些测试必须在部署和持续构建时运行,并将测试适配器。
我们提出了这个解决方案,因为在部署适配器之前无法在本地测试适配器。此外,我们不能真正将测试代码包含到我们的适配器中并将代码与适配器一起部署。这不是一个好的解决方案,我们将在带有适配器的服务器上测试代码。
我们可能会使用 Jasmine 和 JUnit 作为我们的测试工具,我尝试通过包含所有 Worklight JS 库和变量来设置独立的 Jasmine Worklight Client/Test (worklight 编译器添加到最终编译和部署的 App .html 中)到我的 Jasmine 测试中。
它运行了一段时间,似乎初始化正常:
wlclient init started worklight.js:1112
before: app init onSuccess worklight.js:1112
after: app init onSuccess worklight.js:1112
wlclient init success
但是当我想执行 WL.Client.invokeProcedure(invocationData, 我得到错误:
TypeError:无法使用“in”运算符在 null 中搜索“SUPPORT_COOKIES”
因此,我的独立 Worklight 客户端/测试中似乎缺少一些配置或初始化。 有哪位专家能告诉我那可能是什么吗?
<head>
<title>Jasmine Spec Runner</title>
<link rel="stylesheet" type="text/css" href="lib/jasmine-1.3.1/jasmine.css">
<script>
// Define WL namespace.
var WL = WL ? WL : ;
/**
* WLClient configuration variables.
* Values are injected by the deployer that packs the gadget.
*/
WL.StaticAppProps =
"APP_DISPLAY_NAME": "app",
"APP_SERVICES_URL": "\/tests\/",
"APP_VERSION": "1.0",
"ENVIRONMENT": "preview",
"HEIGHT": 460,
"LOGIN_DISPLAY_TYPE": "popup",
"LOGIN_POPUP_HEIGHT": 610,
"LOGIN_POPUP_WIDTH": 920,
"PREVIEW_ENVIRONMENT": "common",
"WIDTH": 320,
"WORKLIGHT_ROOT_URL": "\/tests\/"
;
<script src="lib/common/js/wljq.js"></script>
<script src="lib/common/js/base.js"></script>
<script src="lib/common/js/containerCommunicationAPI.js"></script>
<script src="lib/wlclient/js/messages.js"></script>
<script src="lib/common/js/wlcommon.js"></script>
<script src="lib/common/js/busy.js"></script>
<script src="lib/wlclient/js/diagnosticDialog.js"></script>
<script src="lib/wlclient/js/deviceAuthentication.js"></script>
<script src="lib/wlclient/js/window.js"></script>
<script src="lib/wlclient/js/worklight.js"></script>
<script src="lib/wlclient/js/gadgetCommunicationAPI.js"></script>
<script src="lib/wlclient/js/wlclient.js"></script>
<script src="lib/wlclient/js/wlfragments.js"></script>
<script src="lib/wlclient/js/encryptedcache.js"></script>
<script src="lib/wlclient/js/jsonstore/jsonstore.js"></script>
<script src="lib/wlclient/js/challengeHandlers/antiXSRFChallengeHandler.js"></script>
<script src="lib/wlclient/js/challengeHandlers/authenticityChallengeHandler.js"></script>
<script src="lib/wlclient/js/challengeHandlers/deviceAuthAutoProvisioningChallengeHandler.js"></script>
<script src="lib/wlclient/js/challengeHandlers/deviceAuthNoProvisioningChallengeHandler.js"></script>
<script src="lib/wlclient/js/challengeHandlers/remoteDisableChallengeHandler.js"></script>
<script src="../apps/app/common/js/jquery-1.10.1.min.js"></script>
<!-- script>window.$ = window.jQuery = WLJQ;</script-->
<script src="../apps/app/common/jqueryMobile/jquery.mobile-1.3.1.js"></script>
<script src="../apps/app/common/js/initOptions.js"></script>
<script src="../apps/app/common/js/messages.js"></script>
<script type="text/javascript" src="lib/jasmine-1.3.1/jasmine.js"></script>
<script type="text/javascript" src="lib/jasmine-1.3.1/jasmine-html.js"></script>
<!-- include source files here... -->
<!--<script type="text/javascript" src="../apps/app/common/js/knockout-2.2.1.js"></script>-->
<!--<script type="text/javascript" src="../apps/app/common/js/knockout.mapping-latest.js"></script>-->
<!--<script type="text/javascript" src="../apps/app/common/js/globalize.js"></script>-->
<!--<script type="text/javascript" src="../apps/app/common/js/app.js"></script> -->
<!--<script type="text/javascript" src="../apps/app/common/js/common.js"></script>-->
<!--<script type="text/javascript" src="../apps/app/common/js/date.js"></script>-->
<!--<script type="text/javascript" src="../apps/app/common/js/localize.js"></script>-->
</script>-->
<!-- include spec files here... -->
<!--script type="text/javascript" src="spec/SpecHelper.js"></script-->
<script type="text/javascript" src="spec/TestSpec.js"></script>
2013 年 7 月 15 日:
以这种方式让 Jasmine 在 WL 客户端中自动运行:
var wlInitOptions =
connectOnStartup : false,
;
if (window.addEventListener)
window.addEventListener('load', function() WL.Client.init(wlInitOptions); execJasmine(); , false);
else if (window.attachEvent)
window.attachEvent('onload', function() WL.Client.init(wlInitOptions); execJasmine(); );
function wlCommonInit()
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;
var htmlReporter = new jasmine.HtmlReporter();
jasmineEnv.addReporter(htmlReporter);
jasmineEnv.specFilter = function(spec)
return htmlReporter.specFilter(spec);
;
function execJasmine()
WL.Logger.debug("ExecJasmine");
jasmineEnv.execute();
但现在我遇到了访问控制问题,因为我的 WL-Jasmine-Test-Client 运行在端口 80 上的 Apache 和 上的适配器上端口 8080 上的 WL 服务器。
running test worklight.js:1112
Application did not define an i18n messages object, skipping translation. worklight.js:1112
wlclient init started worklight.js:1112
before: app init onSuccess worklight.js:1112
after: app init onSuccess worklight.js:1112
wlclient init success worklight.js:1112
ExecJasmine worklight.js:1112
Request [http://XXX:8080/apps/services/api/app/desktopbrowser/query] worklight.js:1112
running test 2 worklight.js:1112
OPTIONS http://XXX:8080/apps/services/api/app/desktopbrowser/query 401 (Unauthorized) base.js:883
OPTIONS http://XXX:8080/apps/services/api/app/desktopbrowser/query Origin http://XXX is not allowed by Access-Control-Allow-Origin. base.js:883
XMLHttpRequest cannot load http://XXX:8080/apps/services/api/app/desktopbrowser/query. Origin http://XXX is not allowed by Access-Control-Allow-Origin. SpecRunnerAdapter.html:1
Refused to get unsafe header "X-JSON" base.js:994
[http://XXX:8080/apps/services/api/app/desktopbrowser/query] Host is not responsive. worklight.js:1112
"invocationContext":null,"errorCode":"UNRESPONSIVE_HOST","errorMsg":"The service is currently not available." worklight.js:1112
Refused to get unsafe header "X-JSON"
使用此 WL-Test-Client 应用配置:
WL.StaticAppProps =
"APP_DISPLAY_NAME": "app",
"APP_SERVICES_URL": "http:\/\/XXX:8080\/apps\/services\/",
"APP_VERSION": "1.0",
"ENVIRONMENT": "desktopbrowser",
"HEIGHT": 460,
"LOGIN_DISPLAY_TYPE": "popup",
"LOGIN_POPUP_HEIGHT": 610,
"LOGIN_POPUP_WIDTH": 920,
"WIDTH": 320,
"WORKLIGHT_ROOT_URL": "http:\/\/XXX:8080\/apps\/services\/api\/app\/desktopbrowser\/"
;
我想我们就到此为止,简单地使用 WL-Server 上的 INVOKE 服务来调用 wl_unprotected Adapter Procedures 以进行我们在测试环境中的 JUnit 测试。 p>
然后弄清楚如何在构建/部署时保护生产过程,同时删除单元测试执行 - 因为它们不再适用于受保护的适配器。
【问题讨论】:
【参考方案1】:对于单元测试,您最好的选择是使用 WL 适配器调用服务。根本没有客户端代码,只需向 WL 服务器发出 HTTP(s) 请求:
http://pic.dhe.ibm.com/infocenter/wrklight/v6r0m0/index.jsp?topic=%2Fcom.ibm.worklight.help.doc%2Fdevref%2Fc_adapter_invocation_service.html
【讨论】:
感谢您的回答。从典型的 eclipse 开发工作中,我们当然知道调用服务。这对于开发和初始测试非常有用,但不适合持续构建。 E 特别是由于您必须更改适配器的安全设置的要求,例如安全测试=“wl_unprotected”。我们需要将适配器部署在具有所有安全性、领域等的生产就绪配置中。 首先,您不必使用 wl_unprotected,这只是为了方便。但是请注意,如果您不使用它 - 您必须在模拟客户端中实现客户端逻辑。此外,适配器调用服务不仅仅是开发,它是 WL 服务器的完全生产功能。 感谢您的回答。我明白了,我在阅读 InfoCenter 时没有意识到这一点,谢谢。现在,如果能从移动安全测试(如 XSRF 等)和 Java 中的自定义 AdapterAuthenticator ChallengeHandler 中获得一些关于如何处理标准 WL 安全特性的示例当然会很棒——但我假设没有提供类似的东西。 - 也许我们会使用 wl_unprotected 并弄清楚如何维护两个适配器配置文件。谢谢。 ibm.com/developerworks/community/blogs/worklight/entry/…【参考方案2】:假设您需要 单元测试 为您的适配器代码,如指定的问题的标题,我会注意以下几点:
单元测试的范围是单个代码单元,在 JavaScript 中通常意味着一个小函数。你测试sum(1,2)
是否返回3
。您确实不会自动打开显示您的计算器应用程序的 URL,模拟用户输入以单击按钮(1
、2
和 =
),等待计算事件返回 @987654333 @ 并且测试代码从 DOM 读取输出。前者描述了单元测试,后者描述了集成/功能测试。如果您需要功能测试,请阅读this。如果您想要单元测试,请继续阅读。
模拟与第三方 API 相关的所有内容(例如 Worklight、jQuery、Dojo)。这些 API 已经过测试并且已知可以工作,至少这是一个合理的假设。例如,当您编写 JUnit 测试时,您假设标准 Java 库中的所有内容都按照文档中指定的方式工作。 Sinon.js 是一个很棒的库,用于创建 JavaScript 模拟、存根和间谍。
使用Mozilla Rhino 或Node.js 读取和eval 用于适配器的JavaScript 实现文件的JavaScript(例如myCalculatorAdapter-impl.js
)。有很多创建断言的好方法,例如Mocha,如果您使用 Node.js 路线。
这是一些示例代码,想象一下这是我的适配器程序:
myCalculatorAdapter-impl.js
function sum (a, b)
WL.Logger.debug(hello: 'world');
return a+b;
想象以下包含我对所述适配器过程的单元测试:
test.js
//Mocks
var WL = ;
WL.Logger = ;
WL.Logger.debug = function (msg)
console.log(msg);
;
//Node.js Libraries
var fs = require('fs'),
assert = require('assert');
//Read the adapter code
eval(fs.readFileSync('./myCalculatorAdapter-impl.js').toString());
//Do assertions -- Unit testing
assert.equal(sum(1, 2), 3, '1+2 should be 3');
我运行它:node test.js
。如果我在运行时将实现更改为减法而不是加法 (return a-b
),我会收到失败通知 AssertionError: 1+2 should be 3
。
我使用了 Node.js 提供的 standard assertion 库,您可能想要使用具有更多功能的东西。同样,您可能希望使用库来模拟第三方 API 或不适用于当前测试的东西。如果您正在进行网络通信,您将需要模拟输入。
当然还有其他方法可以测试适配器,请随意尝试和分享。
【讨论】:
感谢您的回答。您是完全正确的,单元测试和功能测试之间的定义在我的问题中非常融合,但这是因为我们的适配器具有需要测试的代码单元,它们也使用编译的适配器 Java 代码来执行它们功能。 JS 运行时 Rhino 和 Node 的方法对于 JS 单元测试来说非常优雅……感谢您对方法的想法和细节。 尽管如此,由于我们需要一起测试 AdapterJS 和 AdapterJava 单元,我们可能不得不在本地使用 Mozilla Rhino 来处理 JSJava ...不确定这是否是矫枉过正将 Rhino 包含在我们的测试基础设施中“只是为了测试适配器”。我认为使用真正的 WL 服务器,它是 Rhino,至少 JSJava 功能在测试时更加优雅和轻量级。此外,通过将我们自己的 Rhino 引入测试,我们当然会创建与 WL 服务器上不同的基础架构 - 测试无法具有代表性。 感谢您在 WL 6 中使用 WL Test Workbench 进行功能测试的链接。很抱歉,我忘了提到我们的项目中不会迁移到 6。我们将继续使用 5.0.6,至少在第一个版本之前。 我建议使用只做一件事的小函数。也许您可以将 Java 代码调用抽象为可以模拟的 JavaScript 函数。这样你就可以用 Node 或 Rhino 测试 JavaScript 代码,用 JUnit 测试 Java 代码。 感谢您的回复。我们确实有做一件事的小函数——但是一件复杂的事情需要使用例如 Java 日历和 TimeZones 来进行转换/计算等——模拟所有的 Java 代码将是太多的工作——我们会看到,也许我们使用 INVOKE 服务,那么我们也可以测试功能性——也许我们会找到其他方法来拆分 JS 和 Java……但这几乎是不可能的,因为它们相互交织。感谢您的建议和cmets。以上是关于如何最好地测试(单元测试)独立于 WL 应用程序的 WL 适配器? + Jasmine 测试:不能使用“in”运算符在 null 中搜索“SUPPORT_COOKIES”的主要内容,如果未能解决你的问题,请参考以下文章
如何最好地对使用 IServiceGateway 调用其他内部服务的 ServiceStack 服务进行单元测试