Conditional Deployment in Arquillian - java

Looking at the Arquillian documentation, I am aware that I can use the #ArquillianSuiteDeployment and #Deployment annotations to deploy my desired jars/wars to the container. Example:
#ArquillianSuiteDeployment
public class MyDeployer {
#Deployment(name = "myapp", order = 1, testable = false)
public static Archive<?> myDeploymentJar() {
final File file = Maven.configureResolver().fromFile(System.getProperty("settings.xml"))
.loadPomFromFile("pom.xml").resolve("com.myapp:test-app").withoutTransitivity()
.asSingleFile();
final JavaArchive jar = ShrinkWrap.createFromZipFile(JavaArchive.class, file);
final JavaArchive archive = ShrinkWrap.create(JavaArchive.class, "test-app.jar").merge(jar);
return archive;
}
}
Let's infer that I have two more jars that I would like to deploy, but never together during the same test, JAR-A and JAR-C.
#Test
#RunAsClient
public void testOne() {
// deploy JAR-A before all others, but do not deploy JAR-C
}
#Test
#RunAsClient
public void testTwo() {
// deploy JAR-C before all others, but do not deploy JAR-A
}
Is it possible to introduce a conditional #Deployment that would go along with my tests?

That's an interesting usecase.
Deployments happen before execution of the tests so it's not possible at the moment to achieve it just by using annotations.
You can, however, programmatically control deployments using Deployer service.
#Deployment(name = "JAR-C", managed = false)
public static WebArchive create() {
return ShrinkWrap.create(JavaArchive.class);
}
#ArquillianResource
private Deployer deployer;
#Test
public void should_work() {
deployer.deploy("JAR-C");
// test
deploy.undeploy("JAR-C");
}
Important fact is that you have to instruct Arquillian to not manage this deployment for you, that's what managed = false flag is for in #Deployment(name = "X", managed = false).
Hope that helps.

Related

Could not find an 'annotation declaring class' for unit tests

I am setting up an Spring boot application on Jenkins. For the unit tests i am getting below error. This error is not particular to one test cases. Every time I run it is giving me error for different test. I am not sure what is wrong. Same project is working fine (build and unit tests) on local and other environments like (development, stage). Any idea with below errors?
00:49:42.836 [main] DEBUG org.springframework.test.context.support.ActiveProfilesUtils - Could not find an 'annotation declaring class' for annotation type [org.springframework.test.context.ActiveProfiles] and class [com.abc.services.tokens.crypto.aws.AesGcmDynamoCryptoCipherProviderTest]
00:49:42.836 [main] INFO org.springframework.test.context.support.DefaultTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener#43195e57, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener#333291e3, org.springframework.test.context.support.DependencyInjectionTestExecutionListener#479d31f3, org.springframework.test.context.support.DirtiesContextTestExecutionListener#40ef3420]
Here is the test class
#SuppressWarnings("unchecked")
public class AesGcmDynamoCryptoCipherProviderTest extends AbstractTestNGBeanMockingTests {
#MockBean
AwsCrypto awsCrypto;
#MockBean
DynamoDBProvider dynamoDBProvider;
#MockBean
MasterKeyProvider masterKeyProvider;
#MockBean
Table table;
private static Item mockCipherItem(UUID cipherId) {
Item item = mock(Item.class);
return item;
}
private static <T> CryptoResult<T, ?> mockCryptoResult(T result) {
// do something
return cryptoResult;
}
#BeforeMethod
private void init() {
CryptoResult<String, ?> decryptoResult = mockCryptoResult(Base64.getEncoder().encodeToString("*decrypted*".getBytes()));
CryptoResult<String, ?> encryptoResult = mockCryptoResult("*encrypted*");
}
#Test
public void testGetCipher() {
AesGcmDynamoCryptoCipherProvider provider = new AesGcmDynamoCryptoCipherProvider("table", awsCrypto, dynamoDBProvider, masterKeyProvider);
UUID cipherId = UUID.randomUUID();
Item cipherItem = mockCipherItem(cipherId);
AesGcmCipher cipher = provider.getCipher(cipherId);
assertNotNull(cipher);
assertEquals(cipher.getCipherId(), cipherId);
}
}
Base class
#ContextConfiguration(classes = { //...
AbstractTestNGBeanMockingTests.MockBeanConfiguration.class //...
})
#DirtiesContext
public class AbstractTestNGBeanMockingTests extends AbstractTestNGSpringContextTests {
private static ThreadLocal<Class<? extends AbstractTestNGBeanMockingTests>> currentTestClass = new ThreadLocal<>();
#AfterClass(alwaysRun = true)
#Override
protected void springTestContextAfterTestClass() throws Exception {
super.springTestContextAfterTestClass();
}
#BeforeClass(alwaysRun = true, dependsOnMethods = { "springTestContextBeforeTestClass" })
#Override
protected void springTestContextPrepareTestInstance() throws Exception {
currentTestClass.set(this.getClass());
super.springTestContextPrepareTestInstance();
currentTestClass.set(null);
}
#BeforeMethod
public void initializeMockedBeans() {
MockBeanRegistration.initializeMockedBeans(this);
}
protected static class MockBeanConfiguration {
MockBeanConfiguration(ApplicationContext context) {
MockBeanRegistration.registerMocks((BeanDefinitionRegistry) context, currentTestClass.get());
}
}
}
I have bumped into this error after moving classes into new packages somewhere under the java folder, but omitting to move the corresponding test classes in the test folders.
After applying the changes in the test packages as well, it runs again.
You wrote that you experience the problem only in the Jenkins environment.
My guess is that Jenkins starts always with a new checkout of the project from a 100% clean status. In the other environments you might have some residues from the previous development, and these somehow allow the tests to 'work', but I would expect that it is Jenkins getting it right...
Try to setup the app in a development environment from scratch. If you get the error, so you will properly analyze it and correct it.

#SpringBootTest + #BeforeAll

I have a small spring boot app with database and rabbitmq usages.
So I would like to test with integration test (H2 + apache qpid).
#ExtendWith(SpringExtension.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = TestSpringConfig.class)
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
As my app expect database and mq Im using #BeforeAll to start it:
#BeforeAll
public void before() {
startMessageBroker();
startDatabase();
}
The problem is that my web app starts before database/mq defined in #BeforeAll.
org.springframework.test.context.junit.jupiter.SpringExtension:
public class SpringExtension implements BeforeAllCallback, AfterAllCallback, TestInstancePostProcessor,
BeforeEachCallback, AfterEachCallback, BeforeTestExecutionCallback, AfterTestExecutionCallback,
ParameterResolver {
// ...
#Override
public void beforeAll(ExtensionContext context) throws Exception {
getTestContextManager(context).beforeTestClass();
}
// ...
#Override
public void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception {
getTestContextManager(context).prepareTestInstance(testInstance);
}
// ...
Web app starts in postProcessTestInstance phase and #BeforeAll methods in beforeAll.
org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor:
private void execute(TestDescriptor testDescriptor, C parentContext, ExecutionTracker tracker) {
Node<C> node = asNode(testDescriptor);
tracker.markExecuted(testDescriptor);
C preparedContext;
try {
preparedContext = node.prepare(parentContext); // 1 <<<
SkipResult skipResult = node.shouldBeSkipped(preparedContext);
if (skipResult.isSkipped()) {
this.listener.executionSkipped(testDescriptor, skipResult.getReason().orElse("<unknown>"));
return;
}
}
catch (Throwable throwable) {
rethrowIfBlacklisted(throwable);
// We call executionStarted first to comply with the contract of EngineExecutionListener
this.listener.executionStarted(testDescriptor);
this.listener.executionFinished(testDescriptor, TestExecutionResult.failed(throwable));
return;
}
this.listener.executionStarted(testDescriptor);
TestExecutionResult result = singleTestExecutor.executeSafely(() -> {
C context = preparedContext;
try {
context = node.before(context); // 2 <<<
C contextForDynamicChildren = context;
context = node.execute(context, dynamicTestDescriptor -> {
this.listener.dynamicTestRegistered(dynamicTestDescriptor);
execute(dynamicTestDescriptor, contextForDynamicChildren, tracker);
});
C contextForStaticChildren = context;
// #formatter:off
testDescriptor.getChildren().stream()
.filter(child -> !tracker.wasAlreadyExecuted(child))
.forEach(child -> execute(child, contextForStaticChildren, tracker));
// #formatter:on
}
finally {
node.after(context);
}
});
this.listener.executionFinished(testDescriptor, result);
}
See points 1 and 2. There are executions of 'prepare' and then 'before'.
Im not sure is it issue of junit, SpringExtension or Im doing something wrong.
Any advice?
junit-jupiter: 5.0.1
spring-test: 5.0.0.RELEASE
spring-boot-test: 1.5.8.RELEASE
Checkout https://www.testcontainers.org/ it provides integration with JUnit to launch RabbitMQ and a database in docker containers as part of the JUnit testing. This makes integration tests much realistic because you using the same versions of database and message queue would be using in production.
This is by design, I think. Try to add the Bean post-processor/Context initializer to init/start your DB/rabbitMQ..
Is there any reason to start the DB and the message broker in test class? It seems to me that this is wrong by design. They both should be started along with your application context since they are part of your infrastructure.
It's not a responsibility of your tests to set up your infrastructure!
IMHO, a better way of doing things is the following:
Use H2 dependency with a test scope in maven + configure a starter in a way it starts H2 when the application context is starting
Start apache qpid (preferably embedded) on application start
In #Before just make sure you clean up stuff before running a test case
JUnit 5 [#BeforeAll] annotation is replacement of #BeforeClass annotation in JUnit 4.
It is used to signal that the annotated method should be executed before all tests in the current test class.
#BeforeAll should be used in static method
For More reading:
http://junit.org/junit5/docs/current/user-guide/#writing-tests-annotations

How to load the HSQL DB once before running all junit in Java project

My project is not Spring based .Its a java with Hibernate.Building tool - Maven.
I am loading data from one database to HSQL DB before running junits.
My DB util class:
public class DatabaseUtil {
SchemaLoad schemaLoad = new SchemaLoad();
DataLoad dataLoad = new DataLoad();
boolean dataLoaded = false;
static final String filename1 = "test1.txt";
static final String filename2 = "text2.txt";
void dbLoad() throws SQLException {
if (!dataLoaded) {
schemaLoad.cloneSchema(filename1);
dataLoad.exportData(filename2);
System.out.println("***********executed**********8");
dataLoaded = true;
}
}
}
First Test Case:
public class TestCase {
TrainRepository trainRepository = new TrainRepositoryImpl();
DatabaseUtil databaseUtil = new DatabaseUtil();
#BeforeClass
private void setUp() throws SQLException {
databaseUtil.dbLoad();
}
#Test
private void positiveTestCaseForTrainRepo() throws Exception {
//TestCases
}
Second Test case:
public class TestCase1 {
AirRepository airRepository = new AirRepositoryImpl();
DatabaseUtil databaseUtil = new DatabaseUtil();
#BeforeClass
private void setUp() throws SQLException {
databaseUtil.dbLoad();
}
#Test
private void positiveTestCaseForAirRepo() throws Exception {
//TestCases
}
Both the test cases are running fine.But Its executing databaseUtil.dbLoad(); method on each junit.
My question is I need to load the database only once ie before start of first junit and need to set some indicator .The further junits need to check the DB instance If DB instance is there it should not load the data ie DatabaseUtil class need to be singleton.
All the junits are running through maven suffire plugin during mvn install phase.
Kindly help me to achieve this.
void dbLoad() will be called each time.
then use a static variable to keep track
static boolean dataLoaded = false;
if you don't use spring you need to implement caching yourself. you have a few option. use static field with some kind of synchronization (in case you use/plan to use threads). other option is to switch to testng that gives you #BeforeGroup functionality so you can mark all your db tests and have your initialization run before.

Arquillian+OSGi+Test Different Framework Properties

I have an OSGi bundle which reads some properties from the config.properties file of Apache Felix during the activation process, and if this configuration is malformed or absent then the bundle should not start. For this, I am creating its respective Unit Test, I am using Arquillian for the tests. The problem arises when I want to provide different types of conf.properties to different Arquillian tests, in order to cover each scenario.
When Arquillian runs the tests it load a framework.properties file from the /test/resources/ folder to initialize Apache Felix, install the test bundles and run the tests. Now, my question is how can I provide a different framework.properties file for each test case?
Here is the Arquillian Unit Test I use:
#RunWith(Arquillian.class)
public class PersistenceLoaderTest {
#Deployment
public static Archive<?> createDeployment() {
final JavaArchive archive = ShrinkWrap.create(JavaArchive.class, "persistence-arq.jar");
archive.addClass(ProviderLoader.class);
archive.setManifest(new Asset() {
public InputStream openStream() {
OSGiManifestBuilder builder = OSGiManifestBuilder.newInstance();
builder.addBundleSymbolicName(archive.getName());
builder.addBundleManifestVersion(2);
builder.addImportPackages("org.osgi.service.startlevel", "org.osgi.service.url");
builder.addImportPackages(ProviderLoader.class);
return builder.openStream();
}
});
return archive;
}
#ArquillianResource
public Bundle bundle;
#ArquillianResource
BundleContext bundleContext;
#Test
public void loadFrameworkConfiguration(){
// What goes here?
}
}
And the framework.properties file:
# The Felix Framewok log level
#
# ERROR = 1;
# WARNING = 2;
# INFO = 3;
# DEBUG = 4;
felix.log.level=4
org.domain.database=mydb
org.domain.driver=org.hsqldb.jdbcDriver
org.domain.url=jdbc:hsqldb:file:
org.domain.username=sa
org.domain.password=
These are the property values I need to change and test them for different scenarios.
To my understanding this is Container level properties and not Deployment level properties, so you would need to restart the container for it to take effect.
You could achieve this by setting the Container mode in arquillian.xml to manual.
<arquillian>
<container qualifier="manual_felix" mode="manual">
</container>
</arquillian>
Then in a TestClass you can inject the ContainerController and start it with new properties for each run.
#RunWith(Arquillian.class)
public class TestA {
#Deployment(name = "x", managed = false) #TargetsContainer("manual_felix")
public static Archive<?> deployment() {
return ShrinkWrap.create....
}
#ArquillianResource
private ContainerController cc;
#ArquillianResource
private Deployer d;
#Test #InSequence(1)
public void start() {
cc.start("manual_felix", new Config().add("frameworkProperties", "my-custom-properties-file"));
d.deploy("x");
}
#Test #InSequence(2) #OperatesOnDeployment("x")
public void shouldDoSomethingInsideX() {
// executes inside container in context of X
}
}

Sanitize a database during Arquillian testing

I am currently writing tests for some REST-full Services I wrote. The services I am testing are written in Java and use MongoDb/Morphia. The tests call on the services, some of which in turn write to a test collection. I need to clean up after the tests and delete the data I injected. What is the best way to go about this?
Here is an example of one of my simple services:
package org.haib.myerslab.services;
#Path("/database")
public class DatabaseService {
#Inject
private Datastore ds;
#Path("/genre/")
#POST
#Produces("application/json")
public GenreDTO postFromGenreDTO(#Context UriInfo uri, GenreDTO form) throws ParseException {
Genre myNewGenre = DtoToDomainMapper.gerneFromGenreDTO(form);
myNewGenre.setId(form.getId());
ds.save(myNewGenre);
return new GenreDTO(myNewGenre);
}
}
And here is an example of my Arquillian test:
#RunWith(Arquillian.class)
public class GeneTest {
private static String myId = "myGenreId";
private static String myGenre = "myGenre";
private static String myGenreInfo = "myGenreInfo";
#Deployment
public static WebArchive getDeployment() {
return TestHelper.getDeployment();
}
#Test
#RunAsClient
#InSequence(1)
public void canPostGenre(#ArquillianResource URL baseURL) throws Exception {
GenreDTO newGenre = new GenreDTO();
newGenre.setGenre(myGenre);
newGenre.setGenreInfo(myGenreInfo);
newGenre.setId(myId);
String url = baseURL.toURI().resolve("/database/genre/").toString();
JsonNode rootNode = TestHelper.postUrl(url, newGene);
assertEquals(myGenre, rootNode.get("genre").asText());
assertEquals(myGenreInfo, rootNode.get("genreInfo").asText());
assertEquals(myId, rootNode.get("id").asText());
}
}
Where the getDeployment function looks like this:
public static WebArchive getDeployment() {
File[] depend = Maven.resolver().loadPomFromFile("pom.xml").importRuntimeDependencies().resolve().withTransitivity().asFile();
WebArchive war = ShrinkWrap.create(WebArchive.class).addClass(TestHelper.class)
.addClass(Genre.class).addClass(Application.class).addPackage("org/haib/myerslab")
.addPackage("org/haib/myerslab/database").addPackage("org/haib/myerslab/genre")
.addPackage("org/haib/myerslab/dto").addPackage("org/haib/myerslab/dto/genre")
.addAsLibraries(depend).addAsWebInfResource("jboss-deployment-structure.xml")
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml").setWebXML("test-web.xml");
return war;
}
So where I am lost is, what is the best way to Inject the database in an #After, and clear our the Genre Class I posted into it so that my next test doesn't have it there.
How should I do this? Is there another way?
Take a look at nosql-unit. It provides annotations and rules that help you with seeding datasets, comparing expectations and cleaning up MongoDB.
To get your MongoDB into a pristine state before executing a test, you can simply use the following Annotation with the `CLEAN_INSERT´ :
#UsingDataSet(locations="my_data_set.json", loadStrategy=LoadStrategyEnum.CLEAN_INSERT)
public void canPostGenre() { ...}
If you need the behavior around the integration testing lifecycle with MongoDB to be more powerful, you can also roll your own based on the ideas of nosql-unit. Also make sure to check out Junit Rules.

Categories