Invoking “Apex WSDL” Web Services from Apex Code
Because of Tolerado, I came to know about “Apex Programming WSDL” given by Salesforce. Apex WSDL on a high level, provides web service APIs for:
Running Apex tests.
Execute anonymous code.
Compiling classes and triggers
How to download this WSDL?
Apex programming WSDL can be downloaded from the “Setup” area of your Salesforce org. Just land on the “App Setup > Develop > API” page. Instructions to download are shown below:
Integrating with “Apex Programming WSDL”!
One can compile/consume this WSDL in various programming languages like Java/PHP/.NET/etc. Post-compilation, one can easily create mashup applications to behave like mini Cruise Control for Apex, those that can run nightly tests for your Salesforce org, etc. The only problem with those applications is they need to be hosted and maintained by one, not really on CLOUD unless one is developing it.
Compiling “Apex WSDL” to generate Apex code stubs (WSDL > Apex)
Fortunately, one can also generate Apex code stubs for a given WSDL i.e. force.com platform gives you the option to do “WSDL > Apex” compilation.
Using this feature, we can bring the whole bunch of web services exposed by “Apex WSDL” to Apex code directly.
So, let's hit the button shown in the above screenshot, i.e. “Generate from WSDL” and specify the downloaded “apex.wsdl” file here, as shown below:
In the next step, you will be asked to give a good name to the generated Apex class; by default, it will come with something like “soapSforceCom200608Apex”. Detailed instructions are shown below:
Next, you should see a screen like the following, giving you the link to the newly generated/compiled Apex class from “Apex WSDL”.
Next, we will see how to invoke Apex WSDL web services from Apex code directly.
Invoke Apex WSDL web services from Apex Code
In this section, I am assuming you have already generated the “ApexStub” class by compiling Apex WSDL, as described above.
Step 1 – Know your web service endpoint!
As we are going to make web service calls from Apex code, we need to whitelist the web service endpoints first. There are two ways to know your web service endpoint:
1. Check the location attribute of <soap:address location=”https://….”> in Apex WSDL file; it should have something like this. So for my org, the web service endpoint to white list is “https://ap1-api.salesforce.com/services/Soap/s/20.0”
<soap:address location="https://ap1-api.salesforce.com/services/Soap/s/20.0"/>
2. You can also check your endpoint directly in the ApexStub class. “ApexStub.Apex.endpoint_x” property points to the web service endpoint.
Step 2 – Whitelist the web service endpoint.
To whitelist, go to “Setup > Administration Setup > Security Controls > Remote Site Settings” and create a “New Remote Site” using the domain details from the endpoint captured in the steps above. Make sure the remote site URL is as per your SFDC org, my org is on node “ap1”, yours can be different, i.e. on NA1, etc. My security settings are shown below:
Step 3 – Coding time, make the web service call!
Out of all the web service calls given by Apex WSDL, the most interesting for me is runTests(). This allows one to run tests on either a single, bunch, or all apex classes in SFDC org.
Run All Tests Sample
Here is the sample code that uses the “ApexStub” class generated above to run all tests in the org:
ApexStub.Apex ap = new ApexStub.Apex();
ap.SessionHeader = new ApexStub.SessionHeader_element ();
// This is important, give session id, to let the call work.
ap.SessionHeader.sessionId = UserInfo.getSessionId();
ApexStub.RunTestsRequest rtr = new ApexStub.RunTestsRequest();
// Run All Tests
rtr.allTests = true;
rtr.Namespace = '';
// Execute the Tests
ApexStub.RunTestsResult testResults = ap.runTests(rtr);
System.debug("Failures >" + testResults.numFailures);
Running tests on a Single or a bunch of classes
// You can also specify single class in this array
String[] classNames = new String [] {'TestFeature1', 'TestFeature2'};
// or specify package or org wide
// namespace here
String namespace = '';
ApexStub.Apex ap = new ApexStub.Apex();
ap.SessionHeader = new ApexStub.SessionHeader_element ();
ap.SessionHeader.sessionId = UserInfo.getSessionId();
ApexStub.RunTestsRequest rtr = new ApexStub.RunTestsRequest();
rtr.classes = classNames;
rtr.allTests = false;
rtr.Namespace = namespace;
ApexStub.RunTestsResult results = ap.runTests(rtr);
For more details on how to:
Making Apex WSDL calls from Apex—Limitations!!
As we are making web service callouts from Apex, all the governor limits imposed on web service callouts are applicable here too. For ex.
The total request time for all callouts (HTTP requests or Web services calls) in a single unit of work is 120 seconds.
The maximum size of a callout request or response (HTTP request or Web services call) is 3 MB.
Out of these two, only the first one limits us most from doing anything super creative from Apex. So you can’t run all/single/bunch tests, if they consume more than 120 seconds to complete. This is pretty easily possible with an org with a decent number of apex classes.
So one can’t go straight forward and do anything with Apex WSDL calls.
Creating a Junit-like Test Suite using Apex WSDL.
JUnit is a unit testing framework in Java. Many bits and pieces of Apex TestCase class are inspired by this framework itself.
A nice feature that’s given to Junit is the ability to create TestSuites. A TestSuite is a Composite of Tests. It runs a collection of test cases. The beauty of test suites is you can create a small suite of tests per feature or module of your org, this suite will have test classes for the feature/module only. So when you change something, it's really not required to run all the tests; you can just run the suite and see if everything for the feature is stable.
Apex Test Suites & ApexClass Sobject!
As of now, Apex doesn’t offer this feature. But we can run tests on a single class as described above, so why don’t we use this feature to create test suites in Apex? But for that, we need access to Apex Classes available in the org.
Good news! Salesforce schema already has a Sobject called “ApexClass”. This SObject keeps information about all the classes in an SFDC org. One can query this Sobject for details like ClassName, Namespace, Body and length, etc.
So, to create Test Suite, a rough idea could be to:
Create a “TestSuite” and “TestSuiteClass” custom Sobject. “TestSuiteClass” will keep the Apex class name and namespace, because we can’t lookup or master details for the ApexClass object. Test Suite Class will master details on "TestSuite”.
Create a Visualforce page/apex controller, that queries ApexClass sobject and presents a list of Apex classes for the end user to select. These selected Apex classes can then be used to create TestSuite/TestSuiteClass records.
Now having this TestSuite and ApexStub class, we can execute it in many ways; a slight challenge will be to hack around the governor limits imposed. For example, we can create another Visualforce/Apex page to run tests available in the TestSuite.
Also, by using TestSuite and the ApexStub class, we can create applications that tell coverage, etc. As RunTestsResult returned after running tests, have fine details about that too.
Test Suite as a Salesforce App – Your views
Let me know how you feel about this TestSuite concept and if it's really worth moving forward like this. If you have some ideas and a good feeling about it, we can join our efforts to create an open-source project for this.
Getting Test Suite natively in Force.com Platform—Idea Exchange!
I'm posting TestSuite as an idea on Salesforce Idea Exchange too, to get this feature natively in the platform itself. Please promote it to make the platform better.