Click here to Skip to main content
13,701,886 members
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

3.9K views
2 bookmarked
Posted 8 May 2018
Licenced CPOL

CodeSV by CA Technologies

, 8 May 2018
Rate this:
Please Sign up or sign in to vote.
In this article, I walk through using CodeSV and provide an overview of how it works and what makes it useful.

Editorial Note

This Review is from our sponsors at CodeProject. These reviews are intended to provide you with information on products and services that we consider useful and of value to developers.

CodeSV is a handy tool from CA Technologies that provides a Java library for setting up virtual services in unit testing code. In this article, I walk through using CodeSV and provide an overview of how it works and what makes it useful. Specifically, I’ll describe my experiences downloading and installing the library, integrating it with an existing project and using it to set up virtual services within unit tests.

The version of the library I used was initially downloaded on March 3rd, 2018, and was the 1.2.1-signed version. Before writing this review, I was not aware of this library and have not used an in-code virtual service provider for unit testing.

Environment and Setup

The dev environment I tested this on was a Windows 10 machine, with Oracle Java 1.8.0_161. The project used to test this was a Spring Boot project, built with Gradle. My IDE was IntelliJ IDEA 2017.3 (Community Edition).

The base project that I used for my review is available in a personal GitHub repo here. You can find the result of my experiments here.

Getting Started

Initially, I downloaded the library from the home page on the site. I used the link for Getting Started to walk through setting it up in my environment. I liked how the process covered Maven, Gradle and Manual installation. I realized at this point that I could just include the library in my Gradle configuration, and abandoned the download.

The download link on the home page was very prominent. I think developers would appreciate having additional buttons with captions similar to "Add to your Gradle Project," and "Add to your Maven Project."

A few areas which I think could be improved:

  • The download version was newer than the version included in the Gradle and Maven instructions. (1.2.1 vs 1.2.0.)
  • The Gradle instructions used the compile function in the dependencies, where testCompile may be more appropriate, and leaving the library out of production code.

I think a table of contents or perhaps converting the Quick Start Guide into a series of pages would make it easier for engineers to navigate to the section relative to their needs. Personally, I skipped the Maven section, implemented the suggestions in the Gradle section, and assumed the Manual Installation section was the end of the page.

I was able to get a test up and running in about 30 minutes. The most significant problem that I encountered—aside from silly mistakes—was that my code was expecting a JSON-formatted response and I didn’t initially realize that I could set the ContentType on the response from the virtual service.

I actually found two similar solutions:

forGet("http://www.zipcodeapi.com/rest/" + apiKey + "/distance.json/97229/84015/mile")
       .doReturn(
               okMessage()
                       .withStringBody("{\"distance\":620.755}")
                       .withContentType(MediaType.APPLICATION_JSON.toString())
       );
forGet("http://www.zipcodeapi.com/rest/" + apiKey + "/distance.json/97229/84015/mile")
       .doReturn(
               okMessage()
                       .withJsonBody("{\"distance\":620.755}")
       );

I found 12 ‘with’ methods for the ‘forGet’ command if I combined overloaded functions. I think it would be handy to include brief descriptions of each of these on the Getting Started page. Adding these would expose engineers who are new to the library to more potential options and accelerate the learning curve.

My initial impressions of the product at this point were that it was much easier than my typical approach to testing dependent services. Getting a functional and useful test up and running was straightforward. I’m looking forward to delving more into the product and its capabilities.

Implementing Additional Functionality

Following the success of the initial integration of the product into my application, I decided on a couple of challenges to explore additional functionality within the library. I have documented the results of those challenges below.

Challenge 1: SSL Integration

The web service called from my microservice was secured with SSL. For the initial test of the library, I changed the protocol of the target service to standard HTTP. This approach worked well for the test but prevented me from connecting to the real service when the application was deployed. I spent a few hours working through this, but was only able to realize success by changing the way the original code was connecting to the service.

The documentation provides an example which executes a request to localhost using an HttpsURLConnection object. In my application, the request was performed using the RestTemplate object provided as part of the Spring Boot framework.

Working with the example code, I generated a new local keystore for the target site. Following an initially unsuccessful attempt to execute the test using this keystore, I started using the example and iterated through steps to attempt to find the problem.

  • I was able to successfully return a value using the library when connecting to localhost with the example code provided.
  • I was able to successfully return a value using the library when connecting to the remote service with HttpsURLConnection using the new keystore file.
  • I was unable to return a value using the library when connecting to the remote service with the RestTemplate object using the new keystore file.
  • I was unable to return a value using the library when connecting to localhost with the RestTemplate object and the example keystore.

It appears that while RestTemplate uses HttpsURLConnection within its implementation to connect to a web service, something about the way it is implemented differs from calling HttpsURLConnection directly in the code. The former was not fully able to interact with how the library provides the secured virtual service. The exception thrown is shown below.

