如何在测试模块中修改XQuery常量模块变量以测试库函数?
问题:如何在XQuery测试中修改常量模块的变量以测试依赖函数?
我有一个库模块(lib),其函数依赖另一个常量模块(const)中的变量。现在想在测试模块中测试lib的函数,尝试修改const模块的变量来进行特定测试,但尝试重置$con:numbers或用replace node语句修改都无效。想请教是否可以修改$const:numbers,让后续调用lib:inc5时使用修改后的值?
相关代码如下:
const模块
xquery version "3.1" encoding "utf-8"; module namespace const = "Constant"; declare variable $const:numbers:= <numbers> <value s='one'>1</value> <value s='two'>2</value> <value s='three'>3</value> <value s='four'>4</value> <value s='five'>5</value> <value s='other'></value> </numbers>;
lib模块
xquery version "3.1" encoding "utf-8"; module namespace lib = "Library"; import module namespace con="Constant" at "const.xqm"; declare function lib:inc5($val as xs:string) as xs:integer { 5+xs:integer($con:numbers//value[@s=$val]) };
test模块
xquery version "3.1" encoding "utf-8"; module namespace test='http://basex.org/modules/xqunit-tests'; import module namespace con="Constant" at "const.xqm"; import module namespace lib="Library" at "lib.xqm"; declare %unit:test function test:inc5_add_one() { unit:assert-equals(lib:inc5('one'), 6) }; declare %unit:test function test:inc5_set_other_as_10_add_ten() { (: replace node $con:numbers//value[@s='other'] with <value s='ten'>10</value> :) (: $con:numbers=<numbers> <value s='one'>1</value> <value s='two'>2</value> <value s='three'>3</value> <value s='four'>4</value> <value s='five'>5</value> <value s='ten'>10</value> </numbers>, :) unit:assert-equals(lib:inc5('ten'), 15) };
解答
核心原因:默认模块变量是只读的
XQuery中,用declare variable声明的模块全局变量默认是只读的,无法直接赋值修改,也不能通过普通的节点更新语句修改(除非显式声明为可更新)。这就是你尝试修改无效的原因。
推荐方案:重构lib函数,增加可选数据源参数
这是最干净、无副作用的测试方式——让lib:inc5函数接受一个可选的$numbers参数,默认使用常量模块的值,测试时传入自定义的测试数据即可:
修改后的lib模块
xquery version "3.1" encoding "utf-8"; module namespace lib = "Library"; import module namespace con="Constant" at "const.xqm"; declare function lib:inc5($val as xs:string, $numbers as node()? := $con:numbers) as xs:integer { 5 + xs:integer($numbers//value[@s=$val]) };
修改后的测试函数
declare %unit:test function test:inc5_set_other_as_10_add_ten() { let $test-numbers := <numbers> <value s='one'>1</value> <value s='two'>2</value> <value s='three'>3</value> <value s='four'>4</value> <value s='five'>5</value> <value s='ten'>10</value> </numbers> return unit:assert-equals(lib:inc5('ten', $test-numbers), 15) };
这种方式不修改全局状态,测试之间完全隔离,不会互相干扰,也不需要依赖处理器的更新特性。
备选方案:使用可更新变量(需处理器支持)
如果你的XQuery处理器(比如BaseX)支持更新特性,可以将常量模块的变量声明为可更新的,然后在测试中修改并恢复:
修改后的const模块
xquery version "3.1" encoding "utf-8"; module namespace const = "Constant"; declare %updating variable $const:numbers := <numbers> <value s='one'>1</value> <value s='two'>2</value> <value s='three'>3</value> <value s='four'>4</value> <value s='five'>5</value> <value s='other'></value> </numbers>;
修改后的测试函数
declare %unit:test function test:inc5_set_other_as_10_add_ten() { (: 先保存原始值,测试后恢复,避免影响其他测试 :) let $original := $con:numbers do { (: 替换目标节点 :) replace node $con:numbers//value[@s='other'] with <value s='ten'>10</value>, unit:assert-equals(lib:inc5('ten'), 15), (: 恢复原始状态 :) replace node $con:numbers with $original } };
注意:这种方式会修改全局变量状态,必须在测试后恢复原始值,否则会干扰其他测试用例,不推荐作为首选方案。
内容的提问来源于stack exchange,提问作者user18451290
相关产品推荐
相关产品推荐

