Unit Test Automator for Oracle SOA Suite
Testing a Complex Business process could get more complex than the Business Process itself. The Code Quality challenges are depicted below.

While Automated Build Tools like Jenkins and Hudson ensure that the code check-ins are devoid of any compilation issues. We can also ensure that the Check-ins are not breaking the existing functionality, with the approach discussed below.
Oracle SOA Unit Testing ‐ As Is Approach
Oracle SOA Suite provides customized form of JUnit Test Case functionality. The testing Methodology is as below.
- Create Unit Test Case Configuration (XML) in JDeveloper GUI, with Sample Requests and expected Outcome. The Request and Response can be evaluated for Partner Links and Composite IO. The Assertion Validations are provided for either whole request or response fragments, to validate the Test Case Result.
- The Unit Test cases are executed from EM console - Composite Dashboard - Unit Tests. It is nothing but ant script executing the Test Cases based on the Configuration XML created above.
- The Unit Test case results are returned back as XML Output.
Oracle SOA Unit Testing ‐ As automated nightly builds.
The Process of Unit testing can be automated as well, so that everyday morning we know we have a report on Code Quality, just like Build automation. The approach is depicted as below.

- The Test Case Configuration XML file, which gets created from JDeveloper UI, is a collection of Requests and appropriate Response to be validated. This will be based on existing manually tested Reference Composite Instance. We can generate the XML file, by parsing the Audit trail of the Reference Composite Instance.
- The Unit Test Case execution can be done as Ant Build, which could be further scheduled as Nightly Builds to check Code‐Check‐Ins.
- The Test Result Output XML from Ant Build can be leveraged for generating Test Report. Else, we can also adopt a similar approach of scanning the Audit trail of the Resultant Composite Instance and form the report, matching the Results with the Test Configuration Responses.
Henceforth, the above approach, resolves the Code Quality challenges as depicted below.

