我们只是在一个真实的服务上运行我们的应用程序,但是对于开发和测试我们的应用程序,我们不希望依赖于“真实”服务的可用性,或者在数据服务所在的系统上增加额外的负载。
这个系统就是所谓的后端系统,我们现在将使用一个称为mock server的SAPUI5特性来模拟它。它为本地文件提供服务,但它模拟后端系统比加载本地数据更实际。我们还将更改模型实例化部分,以便模型在描述符中配置,并由SAPUI5自动实例化。这样,我们就不需要关心代码中的模型实例化。
Preview
The list of invoices is now served by the Mock Server
Coding
You can view and download all files at Walkthrough - Step 27.
Folder Structure for this Step
在这一步之后,我们app项目的文件夹结构将测试文件和生产文件清晰地分开。新的测试文件夹现在包含一个新的HTML页面mockServer。它将在测试模式下启动我们的应用程序,而无需调用真正的服务。
新的localService文件夹包含一个元数据。用于模拟服务器OData的xml服务描述文件。使用本地数据模拟实际服务的 mockserver.js文件,以及包含本地测试数据的mockdata子文件夹(invoice .json)。
webapp/test/mockServer.html (New)
<!DOCTYPE html> <html> <head> <metahttp-equiv="X-UA-Compatible"content="IE=edge"> <metacharset="utf-8"> <title>SAPUI5 Walkthrough - Test page</title> <script id="sap-ui-bootstrap" src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js" data-sap-ui-theme="sap_belize" data-sap-ui-libs="sap.m" data-sap-ui-compatVersion="edge" data-sap-ui-preload="async" data-sap-ui-resourceroots='{ "sap.ui.demo.walkthrough": "../" }'> </script> <script> sap.ui.getCore().attachInit(function(){ sap.ui.require([ "sap/ui/demo/walkthrough/localService/mockserver", "sap/m/Shell", "sap/ui/core/ComponentContainer" ],function(mockserver,Shell,ComponentContainer){ mockserver.init(); newShell({ app :newComponentContainer({ name :"sap.ui.demo.walkthrough", settings :{ id :"walkthrough" } }) }).placeAt("content"); }); }); </script> </head> <bodyclass="sapUiBody"id="content"> </body> </html>
复制index.html到webapp/test文件夹中的一个单独文件,并将其命名为mockServer.html。现在我们将使用这个文件在测试模式下运行我们的应用程序,从JSON文件加载模拟数据。测试页面不应该放在应用程序根文件夹中,而应该放在测试文件夹中,以便清楚地将生产代码和测试代码分开。
从现在开始,您有两个不同的入口页面:一个用于真正的“连接”应用程序(index.html),另一个用于本地测试(mockServer.html)。您可以自由决定是对实际服务数据还是对应用程序中的本地数据执行下一步操作。
请注意:如果到实际服务的连接不可用,或者前面步骤中的代理配置不起作用,则始终可以使用mockServer.html文件。这将显示带有模拟测试数据的应用程序。index.html文件总是从远程服务器加载数据。如果请求失败,发票列表将保持空。
webapp/test/mockServer.html
<!DOCTYPE html> <html> <head> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta charset="utf-8"> <title>Walkthrough - Test page</title> <script id="sap-ui-bootstrap" src="/resources/sap-ui-core.js" data-sap-ui-theme="sap_belize" data-sap-ui-libs="sap.m" data-sap-ui-bindingSyntax="complex" data-sap-ui-compatVersion="edge" data-sap-ui-preload="async" data-sap-ui-resourceroots='{ "sap.ui.demo.walkthrough": "../" }'> </script> <script> sap.ui.getCore().attachInit(function () { sap.ui.require([ "sap/ui/demo/walkthrough/localService/mockserver", "sap/m/Shell", "sap/ui/core/ComponentContainer" ],function(mockserver,Shell,ComponentContainer){ mockserver.init(); new Shell({ app : new ComponentContainer({ height: "100%", name: "sap.ui.demo.walkthrough" }) }).placeAt("content"); }); }); </script> </head> <body class="sapUiBody" id="content"> </body> </html>
我们修改mockServer.html文件,并更改页面标题,以将其与生产启动页区分开来。在引导过程中,由于mockServer的原因,data-sap-ui-resourceroots属性也略有更改。html文件不再直接在webapp文件夹中。
此外,我们将组件的初始化切换到sap.ui.require语法,因为我们现在需要加载更多启动应用程序所需的额外文件。第一个依赖项是名为mockserver.js的文件。稍后将位于localService文件夹中的。我们还切换到require语句为实例化Shell和ComponentContainer提供的依赖项,而不是将完整的名称空间用于sap.m.Shell和sap.ui.core.ComponentContainer。
新mockserver.js我们刚刚加载并即将实现的资源是我们的本地测试服务器。它的init方法在我们实际定义组件之前立即被调用。通过这种方式,我们可以捕获所有将进入“real”服务的请求,并在使用mockServer.html文件启动应用程序时通过测试服务器本地处理它们。组件本身并不“知道”它现在将在测试模式下运行。
模拟服务器不需要从代码中的任何其他地方调用,因此我们使用sa .ui。需要异步加载依赖项,而不需要定义全局名称空间。
webapp/localService/mockdata/Invoices.json (New)
[
{
"ProductName":"Pineapple",
"Quantity":21,
"ExtendedPrice":87.2000,
"ShipperName":"Fun Inc.",
"ShippedDate":"2015-04-01T00:00:00",
"Status":"A"
},
{
"ProductName":"Milk",
"Quantity":4,
"ExtendedPrice":9.99999,
"ShipperName":"ACME",
"ShippedDate":"2015-02-18T00:00:00",
"Status":"B"
},
{
"ProductName":"Canned Beans",
"Quantity":3,
"ExtendedPrice":6.85000,
"ShipperName":"ACME",
"ShippedDate":"2015-03-02T00:00:00",
"Status":"B"
},
{
"ProductName":"Salad",
"Quantity":2,
"ExtendedPrice":8.8000,
"ShipperName":"ACME",
"ShippedDate":"2015-04-12T00:00:00",
"Status":"C"
},
{
"ProductName":"Bread",
"Quantity":1,
"ExtendedPrice":2.71212,
"ShipperName":"Fun Inc.",
"ShippedDate":"2015-01-27T00:00:00",
"Status":"A"
}
]
清理旧Invoices.json文件从webapp文件夹,它已不再使用。 Invoices.json 文件类似于webapp文件夹中的前一个文件。只需复制内容并删除带有关键发票的外部对象结构,以便该文件由一个包含发票项的平面数组组成。此文件将在本步骤稍后由服务器自动读取。
webapp/localService/metadata.xml (New)
<edmx:EdmxVersion="1.0"xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx"> <edmx:DataServicesm:DataServiceVersion="1.0"m:MaxDataServiceVersion="3.0" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"> <SchemaNamespace="NorthwindModel"xmlns="http://schemas.microsoft.com/ado/2008/09/edm"> <EntityTypeName="Invoice"> <Key> <PropertyRefName="ProductName"/> <PropertyRefName="Quantity"/> <PropertyRefName="ShipperName"/> </Key> <PropertyName="ShipperName"Type="Edm.String"Nullable="false"MaxLength="40"FixedLength="false" Unicode="true"/> <PropertyName="ProductName"Type="Edm.String"Nullable="false"MaxLength="40"FixedLength="false" Unicode="true"/> <PropertyName="Quantity"Type="Edm.Int16"Nullable="false"/> <PropertyName="ExtendedPrice"Type="Edm.Decimal"Precision="19"Scale="4"/> </EntityType> </Schema> <SchemaNamespace="ODataWebV2.Northwind.Model"xmlns="http://schemas.microsoft.com/ado/2008/09/edm"> <EntityContainerName="NorthwindEntities"m:IsDefaultEntityContainer="true"p6:LazyLoadingEnabled="true" xmlns:p6="http://schemas.microsoft.com/ado/2009/02/edm/annotation"> <EntitySetName="Invoices"EntityType="NorthwindModel.Invoice"/> </EntityContainer> </Schema> </edmx:DataServices> </edmx:Edmx>
元数据文件包含关于服务接口的信息,不需要手工编写。可以通过调用服务URL并在末尾添加$metadata(例如,在我们的示例http://services.odata.org/v2/northwind/northwind.svc/$ metadata)直接从“真实”服务访问它。mock服务器将读取这个文件来模拟实际的OData服务,并以适当的格式返回来自本地源文件的结果,以便应用程序可以使用它(XML或JSON格式)。
为了简单起见,我们从原来的Northwind OData元数据文档中删除了场景中不需要的所有内容。我们还将status字段添加到元数据中,因为它在真正的Northwind服务中不可用。
webapp/localService/mockserver.js (New)
sap.ui.define([ "sap/ui/core/util/MockServer" ],function(MockServer){ "use strict"; return{ init:function(){ // create var oMockServer =newMockServer({ rootUri:"https://services.odata.org/V2/Northwind/Northwind.svc/" }); var oUriParameters = jQuery.sap.getUriParameters(); // configure MockServer.config({ autoRespond:true, autoRespondAfter: oUriParameters.get("serverDelay")||1000 }); // simulate var sPath = jQuery.sap.getModulePath("sap.ui.demo.walkthrough.localService"); oMockServer.simulate(sPath +"/metadata.xml", sPath +"/mockdata"); // start oMockServer.start(); } }; });
现在我们已经添加了OData服务描述文件元数据。在xml文件中,我们可以编写代码来初始化模拟服务器,然后模拟对真正的Northwind服务器的任何OData请求。
我们将MockServer模块作为依赖项加载,并创建一个helper对象,该对象定义一个init方法来启动服务器。此方法在mockServer.html文件中的组件初始化之前调用init方法,使用与实际服务调用相同的URL创建一个MockServer实例。
配置参数rootURI.json中的URL文件描述符必须与清单中为数据源定义的uri完全相同。这可以是一个绝对URL,也可以是一个相对URL,例如在SAP Web IDE中。URL现在将由我们的测试服务器而不是实际服务提供。接下来,我们设置两个全局配置设置,告诉服务器自动响应,并引入1秒延迟来模拟典型的服务器响应时间。否则,我们将不得不手动调用MockServer上的response方法来模拟调用。
要模拟服务,只需在MockServer实例上调用模拟方法,并提供到新创建的元数据.xml的路径。这将从本地文件系统读取测试数据,并设置URL模式来模拟实际服务。
最后,我们调用oMockServer上的start。从现在开始,对URL模式rootURI的每个请求都将由MockServer处理。如果你从索引中切换。到mockServer的html文件。浏览器中的html文件,现在您可以看到测试数据再次从本地源显示出来,但是延迟较短。延迟可以用URI参数serverDelay指定,默认值是1秒。
这种方法非常适合本地测试,即使没有任何网络连接。这样,您的开发就不依赖于远程服务器的可用性,即运行测试。
尝试使用索引调用应用程序。html文件和模型服务器。查看html文件的区别。如果无法进行真正的服务连接,例如在没有网络连接的情况下,您总是可以回到本地测试页面。
Conventions
▪webapp/test文件夹只包含非生产性代码。
▪Mock数据和启动MockServer的脚本存储在webapp/localService文件夹中。
▪启动MockServer的脚本称为MockServer .js。
Parent topic: Walkthrough
Previous: Step 26: (Optional) Remote OData Service
Next: Step 28: Unit Test with QUnit
Related Information