The use case which lead to the birth of the ShrinkWrap project may be summed up: identify deployments for simplified integration testing.
So we built an API which made it easy to group resources together in a single deployable unit:
JavaArchive archive = ShrinkWrap.create(JavaArchive.class, "deployment.jar") .addClass(MyEjb.class, MyCdiBean.class);
Combined with the Arquillian Test Platform which deploys objects like the above into Application Servers, Servlet Containers, and IoC Containers, the result is a full integration test which writes and executes like a unit test. This is the heart of the Arquillian component model.
ShrinkWrap excels at filing byte-based content into a tree structure; that’s all a virtual filesystem does (or should do!). But for a long while now there’s been a divide which has made testing more complex deployments difficult: you might not always have the content you want to insert on-hand. Consider the following use cases:
- A CDI bean which has library dependencies on a 3rd-party project
- An EAR module which is composed of a few JARs, WARs, etc. which are built by other projects.
- Module A depends on Module B, and you want to easily test the development versions (ie. unreleased) without changing your test code to match
The problem we have here is that once you start calling out to code that’s not in your project, you need to incorporate dependencies into your deployments. And finding these dependencies is a nontrivial problem to solve:
- Not every developer will be keeping a dependency repository in the same place on her machine
- Versions change as development progresses
- If you’re testing as part of your build, and your test relies upon a build output (like a JAR), you’ve got a cyclic dependency
This gap is the number one complaint we get in the Arquillian Community, and I’ve been happy to take the blame from the ShrinkWrap side because I’ve refused to offer up stopgap solutions. I don’t like software that guesses, and I especially don’t like it when software guesses incorrectly. Behaviour should be clear, concise, and deterministic.
So what we have really is two different problems: First, getting dependency content into a ShrinkWrap archive. That’s easy. The harder part is in resolving the dependencies from some coordinates which make sense to put into a test.
Perhaps the biggest asset made available by the Maven community is its expansive Central repository. Regardless of the build tool you’re using, this repo makes libraries available under a coordinate system which acts as a unique address (per repository).
Additionally, the Aether project, used by the Maven backend, is a standalone dependency resolver. Unfortunately, the Aether API isn’t really built to handle the kinds of slimmer-code aims we espouse in the Arquillian community.
Perhaps no one is aware of the warts in testing enterprise software better than the JBoss Quality Engineering team in Brno, and our own Karel Piwko last year started prototyping an adaptor between ShrinkWrap and the Aether backend. What he built worked, and over the next several months we got a handle on exactly how users were hoping to pull dependencies into their deployments. We’re now happy with the API and seeking more input to refine it before it locks as backwards-compatible, so:
Tonight we’d like to officially announce ShrinkWrap Resolvers (SWR), 2.0.0-alpha-2.
The base has no compile-time dependencies (though you’ll need a bunch at runtime), and is capable of resolving to Files, InputStreams, or a custom type if you’d like to provide your own
The syntax follows generally the form:
Resolve [this coordinate set] using [some resolution strategy] formatted as [a type]
Its use looks like:
File junit = Maven.resolver().resolve("junit:junit:4.10") .withoutTransitivity().asSingle(File.class);
It’s also easy to pull in transitive dependencies associated with a coordinate:
File dependencyAndTransitives = Maven.resolver().resolve("groupId:artifactId:version") .withTransitivity().as(File.class);
Often we need to run resolution using alternate repositories or project-specific configuration. In that case:
When we need to test the current version of the software we’re building, we might want to take advantage of all the
dependencyManagement settings in our POM such that we don’t have to manually specify the version. In that case:
And perhaps we just want to get everything the current POM has defined as a dependency, with correct versions:
We’re offering a separate “ShrinkWrap Archive” extension to SWR, enabling direct resolution to an archive:
JavaArchive jar = ShrinkWrapMaven.resolver().resolve("groupId:artifactId:version") .withoutTransitivity().asSingle(JavaArchive.class);
SWR also supports offline mode, and integration with a Plugin enables the developer to skip explicitly setting the
Over the next few alpha-level releases we’ll work on validating that the API makes sense, building up documentation, and porting in more essential features. As always, we’d love you to get involved in development or offer us your experience through feature requests and bug reports.