Let us now focus on implementing the approach.
Execution Step 1 : Generate Unit Test Cases.
The Unit Test Case generation involves the following steps.
- Consume the Composite Instance details, like the Composite ID, Process Name so as to find the Instance programmatically. The Audit Trail details are retrieved as explained in separate Post.
-
The Audit Trail Data pertaining to the Composite Instance, will be parsed for the Request Responses for the Service and Partner Links and other necessary details, required to generate the Test Case. The Code Snippet is provided below.
This is more of an ideation code rather production ready. The Code parses the Audit Trail with String manipulation to keep it simple and swift. The XML DOM can also be leveraged for Audit trail parsing.public class TestCaseHelper { public TestCaseHelper() { super(); } //The Entry method for populating Test Case. public GeneratorResponseBean generateTestCase(GeneratorRequestBean request) throws SQLException, IOException, Exception { //search by instance id for audit trail QueryInstancesBySearchTerms qst = new QueryInstancesBySearchTerms(request); //Composite Instance Metadata populated in searchResult. AuditCheckResponseBean searchResult = qst.getInstancesByCompositeID(); QueryAuditTrail qat = new QueryAuditTrail(request); //GeneratorResponseBean stores the data required for the Test case config generation. GeneratorResponseBean responseBean = new GeneratorResponseBean(); //CompositeHelper gets all the Partner link details from the Composite.xml CompositeHelper compositeHelper = new CompositeHelper(request.getBPELProjectName()); responseBean.setServiceName(compositeHelper.getServiceName()); responseBean.setProjectName(request.getBPELProjectName()); responseBean.setTestCaseName(request.getTestCaseName()); if( compositeHelper.ifServiceHasCallback() ) responseBean.setInvocationType( "1-way" ); else responseBean.setInvocationType( "2-way" ); // The WSDL optionally required if you would like to perform external simulations of systems. responseBean.setWsdlURL("https://" + PropertyHelper.getServerURL() + "/soa-infra/services/default/" + responseBean.getProjectName() + "/" + responseBean.getServiceName() + "?wsdl"); List<InstanceSearchResultBean> instanceSearchResults = searchResult.getSearchResultInstances(); int index = 0; for (InstanceSearchResultBean instanceSearchResult : instanceSearchResults) { // get audit trail for each row String auditXML = qat.getAuditTrail(instanceSearchResult); if (index == 0) { responseBean = populateComponentName(auditXML, responseBean, compositeHelper); responseBean = populateInputRequest(auditXML, responseBean, compositeHelper); responseBean = populateOutputResponse(auditXML, responseBean, compositeHelper); } responseBean = populateMockServices(auditXML, responseBean, compositeHelper); index++; } return responseBean; } public GeneratorResponseBean retrieveTestResults(AuditCheckRequestBean auditRequest) throws SQLException, IOException, Exception { //search for instance id in audit trail QueryInstancesBySearchTerms qst = new QueryInstancesBySearchTerms(auditRequest); AuditCheckResponseBean searchResult = qst.getInstancesBySearchTerm(); QueryAuditTrail qat = new QueryAuditTrail(auditRequest, searchResult); GeneratorResponseBean responseBean = new GeneratorResponseBean(); List<InstanceSearchResultBean> instanceSearchResults = searchResult.getSearchResultInstances(); CompositeHelper compositeHelper = new CompositeHelper(auditRequest.getBPELProcessNameForTestResults()); int index = 0; for (InstanceSearchResultBean instanceSearchResult : instanceSearchResults) { // get audit trail for each row String auditXML = qat.getAuditTrail(instanceSearchResult); System.out.println(auditXML); if (index == 0) { responseBean = populateInputRequest(auditXML, responseBean, compositeHelper); responseBean = populateOutputResponse(auditXML, responseBean, compositeHelper); } responseBean = populateMockServices(auditXML, responseBean, compositeHelper); index++; } return responseBean; } public GeneratorResponseBean populateComponentName(String auditXML, GeneratorResponseBean bean, CompositeHelper compositeHelper) { int partnerOccurence = auditXML.indexOf("initiated "); int firstOccurrence = auditXML.indexOf("\"", partnerOccurence) + 1; int lastOccurrence = auditXML.indexOf("\"", firstOccurrence); bean.setComponentName( auditXML.substring(firstOccurrence, lastOccurrence) ); return bean; } public GeneratorResponseBean populateInputRequest(String auditXML, GeneratorResponseBean bean, CompositeHelper compositeHelper) { int partnerOccurence = auditXML.indexOf("partner", auditXML.indexOf("[Received")); int firstOccurrence = auditXML.indexOf("\"", partnerOccurence) + 1; int lastOccurrence = auditXML.indexOf("\"", firstOccurrence); String client = auditXML.substring(firstOccurrence, lastOccurrence); partnerOccurence = auditXML.indexOf(client, lastOccurrence); int foundOccurrence = auditXML.indexOf("<details>", partnerOccurence); int auditDetailOccurence = auditXML.indexOf("<details id=", partnerOccurence); if ((foundOccurrence > 0 && auditDetailOccurence > 0 && foundOccurrence < auditDetailOccurence) || (foundOccurrence > 0 && auditDetailOccurence < 0)) { firstOccurrence = auditXML.indexOf("<part", foundOccurrence); int partNameOccurence = auditXML.indexOf( "name=", firstOccurrence ) + 6; bean.setInputPartName( auditXML.substring( partNameOccurence, auditXML.indexOf( "\"", partNameOccurence ) ) ); firstOccurrence = auditXML.indexOf(">", firstOccurrence) + 1; lastOccurrence = auditXML.indexOf("</part>", foundOccurrence); String input = auditXML.substring(firstOccurrence, lastOccurrence); bean.setInputRequest(input); System.out.println("Input Request : " + input); System.out.println( "Input Part : " + bean.getInputPartName()); } else { firstOccurrence = auditXML.indexOf("\"", auditDetailOccurence) + 1; lastOccurrence = auditXML.indexOf("\"", firstOccurrence); String auditDetailID = auditXML.substring(firstOccurrence, lastOccurrence); System.out.println("auditDetailID : " + auditDetailID); firstOccurrence = auditXML.indexOf("<AuditDetail_" + auditDetailID + ">"); lastOccurrence = auditXML.indexOf("</AuditDetail_" + auditDetailID + ">"); String payload = auditXML.substring(firstOccurrence, lastOccurrence); firstOccurrence = payload.indexOf("<part"); int partNameOccurence = auditXML.indexOf( "name=", firstOccurrence ) + 6; firstOccurrence = payload.indexOf(">", firstOccurrence) + 1; lastOccurrence = payload.indexOf("</part>", firstOccurrence); bean.setInputPartName( auditXML.substring( partNameOccurence, auditXML.indexOf( "\"", partNameOccurence ) ) ); System.out.println( "Input Part : " + bean.getInputPartName()); String input = payload.substring(firstOccurrence, lastOccurrence); bean.setInputRequest(input); System.out.println("Request : " + input); } firstOccurrence = auditXML.indexOf("\"", auditXML.indexOf("[Received")) + 1; lastOccurrence = auditXML.indexOf("\"", firstOccurrence); String operation = auditXML.substring(firstOccurrence, lastOccurrence); System.out.println(operation); bean.setInputOperation(operation); return bean; } public GeneratorResponseBean populateOutputResponse(String auditXML, GeneratorResponseBean bean, CompositeHelper compositeHelper) { int partnerOccurence; int firstOccurrence; int lastOccurrence; if(compositeHelper.ifServiceHasCallback()) { partnerOccurence = auditXML.lastIndexOf("Invoked 1-way"); firstOccurrence = auditXML.indexOf("\"", partnerOccurence) + 1; lastOccurrence = auditXML.indexOf("\"", firstOccurrence); bean.setCallBackOperation( auditXML.substring(firstOccurrence, lastOccurrence) ); partnerOccurence = auditXML.indexOf("partner", partnerOccurence); } else { partnerOccurence = auditXML.indexOf("partner", auditXML.lastIndexOf("Reply to")); } int foundOccurrence = auditXML.indexOf("<details>", partnerOccurence); int auditDetailOccurence = auditXML.indexOf("<details id=", partnerOccurence); if ((foundOccurrence > 0 && auditDetailOccurence > 0 && foundOccurrence < auditDetailOccurence) || (foundOccurrence > 0 && auditDetailOccurence < 0)) { firstOccurrence = auditXML.indexOf("<part", foundOccurrence); int partNameOccurence = auditXML.indexOf( "name=", firstOccurrence ) + 6; bean.setOutputPartName( auditXML.substring( partNameOccurence, auditXML.indexOf( "\"", partNameOccurence ) ) ); firstOccurrence = auditXML.indexOf(">", firstOccurrence) + 1; lastOccurrence = auditXML.indexOf("</part>", foundOccurrence); String output = auditXML.substring(firstOccurrence, lastOccurrence); bean.setOutputResponse(output); System.out.println("Output Response : " + output); } else { firstOccurrence = auditXML.indexOf("\"", auditDetailOccurence) + 1; lastOccurrence = auditXML.indexOf("\"", firstOccurrence); String auditDetailID = auditXML.substring(firstOccurrence, lastOccurrence); System.out.println("auditDetailID : " + auditDetailID); firstOccurrence = auditXML.indexOf("<AuditDetail_" + auditDetailID + ">"); lastOccurrence = auditXML.indexOf("</AuditDetail_" + auditDetailID + ">"); String payload = auditXML.substring(firstOccurrence, lastOccurrence); firstOccurrence = payload.indexOf("<part"); int partNameOccurence = auditXML.indexOf( "name=", firstOccurrence ) + 6; bean.setOutputPartName( auditXML.substring( partNameOccurence, auditXML.indexOf( "\"", partNameOccurence ) ) ); firstOccurrence = payload.indexOf(">", firstOccurrence) + 1; lastOccurrence = payload.indexOf("</part>", firstOccurrence); String output = payload.substring(firstOccurrence, lastOccurrence); bean.setOutputResponse(output); System.out.println("Response : " + output); } return bean; } public GeneratorResponseBean getReceivedResponse(String auditXML, int startOccurrence, GeneratorResponseBean bean) { System.out.println("startOccurrence : " + startOccurrence); String output = ""; int firstOccurrence; int lastOccurrence; int partnerOccurence = auditXML.indexOf("[Received", startOccurrence); int foundOccurrence = auditXML.indexOf("<details>", partnerOccurence); int auditDetailOccurence = auditXML.indexOf("<details id=", partnerOccurence); if ((foundOccurrence > 0 && auditDetailOccurence > 0 && foundOccurrence < auditDetailOccurence) || (foundOccurrence > 0 && auditDetailOccurence < 0)) { firstOccurrence = auditXML.indexOf("<part", foundOccurrence); int partNameOccurence = auditXML.indexOf( "name=", firstOccurrence ) + 6; bean.setOutputPartName( auditXML.substring( partNameOccurence, auditXML.indexOf( "\"", partNameOccurence ) ) ); firstOccurrence = auditXML.indexOf(">", firstOccurrence) + 1; lastOccurrence = auditXML.indexOf("</part>", firstOccurrence); output = auditXML.substring(firstOccurrence, lastOccurrence); bean.setOutputResponse(output); System.out.println("Output Response : " + output); } else { firstOccurrence = auditXML.indexOf("\"", auditDetailOccurence) + 1; lastOccurrence = auditXML.indexOf("\"", firstOccurrence); String auditDetailID = auditXML.substring(firstOccurrence, lastOccurrence); System.out.println("auditDetailID : " + auditDetailID); firstOccurrence = auditXML.indexOf("<AuditDetail_" + auditDetailID + ">"); lastOccurrence = auditXML.indexOf("</AuditDetail_" + auditDetailID + ">"); String payload = auditXML.substring(firstOccurrence, lastOccurrence); firstOccurrence = payload.indexOf("<part"); int partNameOccurence = auditXML.indexOf( "name=", firstOccurrence ) + 6; bean.setOutputPartName( auditXML.substring( partNameOccurence, auditXML.indexOf( "\"", partNameOccurence ) ) ); firstOccurrence = payload.indexOf(">", firstOccurrence) + 1; lastOccurrence = payload.indexOf("</part>", firstOccurrence); output = payload.substring(firstOccurrence, lastOccurrence); bean.setOutputResponse(output); System.out.println("Response : " + output); } return bean; } public GeneratorResponseBean populateMockServices(String auditXML, GeneratorResponseBean responseBean, CompositeHelper compositeHelper) { int firstOccurrence; int lastOccurrence; int auditDetailOccurence; int foundOccurrence = 0; String invocationType = null; String invocationResult = null; foundOccurrence = getNextInvocationOccurence(auditXML, foundOccurrence ); while (foundOccurrence > 0) { ServiceBean bean = new ServiceBean(); firstOccurrence = auditXML.indexOf("initiated "); firstOccurrence = auditXML.indexOf("\"", firstOccurrence) + 1; lastOccurrence = auditXML.indexOf("\"", firstOccurrence); bean.setComponentName( auditXML.substring(firstOccurrence, lastOccurrence) ); invocationResult = getNextInvocationResult(auditXML, foundOccurrence ); System.out.println("invocation result : " + invocationResult); if( null != invocationResult && invocationResult.equals("SUCCESS") ) { invocationType = auditXML.substring(foundOccurrence + "Invoked ".length(), foundOccurrence + "Invoked ".length() + 5); System.out.println("invocationType : " + invocationType); bean.setInvocationType(invocationType); } firstOccurrence = auditXML.indexOf("\"", foundOccurrence) + 1; lastOccurrence = auditXML.indexOf("\"", firstOccurrence); String operation = auditXML.substring(firstOccurrence, lastOccurrence); System.out.println("operation : " + operation); bean.setOperation(operation); firstOccurrence = auditXML.indexOf( "\"", lastOccurrence + 1) + 1; int partnerOccurence = auditXML.indexOf( "\"", firstOccurrence); String partner = auditXML.substring(firstOccurrence, partnerOccurence); System.out.println("partner : " + partner); if (null != compositeHelper && doesPartnerExistsAsComponent(partner, compositeHelper)) { foundOccurrence = auditXML.indexOf("Invoked ", foundOccurrence + 1); System.out.println("Skipping partner : " + partner); continue; } bean.setPartner(partner); if (operation.startsWith("DB")) bean.setServiceType("DB"); else bean.setServiceType("HTTP"); firstOccurrence = auditXML.indexOf("<details>", partnerOccurence); auditDetailOccurence = auditXML.indexOf("<details id=", partnerOccurence); if ((firstOccurrence > 0 && auditDetailOccurence > 0 && firstOccurrence < auditDetailOccurence) || (firstOccurrence > 0 && auditDetailOccurence < 0)) { firstOccurrence = auditXML.indexOf("<part", foundOccurrence); int partNameOccurence = auditXML.indexOf( "name=", firstOccurrence ) + 6; bean.setInputPartName( auditXML.substring( partNameOccurence, auditXML.indexOf( "\"", partNameOccurence ) ) ); firstOccurrence = auditXML.indexOf(">", firstOccurrence) + 1; lastOccurrence = auditXML.indexOf("</part>", firstOccurrence); String input = auditXML.substring(firstOccurrence, lastOccurrence); bean.setRequest(input); System.out.println("Request : " + input); System.out.println("invocationType : " + invocationType); if ( null == invocationType || invocationType.startsWith("2") ) { if( invocationResult.equals("FAULT")) { firstOccurrence = auditXML.indexOf("<fault", lastOccurrence + 1); firstOccurrence = auditXML.indexOf(">", firstOccurrence) + 1; lastOccurrence = auditXML.indexOf("</fault>", firstOccurrence ); input = auditXML.substring(firstOccurrence, lastOccurrence); } else { System.out.println("In else loop"); firstOccurrence = auditXML.indexOf("<part", lastOccurrence + 1); partNameOccurence = auditXML.indexOf( "name=", firstOccurrence ) + 6; bean.setOutputPartName( auditXML.substring( partNameOccurence, auditXML.indexOf( "\"", partNameOccurence ) ) ); firstOccurrence = auditXML.indexOf(">", firstOccurrence) + 1; lastOccurrence = auditXML.indexOf("</part>", firstOccurrence); input = auditXML.substring(firstOccurrence, lastOccurrence); } } else { int operationOccurence = auditXML.indexOf("[Received", lastOccurrence); if (operationOccurence > 0) { firstOccurrence = auditXML.indexOf("\"", operationOccurence) + 1; bean.setCallbackOperation(auditXML.substring(firstOccurrence, auditXML.indexOf("\"", firstOccurrence))); responseBean = getReceivedResponse(auditXML, lastOccurrence, responseBean ); } else { operationOccurence = auditXML.indexOf("Waiting", lastOccurrence); if (operationOccurence > 0) { firstOccurrence = auditXML.indexOf("\"", operationOccurence) + 1; lastOccurrence = auditXML.indexOf("\"", firstOccurrence); bean.setCallbackOperation(auditXML.substring(firstOccurrence, lastOccurrence)); } } } System.out.println("Response : " + input); bean.setResponse(input); } else { firstOccurrence = auditXML.indexOf("\"", auditDetailOccurence) + 1; lastOccurrence = auditXML.indexOf("\"", firstOccurrence); String auditDetailID = auditXML.substring(firstOccurrence, lastOccurrence); System.out.println("auditDetailID : " + auditDetailID); firstOccurrence = auditXML.indexOf("<AuditDetail_" + auditDetailID + ">"); lastOccurrence = auditXML.indexOf("</AuditDetail_" + auditDetailID + ">"); String payload = auditXML.substring(firstOccurrence, lastOccurrence); firstOccurrence = payload.indexOf("<part"); int partNameOccurence = auditXML.indexOf( "name=", firstOccurrence ) + 6; bean.setInputPartName( auditXML.substring( partNameOccurence, auditXML.indexOf( "\"", partNameOccurence ) ) ); firstOccurrence = payload.indexOf(">", firstOccurrence) + 1; lastOccurrence = payload.indexOf("</part>", firstOccurrence); String input = payload.substring(firstOccurrence, lastOccurrence); bean.setRequest(input); if ( null == invocationType || invocationType.startsWith("2") ) { firstOccurrence = payload.indexOf("<part", lastOccurrence); partNameOccurence = auditXML.indexOf( "name=", firstOccurrence ) + 6; bean.setOutputPartName( auditXML.substring( partNameOccurence, auditXML.indexOf( "\"", partNameOccurence ) ) ); firstOccurrence = payload.indexOf(">", firstOccurrence) + 1; lastOccurrence = payload.indexOf("</part>", firstOccurrence); input = payload.substring(firstOccurrence, lastOccurrence); } else { int operationOccurence = auditXML.indexOf("[Received", lastOccurrence); bean.setCallbackOperation(auditXML.substring(auditXML.indexOf("\"", operationOccurence) + 1, auditXML.indexOf("\"", operationOccurence))); System.out.println("Callback " + bean.getCallbackOperation()); responseBean = getReceivedResponse(auditXML, lastOccurrence, responseBean); } //System.out.println("Response : " + input); bean.setResponse(input); } String requestPattern = bean.getRequest().substring( 0, bean.getRequest().indexOf(' ')).replace("<", ""); if( null != requestPattern && requestPattern.contains( ":" )) { requestPattern = requestPattern.substring( bean.getRequest().indexOf(':') ); } bean.setRequestPattern( requestPattern ); responseBean.addMockService(bean); foundOccurrence = getNextInvocationOccurence( auditXML, foundOccurrence+1); System.out.println( "getNextInvocationOccurence" + foundOccurrence ); } return responseBean; } private boolean doesPartnerExistsAsComponent(String partner, CompositeHelper compositeHelper) { for (String component : compositeHelper.getComponentList()) { partner = partner.toLowerCase(); component = component.toLowerCase(); if (partner.contains(component)) return true; } return false; } private int getNextInvocationOccurence(String auditXML, int cursor) { int foundInvokedOccurrence = auditXML.indexOf("Invoked ", cursor ); int foundFaultedOccurence = auditXML.indexOf("Faulted ", cursor ); if( foundInvokedOccurrence < 0 && foundFaultedOccurence < 0) return -1; if( foundInvokedOccurrence < 0 && foundFaultedOccurence > 0) return foundFaultedOccurence; if( foundInvokedOccurrence > 0 && foundFaultedOccurence < 0) return foundInvokedOccurrence; if(foundInvokedOccurrence > 0 && foundFaultedOccurence > 0 ) { if( foundInvokedOccurrence < foundFaultedOccurence ) return foundInvokedOccurrence; else return foundFaultedOccurence; } return -1; } private String getNextInvocationResult(String auditXML, int cursor) { int foundInvokedOccurrence = auditXML.indexOf("Invoked ", cursor ); int foundFaultedOccurence = auditXML.indexOf("Faulted ", cursor ); if( foundInvokedOccurrence < 0 && foundFaultedOccurence < 0) return null; if( foundInvokedOccurrence < 0 && foundFaultedOccurence > 0) return "FAULT"; if( foundInvokedOccurrence > 0 && foundFaultedOccurence < 0) return "SUCCESS"; if(foundInvokedOccurrence > 0 && foundFaultedOccurence > 0 ) { if( foundInvokedOccurrence < foundFaultedOccurence ) return "SUCCESS"; else return "FAULT"; } return null; } }
-
The Test Case Source data will be converted to Unit Test case configuration and gets added to the SOA Application Source. The Code Snippet is provided below.
public class SOASuiteTestCaseGenerator { public SOASuiteTestCaseGenerator() { super(); } //Entry method for Test Case Files Generation. public void genenrateOracleSOATestSuiteFile(GeneratorResponseBean responseBean) throws FileNotFoundException, XPathExpressionException, ParserConfigurationException, SAXException, IOException, TransformerConfigurationException, TransformerException { String outputFolder = PropertyHelper.getOutputDirectory(); responseBean.setOutputDirectory(outputFolder); PrintStream out = new PrintStream(new FileOutputStream(responseBean.getOutputDirectory() + responseBean.getTestCaseName() + ".xml")); out.print(XMLFormatter.format(genenrateOracleSOATestSuiteContent(responseBean))); out.close(); } public void includeOracleSOATestSuiteFile(GeneratorResponseBean responseBean) throws FileNotFoundException, IOException, XPathExpressionException, ParserConfigurationException, SAXException, TransformerConfigurationException, TransformerException { String outputFolder = PropertyHelper.getSourceCodeDirectory() + "/" + responseBean.getProjectName() + "/testsuites/UnitTest/tests/"; responseBean.setOutputDirectory(outputFolder); createTestCaseFileSetup(responseBean); PrintStream out = new PrintStream(new FileOutputStream(responseBean.getOutputDirectory() + "/" + responseBean.getTestCaseName() + ".xml")); out.print(XMLFormatter.format(genenrateOracleSOATestSuiteContent(responseBean))); out.close(); out = new PrintStream(new FileOutputStream(responseBean.getOutputDirectory() + "/" + responseBean.getTestCaseName() + "_config.xml")); out.print(XMLFormatter.format(responseBean.getXMLOutput())); out.close(); } private void createTestCaseFileSetup(GeneratorResponseBean responseBean) throws IOException { File testSuitesFolder = new File(PropertyHelper.getSourceCodeDirectory() + "/" + responseBean.getProjectName() + "/testsuites"); if (!testSuitesFolder.exists()) { testSuitesFolder.mkdir(); } appendTestSuiteFileEntry(PropertyHelper.getSourceCodeDirectory() + "/" + responseBean.getProjectName() + "/testsuites", "UnitTest"); File testCaseFolder = new File(PropertyHelper.getSourceCodeDirectory() + "/" + responseBean.getProjectName() + "/testsuites/UnitTest"); if (!testCaseFolder.exists()) { testCaseFolder.mkdir(); } testCaseFolder = new File(PropertyHelper.getSourceCodeDirectory() + "/" + responseBean.getProjectName() + "/testsuites/UnitTest/tests"); if (!testCaseFolder.exists()) { testCaseFolder.mkdir(); } appendTestCaseFileEntry(PropertyHelper.getSourceCodeDirectory() + "/" + responseBean.getProjectName() + "/testsuites/UnitTest/tests", responseBean.getTestCaseName() + ".xml"); } private void appendTestSuiteFileEntry(String fileName, String entryName) throws IOException { File entryFile = new File(fileName + "/fileList.xml"); if (!entryFile.exists()) { PrintStream out = new PrintStream(new FileOutputStream(fileName + "/fileList.xml", false)); out.print("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<properties>\n" + " <entry key=\"UnitTest\"/>\n" + "</properties>"); out.close(); } else { String fileContent = PropertyHelper.readFileasString(fileName + "/fileList.xml"); if (!fileContent.contains("UnitTest")) { PrintStream out = new PrintStream(new FileOutputStream(fileName + "/fileList.xml", false)); out.print("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<properties>\n" + " <entry key=\"UnitTest\"/>\n" + "</properties>"); out.close(); } } } private void appendTestCaseFileEntry(String fileName, String entryName) throws FileNotFoundException, IOException { File entryFile = new File(fileName + "/fileList.xml"); if (!entryFile.exists()) { entryFile.createNewFile(); PrintStream out = new PrintStream(entryFile); out.print("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<properties>\n" + " <entry key=\"" + entryName + "\"/>\n" + "</properties>"); out.close(); } else { //read file content StringBuffer fileContent = new StringBuffer(); //add entry fileContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<properties>"); String[] files = new File(fileName).list(); for (String filename : files) { if( ! filename.equals( "fileList.xml" ) ) fileContent.append("<entry key=\"" + filename + "\"/>"); } fileContent.append("<entry key=\"" + entryName + "\"/>" ); fileContent.append("</properties>"); System.out.println("test Suite File Content" + fileContent); //write the file PrintStream out = new PrintStream(new FileOutputStream(fileName + "/fileList.xml", false)); out.print(XMLFormatter.format(fileContent.toString())); out.close(); } } private Document convertStringToDocument(String content) throws ParserConfigurationException, SAXException, IOException { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); InputSource is = new InputSource(new StringReader(content)); return builder.parse(is); } private String applyExclusionFilter( String content ) throws XPathExpressionException, ParserConfigurationException, SAXException, IOException, TransformerConfigurationException, TransformerException { Document doc = convertStringToDocument(content); XPathFactory xPathfactory = XPathFactory.newInstance(); XPath xpath = xPathfactory.newXPath(); String exclusions = PropertyHelper.getXMLCompareExclusionList(); List<String> items = Arrays.asList(exclusions.split("\\s*,\\s*")); for (String exclusionTag : items) { XPathExpression expr = xpath.compile("//*[local-name()='" + exclusionTag + "']"); NodeList nodeList = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); for(int nodeIndex=0; nodeIndex<nodeList.getLength() ; nodeIndex++) { Node node = nodeList.item(nodeIndex); node.setTextContent("*"); } } return getNodeAsString(doc.getDocumentElement()); } private String formatNamespaces(String content ) { int index = content.indexOf( "<"); while( index >= 0 ) { int endIndex = content.indexOf( ">", index ); String tag = content.substring(index, endIndex); if( ! tag.contains("/") && ! tag.contains(":") && ! tag.contains( "xmlns=") && tag.contains( "return") ) { System.out.println( "Found empty Namespace " + tag ); content = content.substring( 0, endIndex ) + " xmlns=\"\"" + content.substring(endIndex, content.length()); } index = content.indexOf( "<", endIndex + 1); } return content; } private String genenrateOracleSOATestSuiteContent(GeneratorResponseBean responseBean) throws XPathExpressionException, ParserConfigurationException, SAXException, IOException, TransformerConfigurationException, TransformerException { StringBuffer XMLOutput = new StringBuffer(); boolean isAsynchrnousCallBackOperation = false; if (null != responseBean.getCallBackOperation() && !responseBean.getCallBackOperation().equals("null")) { isAsynchrnousCallBackOperation = true; } XMLOutput.append("<compositeTest compositeDN=\"" + responseBean.getProjectName() + "\" xmlns=\"http://xmlns.oracle.com/sca/2006/test\">"); XMLOutput.append("<initiate serviceName=\"" + responseBean.getServiceName() + "\" operation=\"" + responseBean.getInputOperation() + "\" isAsync=\""); if (isAsynchrnousCallBackOperation) { XMLOutput.append("true"); } else { XMLOutput.append("false"); } XMLOutput.append("\" delay=\"PT0S\">"); XMLOutput.append("<message><part partName=\"" + responseBean.getInputPartName() + "\"><content>"); XMLOutput.append(responseBean.getInputRequest()); XMLOutput.append("</content></part></message></initiate>"); XMLOutput.append("<wireActions wireSource=\"" + responseBean.getServiceName() + "\" operation=\"" + responseBean.getInputOperation() + "\">"); XMLOutput.append("<assert comparisonMethod=\"xml-similar\"><expected><location key=\"output\" "); if (isAsynchrnousCallBackOperation) { XMLOutput.append("callbackOperation=\"" + responseBean.getCallBackOperation() + "\""); } XMLOutput.append("/>"); XMLOutput.append("<message><part partName=\"" + responseBean.getOutputPartName() + "\"><content>"); XMLOutput.append(formatNamespaces(responseBean.getOutputResponse())); XMLOutput.append("</content></part></message></expected></assert></wireActions>"); for (ServiceBean bean : responseBean.getMockServices()) { XMLOutput.append("<wireActions wireSource=\"" + bean.getComponentName() + "/" + bean.getPartner() + "\" operation=\"" + bean.getOperation() + "\">"); XMLOutput.append("<assert comparisonMethod=\"xml-similar\"><expected><location key=\"input\" "); if (null != bean.getCallbackOperation() && !bean.getCallbackOperation().equals("null")) { XMLOutput.append("callbackOperation=\"" + responseBean.getCallBackOperation() + "\""); } XMLOutput.append("/><message><part partName=\"" + bean.getInputPartName() + "\"><content>"); XMLOutput.append(formatNamespaces(bean.getRequest())); XMLOutput.append("</content></part></message></expected></assert>"); XMLOutput.append("<emulate duration=\"PT0S\"><message><part partName=\"" + bean.getOutputPartName() + "\"><content>"); XMLOutput.append(formatNamespaces(bean.getResponse())); XMLOutput.append("</content></part></message></emulate></wireActions>"); } XMLOutput.append("</compositeTest>"); return applyExclusionFilter(XMLOutput.toString()); } private String getNodeAsString(Node node) throws TransformerConfigurationException, TransformerException { StringWriter writer = new StringWriter(); Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.transform(new DOMSource(node), new StreamResult(writer)); String output = writer.toString(); return output.substring(output.indexOf("?>") + 2); } }
Execution Step 2: Execute the Test Case as Automated Build.
The Ant Task executes the Test Case for the BPEL Project passed.
<!-- test with composite tests -->
<target name="unit-test-composite"
description="test the composite, via sca-test">
<taskdef resource="net/sf/antcontrib/antlib.xml" />
<echo>Running sca-testing for ${composite.name} against ${wls.mgd.server.url}</echo>
<!-- api expects a file - so we create one on demand -->
<property name="tmp.jndi.properties" value="${basedir}/tmp-jndi.properties"/>
<echo file="${tmp.jndi.properties}">
java.naming.factory.initial=${java.naming.factory.initial}
java.naming.provider.url=${java.naming.provider.url}
java.naming.security.principal=${server.user}
java.naming.security.credentials=${server.password}
dedicated.connection=true
dedicated.rmicontext=true
</echo>
<delete file="${test.results.dir}/${testsuite.name}/antRun-TestFwk"/>
<trycatch>
<try>
<ant antfile="${oracle.home}/bin/ant-sca-test.xml"
target="test" inheritall="false">
<!-- connection information -->
<property name="oracle.home" value="${oracle.home}"/>
<property name="java.passed.home" value="${java.home}"/>
<!-- name of the composite -->
<property name="scatest.input" value="${composite.name}"/>
<!-- name of the testsuite -->
<property name="scatestsuite.input" value="${testsuite.name}"/>
<!-- api is bad, hence default ones -->
<property name="jndi.properties.input" value="${tmp.jndi.properties}"/>
<!-- result dir -->
<property name="scatest.result" value="${test.results.dir}"/>
<!-- partition name -->
<property name="scatest.partition" value="${soa.partition.name}"/>
</ant>
<echo>Results written to: ${test.results.dir}</echo>
<!-- clean up beforehand -->
<delete file="${tmp.jndi.properties}" />
</try>
<finally>
<echo>In <finally>.</echo>
<path id="class.path">
<fileset dir="${jars.directory}">
<include name="**/*.jar" />
</fileset>
</path>
<java classname="com.tester.UnitTestCaseReporter">
<sysproperty key="TesterPropertiesFile" value="${tester.properties.location}"/>
<arg line="-e ${unit.testenv} -p ${composite.name} -f ${test.results.dir}/antRun-TestFwk.xml -o ${test.results.dir}"/>
<classpath refid="class.path" />
</java>
</finally>
</trycatch>
</target>
The java.naming.provider.url will be of the format t3://host:port/soa-infra and java.naming.factory.initial=weblogic.jndi.WLInitialContextFactory The Ant Task can be configured to execute from Jenkins, scheduled as per needs.
Execution Step 3 : Generate Test Report.
The Ant Task generates the report. It has few limitaitons, like the Assert Comparison feature from the product has limited options. In case we want to add custom Assertions and override the standard comparison, we can generate a similar report by accessing the Audit trail of the Composite. We can also add more feaures like direct access link to the Composite Instance Flow and Flow trace for easy analysis and debugging.
The XSL file transforms the above report to HTML with simple XSL. The HTML file can be linked to the Jenkins Build Result.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:test="http://xmlns.oracle.com/sca/2006/test">
<xsl:template match="/">
<html>
<head>
<link rel="stylesheet" type="text/css" href="./css/tablestyle.css" />
</head>
<body>
<h1> Test Report</h1>
<br/>
<xsl:for-each select="/test:testRunResults/test:testSuite/test:testResult">
<h2><span><xsl:value-of select="concat( ./@suiteName , ' Scenario : ' , ./@testName )"/></span></h2>
<br/>
<h6 id="HEADLINEOUTCOME"> Test Outcome :
<a target="_blank" href="{./test:compositeLink[1]}">
<xsl:value-of select="./@outcome"/>
</a>
</h6>
<table id="hor-zebra">
<tr>
<th scope="col">Component Wire</th>
<th scope="col">Assertion Outcome</th>
</tr>
<!--For Value-->
<xsl:for-each select="./test:wireActionResults">
<xsl:variable name="css-class">
<xsl:choose>
<xsl:when test="position() mod 2 = 0">even</xsl:when>
<xsl:otherwise>odd</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<tr align="Justify" class="{$css-class}">
<td>
<xsl:value-of select="./@wireSource"/>
</td>
<td>
<xsl:text disable-output-escaping="yes"><![CDATA[<button onclick="window.open('./mergely/compare/compare.html?actual=../../]]></xsl:text>
<xsl:value-of select="./@actualFile"/>
<xsl:text disable-output-escaping="yes"><![CDATA[&expected=../../]]></xsl:text>
<xsl:value-of select="./@expectedFile"/>
<xsl:text disable-output-escaping="yes">');"><![CDATA[<code>]]></xsl:text>
<xsl:value-of select="./test:assertionOutcome/@outcome"/>
<xsl:text disable-output-escaping="yes"><![CDATA[</code></button>]]></xsl:text>
</td>
</tr>
</xsl:for-each>
</table>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>