Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Agile Web Development With Rails, 2nd Edition (2006).pdf
Скачиваний:
30
Добавлен:
17.08.2013
Размер:
6.23 Mб
Скачать

TESTING WEB SERVICES 594

An interceptor can be a symbol, in which case it is expected to refer to an instance method. It can also be a block or an object instance. When it’s an object instance, it is expected to have an intercept method.

Instance method before interceptors receive two parameters when called, the method name of the intercepted method and its parameters as an array.

def interceptor(method_name, method_params) false

end

Block and object instance before interceptors receive three parameters. The first is the object containing the web service method, the second the intercepted method name, and the third its parameters as an array.

before_invocation do |obj, method_name, method_params| false

end

After interceptors receive the same initial parameters as before interceptors but receive an additional parameter at the end. This contains the intercepted method return value, since after interceptors execute after the intercepted method has completed.

The before_invocation and after_invocation methods support the :except and :only options. These options take as an argument an array of symbols identifying the method names to limit interceptions to.

before_invocation :intercept_before, :except => [:some_method]

The previous example applies the :intercept_before interceptor to all web service methods except the :some_method method.

25.6Testing Web Services

AWS integrates with the Rails testing framework, so we can use the standard Rails testing idioms to ensure our web services are working correctly.

When we used the web_service generator for the first example, a skeleton functional test was created for us in test/functional/backend_api_test.rb.

This is our functional test, modified to pass on the parameters expected by the example on page 584.

Download e1/depot_ws/test/functional/backend_api_test.rb

require File.dirname(__FILE__) + '/../test_helper' require 'backend_controller'

class BackendController def rescue_action(e)

raise e

Report erratum

TESTING WEB SERVICES 595

end end

class BackendControllerApiTest < Test::Unit::TestCase fixtures :products

def setup

 

 

@controller

= BackendController.new

@request

=

ActionController::TestRequest.new

@response

=

ActionController::TestResponse.new

end

 

 

def test_find_all_products

result = invoke :find_all_products assert result[0].is_a?(Integer)

end

def test_find_product_by_id

product = invoke :find_product_by_id, 2 assert_equal 'Product 2', product.description

end end

This tests the web service methods in BackendController. It performs a complete Action Pack request/response cycle, emulating how our web service will get called in the real world.

The tests use invoke(method_name, *args) to call the web service. The parameter method_name is a symbol identifying the method to invoke, and *args is zero or more parameters to be passed to that method.

The invoke method can test controllers using direct dispatching only. For layered and delegated dispatching, use invoke_layered and invoke_delegated to perform the test invocations. They have identical signatures.

invoke_layered(service_name, method_name, *args) invoke_delegated(service_name, method_name, *args)

In both cases, the service_name parameter refers to the first parameter passed to web_service when declaring the service in the controller.

External Client Applications (SOAP)

When we want to test with external applications on platforms that have a SOAP stack, we should create clients from the WSDL that AWS can generate.

The WSDL file AWS generates declares our web service to use RPC-encoded messages, because this gives us stronger typing. These are also the only type of message AWS supports: Document/Literal messages are not supported.

The default Rails config/routes.rb file creates a route named service.wsdl on our controller. To get the WSDL for that controller, we’d download the file

http://my.app.com/CONTROLLER/service.wsdl

Report erratum

TESTING WEB SERVICES 596

<?xml version="1.0" encoding="UTF-8"?>

<definitions name="Backend" xmlns:typens="urn:ActionWebService" . . .

<types>

<xsd:schema xmlns="http://www.w3.org/2001/XMLSchema" . . .

<xsd:complexType name="Product" > <xsd:all>

<xsd:element name="id" type="xsd:int" /> <xsd:element name="title" type="xsd:string" />

<xsd:element name="description" type="xsd:string" /> <xsd:element name="image_url" type="xsd:string" />

<xsd:element name="price" type="xsd:double" />

<xsd:element name="date_available" type="xsd:dateTime" />

</xsd:all>

</xsd:complexType>

<xsd:complexType name="IntegerArray"> <xsd:complexContent>

<xsd:restriction base="soapenc:Array" >

<xsd:attribute wsdl:arrayType="xsd:int[]" ref="soapenc:arrayType"/> </xsd:restriction>

</xsd:complexContent> </xsd:complexType>

</xsd:schema>

</types>

<message name="FindAllProducts" >

</message>

<message name="FindAllProductsResponse" >

<part name="return" type="typens:IntegerArray"/>

</message>

. . .

Figure 25.4: WSDL Generated by AWS

and use an IDE such as Visual Studio or the appropriate command-line tools like wsdl.exe to generate the client class files. Should we remove the service.wsdl route, an action named wsdl will still exist in the controller.

External Client Applications (XML-RPC)

If our web service uses XML-RPC instead, we have to know what the endpoint URL for it is going to be, because XML-RPC does not have a WSDL equivalent with information on where to send protocol requests. For direct and layered dispatching, the endpoint URL is

http://my.app.com/PATH/TO/CONTROLLER/api

For delegated dispatching, the endpoint URL is

http://my.app.com/PATH/TO/CONTROLLER/SERVICE_NAME

Report erratum

PROTOCOL CLIENTS 597

In this case, SERVICE_NAME refers to the name given as the first parameter to web_service in the controller.

Having two different URLs for these different cases may seem arbitrary, but there is a reason. For delegated and layered dispatching, the information telling us which service object the invocation should be routed to is embedded in the request. For delegated dispatching we rely on the controller action name to determine which service it should go to.

Note that these URLs are used as both the SOAP and XML-RPC message endpoints; AWS is able to determine the type of message from the request.

25.7Protocol Clients

Action Web Service includes some client classes for accessing remote web services. These classes understand Action Web Service API definitions, so if we have the API definition of a remote service, we can access that service with type conversion to and from the correct types occurring automatically for us.

However, these are not general-purpose clients. If our client application is not tightly coupled to the server, it may make more sense to use Ruby’s native SOAP and XML-RPC clients.

If we want to access a remote web service API from inside a controller with the AWS clients, use the web_client_api helper function.

class MyController < ApplicationController web_client_api :product,

:soap,

"http://my.app.com/backend/api"

def list

@products = product.find_all_products.map do |id| product.find_product_by_id(id)

end end

end

The web_client_api declaration creates a protected method named product in the controller. This uses the ProductApi class we created in the first example. Calling the product method returns a client object with all the methods of ProductApi available for execution.

We can also invoke the web service API directly by creating an instance of the client for the relevant protocol (either ActionWebService::Client::Soap or ActionWebService::Client::XmlRpc). We’ll then be able to invoke API methods on this instance.

shop = ActionWebService::Client::Soap.new(ProductApi,

"http://my.app.com/backend/api" ) product = shop.find_product_by_id(5)

Report erratum