org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://localhost/rest/JZyKNKvzMavcTNzziyPB2tm9upKOTccHLCumREmXz14CfwCgjcwy4RO0wH3rmeV5/distance.json/97229/84015/mile": sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target; nested exception is javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:666)
	at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613)
	at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:287)
	at com.echovue.service.ZipcodeDistanceServiceTest.testGetDistance(ZipcodeDistanceServiceTest.java:114)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:298)
	at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:292)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.lang.Thread.run(Thread.java:748)
Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
	at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1959)
	at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:328)
	at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:322)
	at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1614)
	at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216)
	at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1052)
	at sun.security.ssl.Handshaker.process_record(Handshaker.java:987)
	at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1072)
	at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1385)
	at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1413)
	at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1397)
	at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)
	at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
	at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:162)
	at org.springframework.http.client.SimpleBufferingClientHttpRequest.executeInternal(SimpleBufferingClientHttpRequest.java:78)
	at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
	at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:652)
	... 15 more
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:397)
	at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:302)
	at sun.security.validator.Validator.validate(Validator.java:260)
	at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
	at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229)
	at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124)
	at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1596)
	... 29 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141)
	at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
	at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
	at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:392)
	... 35 more

Challenge 2: Exporting Virtual Services

Exporting the request and response pairs from the test was far simpler than I would have expected. I set the property in the "Before" function, unset it in the "After" function, and with a single run of the test, I had both the request and the response available to share. I’m excited to try this out with CA Service Virtualization Community Edition.

Challenge 3: Supporting Failure Conditions

In the dependent world of microservices and web services, failure within one service can cause a ripple effect of failures through an environment. The library makes it easy to test the handling of successful calls to a service, but I wanted to see if it would allow failure responses so that I could test whether my code had been designed to react defensively as well.

The first problem I noticed was that when I added the failure test to the same test class as the successful test, my code was not responding in the way I assumed it would. I was able to update my code and the test to better handle the case of an unauthorized connect. However, the process led me to discover an additional problem.

While my failure test failed, the successful test still passed, as long as both tests were executed independently. When I executed them together, they would both fail, leading me to believe that the library might not be thread-safe, or that it might be maintaining state between test calls. This persisted even when I provided a unique URL for each test.

This problem occurred both when the tests were executed within my IDE (IntelliJ Community Edition 2017.3) and when I ran the tests from the command line.

Challenge 4: POST Call Verification

For what was initially going to be my final challenge, I added verify checks to the end of my unit test. The checks worked as expected and were easy to implement. As I was reading through the example of how to implement checks, I noticed that it was possible to pass parameters into the calls which establish the responses from the virtual service.

It wasn’t immediately clear from the documentation, but it became apparent that the forGet function doesn’t necessarily filter on URL parameters.

Bonus Challenge 5: Add Additional Matching Criteria to Service Calls

Finally, I decided to go back and attempt to resolve the problems I’d faced in the third challenge concerning multiple tests using different path parameters and receiving conflicting results.

I moved the URL out of both functions and parameterized two of the path parameters to {zipcode1} and {zipcode2}, instead of the hard-coded values. I then added .matchesPathParameter method calls to both of the tests when I was initializing the virtual service.

Individually, both tests passed as expected; however, when executed as part of the entire class of tests, one of the tests continued to fail. I don’t believe that the framework I’d used to make my calls was utilizing any form of caching, but this is something I would expect to see if it was.

Conclusions

The ease with which I was able to include the CodeSV library in my project and was able to create a virtual service within my test and test it out was very surprising—so much so that I immediately Slacked the development team I currently lead and shared the library with them.

The problems I encountered when attempting to add SSL support were frustrating. Fortunately, I don’t usually need to include support for SSL calls in my day-to-day development work, but if I did, having to rewrite connection code to allow support would require additional effort, and I’d lose the convenience of using the RestTemplate object. That aside, I do see the value of this functionality, and I believe it would be worth changing my approach if it allowed me to test my code with unit tests better.

The ability to export the service calls and responses for distribution to other engineers and provide content for a virtual service solution brought added value to the tool, over and above what I would have expected from such a library.

I enjoyed how each piece of functionality in the documentation included a link to a class with a fully implemented example of the functionality. This approach allowed me to begin my tests from a working base, and exposed me to additional features which I might not have realized were available. The inclusion of an additional challenge with my original set of challenges to resolve the problems I ran into with the failure conditions was a prime example of this.

All told, I probably spent 10 to 15 hours working with the library and implementing various use cases in my code. I suspect that there is a great deal of functionality available which I have yet to discover, and there may well be documented solutions to the problems that I encountered. I’m looking forward to continuing to use this library in my daily development activities.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Mike Mackrory
United States United States
Mike Mackrory is a Global citizen who has settled down in the Pacific Northwest - for now. By day he works as a Senior Engineer on a Quality Engineering team and by night he writes, consults on several web based projects and runs a marginally successful eBay sticker business. When he’s not tapping on the keys, he can be found hiking, fishing and exploring both the urban and the rural landscape with his kids. Always happy to help out another developer, he has a definite preference for helping those who bring gifts of gourmet donuts, craft beer and/or Single-malt Scotch.

You may also be interested in...

Comments and Discussions

 
-- There are no messages in this forum --
Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web06-2016 | 2.8.180906.1 | Last Updated 8 May 2018
Article Copyright 2018 by Mike Mackrory
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid