Test SOAP services using JUnit and Mockito
SOAP web-service mocking utility which creates real service endpoints on local ports using webserver instances. These endpoints delegate requests directly to mocks.
Users will benefit from
all with the regular advantages of Mockito.
Bugs, feature suggestions and help requests can be filed with the issue-tracker.
The project is based on Maven and is available form central Maven repository.
Add
<mockito-soap-cxf.version>2.0.0</mockito-soap-cxf.version>
and
<dependency>
<groupId>com.github.skjolber</groupId>
<artifactId>mockito-soap-cxf</artifactId>
<version>${mockito-soap-cxf.version}</version>
</dependency>
or
For
ext {
mockitoSoapCxfVersion = '2.0.0'
}
add
api("com.github.skjolber:mockito-soap-cxf:${mockitoSoapCxfVersion}")
For JDK 9+, add module com.github.skjolber.mockito.soap
.
For JDK 8 or 11 use the 1.2.x
series (override spring and mockito dependency versions).
If you prefer skipping to a full example, see this unit test.
Add a SoapServiceExtension
@ExtendWith(SoapServiceExtension.class)
and mock service endpoints by using
private MyServicePortType serviceMock;
@BeforeEach
public void setup(SoapServiceExtension soap) {
serviceMock = soap.mock(MyServicePortType.class, "http://localhost:12345");
}
or, preferably
private MyServicePortType serviceMock;
@BeforeEach
public void setup(SoapServiceExtension soap) {
soap.mock(MyServicePortType.class, "http://localhost:12345", Arrays.asList("classpath:wsdl/MyService.xsd"));
}
for schema validation. The returned serviceMock
instance is a normal Mockito mock(..) object.
If you prefer skipping to a full example, see this unit test.
Create a SoapServiceRule
@Rule
public SoapServiceRule soap = SoapServiceRule.newInstance();
add a field
private MyServicePortType serviceMock;
and mock service endpoints by using
@Before
public void mockService() {
serviceMock = soap.mock(MyServicePortType.class, "http://localhost:12345");
}
or, preferably
serviceMock = soap.mock(MyServicePortType.class, "http://localhost:12345", Arrays.asList("classpath:wsdl/MyService.xsd"));
for schema validation. The returned serviceMock
instance is a normal Mockito mock(..) object.
Create mock response via code
// init response
GetAccountsResponse mockResponse = new GetAccountsResponse();
List<String> accountList = mockResponse.getAccount();
accountList.add("1234");
accountList.add("5678");
or from XML
GetAccountsResponse response = jaxbUtil.readResource("/my/test/GetAccountsResponse1.xml", GetAccountsResponse.class);
using your favorite JAXB utility. Then mock
when(serviceMock.getAccounts(any(GetAccountsRequest.class))).thenReturn(mockResponse);
and apply standard Mockito test approach. After triggering calls to the mock service, verify number of method calls
ArgumentCaptor<GetAccountsRequest> argument1 = ArgumentCaptor.forClass(GetAccountsRequest.class);
verify(serviceMock, times(1)).getAccounts(argument1.capture());
and request details
GetAccountsRequest request = argument1.getValue();
assertThat(request.getCustomerNumber(), is(customerNumber));
Mock SOAP faults by adding import
import static com.skjolberg.mockito.soap.SoapServiceFault.*;
then mock doing
when(serviceMock.getAccounts(any(GetAccountsRequest.class))).thenThrow(createFault(exception));
or mock directly using an XML string / w3c DOM node.
CXF SOAP clients support MTOM of out the box, enable MTOM in the service mock using
serviceMock = soap.mock(BankCustomerServicePortType.class, bankCustomerServiceAddress, properties("mtom-enabled", Boolean.TRUE));
and add a DataHandler
to the mock response using
byte[] mockData = new byte[] {0x00, 0x01};
DataSource source = new ByteArrayDataSource(mockData, "application/octet-stream");
mockResponse.setCertificate(new DataHandler(source)); // MTOM-enabled base64binary
See MTOM unit test for an example.
For use-cases which require test-cases to run in parallel, it is possible to mock endpoints on random (free) ports. For the SoapEndpointRule
methods
@ClassRule
public static SoapEndpointRule soap = SoapEndpointRule.newInstance("myPort", "yourPort");
or with port range
@ClassRule
public static SoapEndpointRule soap = SoapEndpointRule.newInstance(10000, 30000, "myPort", "yourPort");
there will be reserved two random free ports. Ports numbers can be retrieved using.
int myPort = soap.getPort("myPort");
and
String myPort = System.getProperty("myPort");
In other words, for property resolvers which include system-properties, the reserved ports are readily available. For example the Spring property expression
http://localhost:${myPort}/selfservice/bank
would effectively point to the mocked webservice at myPort
. For a more complete example, see
this spring unit test.
There seems to be an issue with the use of the -exsh
parameter for passing headers into the mock and schema validation. Rather than supplying the wsdl location, supply the XSD locations to work around the problem until a solution can be found.
If you see exception cause by
No binding factory for namespace http://schemas.xmlsoap.org/soap/ registered.
then you're mixing CXF version 2 and 3 - see above about excluding cxf-core
artifact.