├── src ├── test │ ├── resources │ │ ├── META-INF │ │ │ ├── beans.xml │ │ │ └── persistence.xml │ │ └── simplelogger.properties │ └── java │ │ └── info │ │ └── novatec │ │ └── beantest │ │ └── demo │ │ ├── ejb │ │ ├── DeltaSpikeSingletonInjectionTest.java │ │ ├── DeltaSpikePersistenceExceptionPropagationTest.java │ │ ├── DeltaSpikeMockProducerTest.java │ │ ├── MyEjbServiceThatCallsAnExternalService.java │ │ ├── MyEJBServiceWithEntityManagerSetter.java │ │ ├── MyExternalService.java │ │ ├── MyEjbSingleton.java │ │ ├── DeltaSpikeRollbackExceptionTest.java │ │ ├── TestSingletonInjection.java │ │ ├── TestPersistenceExceptionPropagation.java │ │ ├── TestExternalServices.java │ │ ├── MyOtherEJBService.java │ │ ├── TestNoRollbackException.java │ │ ├── DeltaSpikeEjbJpaTest.java │ │ ├── TestEJBInjection.java │ │ └── MyEJBService.java │ │ ├── exceptions │ │ └── MyException.java │ │ ├── mocks │ │ └── ExternalServicesMockProducer.java │ │ └── entities │ │ ├── MyEntity.java │ │ └── MyEntityWithConstraints.java └── main │ ├── resources │ └── META-INF │ │ ├── services │ │ └── javax.enterprise.inject.spi.Extension │ │ └── beans.xml │ └── java │ └── info │ └── novatec │ └── beantest │ ├── api │ ├── CdiContainerShutdown.java │ ├── BaseBeanTest.java │ └── BeanProviderHelper.java │ ├── transactions │ ├── Transactional.java │ └── TransactionalInterceptor.java │ ├── extension │ ├── AnnotationInstances.java │ ├── InjectionHelper.java │ └── BeanTestExtension.java │ └── producers │ └── EntityManagerProducer.java ├── .travis.yml ├── .gitignore ├── NOTICE.txt ├── .github └── workflows │ └── maven-publish.yml ├── README.md ├── pom.xml └── LICENSE /src/test/resources/META-INF/beans.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk7 4 | -------------------------------------------------------------------------------- /src/test/resources/simplelogger.properties: -------------------------------------------------------------------------------- 1 | org.slf4j.simpleLogger.log.info.novatec=debug -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension: -------------------------------------------------------------------------------- 1 | info.novatec.beantest.extension.BeanTestExtension -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .project 3 | .classpath 4 | .settings/ 5 | nb-configuration.xml 6 | *.log 7 | *.versionsBackup 8 | .vscode/ -------------------------------------------------------------------------------- /src/main/resources/META-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | info.novatec.beantest.transactions.TransactionalInterceptor 9 | 10 | 11 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | Bean Testing 2 | Copyright 2013 NovaTec GmbH. 3 | 4 | This product includes software developed for NovaTec Consulting GmbH 5 | 6 | This product includes software from: 7 | 8 | Simple Logging Facade for Java (SLF4J) (http://www.slf4j.org/) under the MIT License. 9 | Apache Deltaspike (http://deltaspike.apache.org/) under the Apache License 2.0. 10 | JBoss Weld (http://weld.cdi-spec.org/) under the Apache Licesne 2.0. 11 | Junit (http://junit.org/) under the Eclipse Public License 1.0. 12 | Mockito (https://code.google.com/p/mockito/) under the MIT License. 13 | Hamcrest (https://code.google.com/p/hamcrest/) under the BSD License. 14 | -------------------------------------------------------------------------------- /src/test/java/info/novatec/beantest/demo/ejb/DeltaSpikeSingletonInjectionTest.java: -------------------------------------------------------------------------------- 1 | package info.novatec.beantest.demo.ejb; 2 | 3 | import org.apache.deltaspike.testcontrol.api.junit.CdiTestRunner; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | 7 | import javax.inject.Inject; 8 | 9 | import static org.hamcrest.Matchers.is; 10 | import static org.junit.Assert.assertThat; 11 | 12 | @RunWith(CdiTestRunner.class) 13 | public class DeltaSpikeSingletonInjectionTest { 14 | 15 | @Inject 16 | private MyEjbSingleton singleton; 17 | 18 | @Test 19 | public void shouldBeInstantiatedOnce() { 20 | assertThat(singleton.wasEjbCalled(), is(false)); 21 | singleton.callAnEjb(); 22 | assertThat(singleton.wasEjbCalled(), is(true)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/info/novatec/beantest/api/CdiContainerShutdown.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Bean Testing. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package info.novatec.beantest.api; 18 | 19 | /** 20 | * Represents a CDI container shutdown event. 21 | *

22 | * This event will be fired when the CDI container is about to shutdown. 23 | * 24 | * @author Carlos Barragan (carlos.barragan@novatec-gmbh.de) 25 | */ 26 | public class CdiContainerShutdown { 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/info/novatec/beantest/demo/exceptions/MyException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Bean Testing. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package info.novatec.beantest.demo.exceptions; 18 | 19 | /** 20 | * Demo Exception. 21 | * 22 | * @author Carlos Barragan (carlos.barragan@novatec-gmbh.de) 23 | */ 24 | public class MyException extends RuntimeException { 25 | 26 | public MyException(String message) { 27 | super(message); 28 | } 29 | 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/info/novatec/beantest/demo/ejb/DeltaSpikePersistenceExceptionPropagationTest.java: -------------------------------------------------------------------------------- 1 | package info.novatec.beantest.demo.ejb; 2 | 3 | import info.novatec.beantest.demo.entities.MyEntityWithConstraints; 4 | import org.apache.deltaspike.testcontrol.api.junit.CdiTestRunner; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | 8 | import javax.inject.Inject; 9 | import javax.persistence.PersistenceException; 10 | 11 | import static org.junit.Assert.fail; 12 | 13 | @RunWith(CdiTestRunner.class) 14 | public class DeltaSpikePersistenceExceptionPropagationTest { 15 | 16 | @Inject 17 | private MyEJBService myEJBService; 18 | 19 | @Test(expected = PersistenceException.class) 20 | public void shouldCauseExceptionBecuaseUniquenessViolation() { 21 | MyEntityWithConstraints entity = new MyEntityWithConstraints("123"); 22 | myEJBService.save(entity); 23 | entity = new MyEntityWithConstraints("123"); 24 | myEJBService.save(entity); 25 | fail("Should have failed because uniqueness violation"); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/info/novatec/beantest/demo/ejb/DeltaSpikeMockProducerTest.java: -------------------------------------------------------------------------------- 1 | package info.novatec.beantest.demo.ejb; 2 | 3 | import info.novatec.beantest.demo.mocks.ExternalServicesMockProducer; 4 | import org.apache.deltaspike.testcontrol.api.junit.CdiTestRunner; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.mockito.Mockito; 8 | 9 | import javax.inject.Inject; 10 | 11 | import static org.hamcrest.CoreMatchers.is; 12 | import static org.junit.Assert.assertThat; 13 | 14 | @RunWith(CdiTestRunner.class) 15 | public class DeltaSpikeMockProducerTest { 16 | 17 | @Inject 18 | private MyEjbServiceThatCallsAnExternalService service; 19 | 20 | @Test 21 | public void shouldCallExternalServiceMock() { 22 | MyExternalService externalService = ExternalServicesMockProducer.getExternalService(); 23 | //Since the ExternalServicesMockProducer returns a Mockito mock, we can initialize it 24 | Mockito.when(externalService.doSomething()).thenReturn("Hello World"); 25 | 26 | assertThat(service.callExternalService(), is("Hello World")); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.github/workflows/maven-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a package using Maven and then publish it to GitHub packages when a release is created 2 | # For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#apache-maven-with-a-settings-path 3 | 4 | name: Maven Package 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | permissions: 15 | contents: read 16 | packages: write 17 | 18 | steps: 19 | - uses: actions/checkout@v3 20 | - name: Set up JDK 11 21 | uses: actions/setup-java@v3 22 | with: 23 | java-version: '11' 24 | distribution: 'temurin' 25 | server-id: github # Value of the distributionManagement/repository/id field of the pom.xml 26 | settings-path: ${{ github.workspace }} # location for the settings.xml file 27 | 28 | - name: Build with Maven 29 | run: mvn -B package --file pom.xml 30 | 31 | - name: Publish to GitHub Packages Apache Maven 32 | run: mvn deploy -s $GITHUB_WORKSPACE/settings.xml 33 | env: 34 | GITHUB_TOKEN: ${{ github.token }} 35 | -------------------------------------------------------------------------------- /src/test/java/info/novatec/beantest/demo/ejb/MyEjbServiceThatCallsAnExternalService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Bean Testing. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package info.novatec.beantest.demo.ejb; 18 | 19 | import javax.ejb.EJB; 20 | import javax.ejb.Stateless; 21 | 22 | /** 23 | * 24 | * @author Carlos Barragan (carlos.barragan@novatec-gmbh.de) 25 | */ 26 | @Stateless 27 | public class MyEjbServiceThatCallsAnExternalService { 28 | 29 | @EJB 30 | MyExternalService externalService; 31 | 32 | public String callExternalService() { 33 | return externalService.doSomething(); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/info/novatec/beantest/demo/ejb/MyEJBServiceWithEntityManagerSetter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Bean Testing. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package info.novatec.beantest.demo.ejb; 17 | 18 | import javax.ejb.Stateless; 19 | import javax.persistence.EntityManager; 20 | import javax.persistence.PersistenceContext; 21 | 22 | @Stateless 23 | public class MyEJBServiceWithEntityManagerSetter { 24 | 25 | private EntityManager em; 26 | 27 | @PersistenceContext(unitName = "db2") 28 | public void setEm(EntityManager em) { 29 | this.em = em; 30 | } 31 | 32 | public EntityManager getEm() { 33 | return em; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/test/resources/META-INF/persistence.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | jdbc/productionDS 7 | 8 | 9 | 10 | org.hibernate.ejb.HibernatePersistence 11 | info.novatec.beantest.demo.entities.MyEntity 12 | info.novatec.beantest.demo.entities.MyEntityWithConstraints 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/test/java/info/novatec/beantest/demo/ejb/MyExternalService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Bean Testing. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package info.novatec.beantest.demo.ejb; 18 | 19 | /** 20 | * Represents an external service whose implementation is located somewhere else. 21 | *

22 | * It is common to have dependencies to external services or modules in a JEE Application. 23 | * Usually it is possible to access those external services via a shared interface. The implementation of 24 | * such interface is normally located in another module or application. 25 | * @author Carlos Barragan (carlos.barragan@novatec-gmbh.de) 26 | */ 27 | public interface MyExternalService { 28 | 29 | String doSomething(); 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/info/novatec/beantest/demo/ejb/MyEjbSingleton.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Bean Testing. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package info.novatec.beantest.demo.ejb; 17 | 18 | import javax.ejb.EJB; 19 | import javax.ejb.Singleton; 20 | 21 | /** 22 | * Demo EJB Singleton. 23 | * @author Carlos Barragan (carlos.barragan@novatec-gmbh.de) 24 | */ 25 | @Singleton 26 | public class MyEjbSingleton { 27 | 28 | @EJB 29 | MyOtherEJBService ejbService; 30 | 31 | private boolean wasEjbCalled=false; 32 | 33 | public void callAnEjb() { 34 | ejbService.doSomething(); 35 | wasEjbCalled = true; 36 | } 37 | 38 | public boolean wasEjbCalled() { 39 | return this.wasEjbCalled; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/info/novatec/beantest/demo/mocks/ExternalServicesMockProducer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Bean Testing. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package info.novatec.beantest.demo.mocks; 18 | 19 | import info.novatec.beantest.demo.ejb.MyExternalService; 20 | import javax.enterprise.inject.Produces; 21 | import org.mockito.Mockito; 22 | 23 | /** 24 | * Creates Mockito mocks for external services. 25 | * 26 | * @author Carlos Barragan (carlos.barragan@novatec-gmbh.de) 27 | */ 28 | public class ExternalServicesMockProducer { 29 | 30 | private static MyExternalService externalServiceMock=Mockito.mock(MyExternalService.class); 31 | 32 | @Produces 33 | public static MyExternalService getExternalService() { 34 | return externalServiceMock; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/info/novatec/beantest/demo/ejb/DeltaSpikeRollbackExceptionTest.java: -------------------------------------------------------------------------------- 1 | package info.novatec.beantest.demo.ejb; 2 | 3 | import info.novatec.beantest.demo.entities.MyEntity; 4 | import org.apache.deltaspike.testcontrol.api.junit.CdiTestRunner; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | 8 | import javax.inject.Inject; 9 | import javax.persistence.NoResultException; 10 | 11 | import static org.hamcrest.Matchers.*; 12 | import static org.junit.Assert.assertThat; 13 | import static org.junit.Assert.fail; 14 | 15 | @RunWith(CdiTestRunner.class) 16 | public class DeltaSpikeRollbackExceptionTest { 17 | 18 | @Inject 19 | private MyEJBService myEJBService; 20 | 21 | @Test 22 | public void shouldExceptionNoCauseRollback() { 23 | MyOtherEJBService myOtherEJBService = myEJBService.getOtherService2(); 24 | 25 | assertThat(myOtherEJBService.getAllEntities(), hasSize(0)); 26 | 27 | MyEntity entity = new MyEntity(); 28 | entity.setName("some name"); 29 | try { 30 | myEJBService.saveEntityAndCausePersistenceExceptionWithoutRollback(entity); 31 | fail("Should have thrown PersistenceException"); 32 | } catch (NoResultException exception) { 33 | assertThat(exception, not(nullValue())); 34 | } 35 | 36 | 37 | //Entity should have been saved 38 | assertThat(myOtherEJBService.getAllEntities(), hasSize(1)); 39 | 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/info/novatec/beantest/api/BaseBeanTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Bean Testing. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package info.novatec.beantest.api; 18 | 19 | import java.lang.annotation.Annotation; 20 | import org.junit.After; 21 | import org.junit.Before; 22 | 23 | /** 24 | * Base class for initializing the {@link BeanProviderHelper} 25 | * 26 | * @author Carlos Barragan (carlos.barragan@novatec-gmbh.de) 27 | */ 28 | public abstract class BaseBeanTest { 29 | 30 | private BeanProviderHelper bm; 31 | 32 | @Before 33 | public void initilaize() { 34 | bm = BeanProviderHelper.getInstance(); 35 | } 36 | 37 | @After 38 | public void cleanUp() { 39 | bm.shutdown(); 40 | } 41 | 42 | protected T getBean(Class beanClass, Annotation... qualifiers) { 43 | return bm.getBean(beanClass, qualifiers); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/info/novatec/beantest/transactions/Transactional.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Bean Testing. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package info.novatec.beantest.transactions; 17 | 18 | import java.lang.annotation.ElementType; 19 | import java.lang.annotation.Retention; 20 | import java.lang.annotation.RetentionPolicy; 21 | import java.lang.annotation.Target; 22 | import javax.ejb.TransactionAttributeType; 23 | import javax.enterprise.util.Nonbinding; 24 | import javax.interceptor.InterceptorBinding; 25 | 26 | /** 27 | * Interceptor binding for transactions. 28 | * 29 | * @author Carlos Barragan (carlos.barragan@novatec-gmbh.de) 30 | */ 31 | @InterceptorBinding 32 | @Target({ElementType.METHOD, ElementType.TYPE}) 33 | @Retention(RetentionPolicy.RUNTIME) 34 | public @interface Transactional { 35 | 36 | @Nonbinding 37 | TransactionAttributeType transactionAttribute() default TransactionAttributeType.REQUIRED; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/info/novatec/beantest/demo/ejb/TestSingletonInjection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Bean Testing. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package info.novatec.beantest.demo.ejb; 17 | 18 | import info.novatec.beantest.api.BaseBeanTest; 19 | import static org.hamcrest.Matchers.is; 20 | import static org.junit.Assert.assertThat; 21 | import org.junit.Test; 22 | 23 | /** 24 | * This test verifies that an EJB Singleton is instantiated just once and is able to call other EJBs. 25 | * 26 | * @author Carlos Barragan (carlos.barragan@novatec-gmbh.de) 27 | */ 28 | public class TestSingletonInjection extends BaseBeanTest { 29 | 30 | @Test 31 | public void shouldBeInstantiatedOnce() { 32 | MyEjbSingleton singleton= getBean(MyEjbSingleton.class); 33 | assertThat(singleton.wasEjbCalled(), is(false)); 34 | singleton.callAnEjb(); 35 | assertThat(singleton.wasEjbCalled(), is(true)); 36 | } 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/info/novatec/beantest/demo/ejb/TestPersistenceExceptionPropagation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Bean Testing. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package info.novatec.beantest.demo.ejb; 18 | 19 | import info.novatec.beantest.api.BaseBeanTest; 20 | import info.novatec.beantest.demo.entities.MyEntityWithConstraints; 21 | import javax.persistence.PersistenceException; 22 | import static org.junit.Assert.fail; 23 | import org.junit.Test; 24 | 25 | /** 26 | * This test verifies that persistence exceptions are correctly propagated. 27 | * 28 | * @author Carlos Barragan (carlos.barragan@novatec-gmbh.de) 29 | */ 30 | public class TestPersistenceExceptionPropagation extends BaseBeanTest { 31 | 32 | 33 | 34 | @Test(expected = PersistenceException.class) 35 | public void shouldCauseExceptionBecuaseUniquenessViolation() { 36 | MyEJBService myEJBService = getBean(MyEJBService.class); 37 | MyEntityWithConstraints entity=new MyEntityWithConstraints("123"); 38 | myEJBService.save(entity); 39 | entity=new MyEntityWithConstraints("123"); 40 | myEJBService.save(entity); 41 | fail("Should have failed because uniqueness violation"); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/info/novatec/beantest/demo/entities/MyEntity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Bean Testing. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package info.novatec.beantest.demo.entities; 17 | 18 | import java.io.Serializable; 19 | import javax.persistence.Entity; 20 | import javax.persistence.GeneratedValue; 21 | import javax.persistence.Id; 22 | 23 | /** 24 | * Demo Entity. 25 | * 26 | * @author Carlos Barragan (carlos.barragan@novatec-gmbh.de) 27 | */ 28 | @Entity 29 | public class MyEntity implements Serializable { 30 | 31 | @GeneratedValue 32 | @Id 33 | private long id; 34 | 35 | private String name; 36 | 37 | private String anotherAttribute; 38 | 39 | public long getId() { 40 | return id; 41 | } 42 | 43 | public void setId(long id) { 44 | this.id = id; 45 | } 46 | 47 | public String getName() { 48 | return name; 49 | } 50 | 51 | public void setName(String name) { 52 | this.name = name; 53 | } 54 | 55 | public String getAnotherAttribute() { 56 | return anotherAttribute; 57 | } 58 | 59 | public void setAnotherAttribute(String anotherAttribute) { 60 | this.anotherAttribute = anotherAttribute; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/test/java/info/novatec/beantest/demo/ejb/TestExternalServices.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Bean Testing. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package info.novatec.beantest.demo.ejb; 18 | 19 | import info.novatec.beantest.api.BaseBeanTest; 20 | import info.novatec.beantest.demo.mocks.ExternalServicesMockProducer; 21 | import static org.hamcrest.CoreMatchers.is; 22 | import static org.junit.Assert.assertThat; 23 | import org.junit.Test; 24 | import org.mockito.Mockito; 25 | 26 | /** 27 | * This is an example to demonstrate how to test external services. 28 | * 29 | * @author Carlos Barragan (carlos.barragan@novatec-gmbh.de) 30 | */ 31 | public class TestExternalServices extends BaseBeanTest { 32 | 33 | 34 | @Test 35 | public void shouldCallExternalServiceMock() { 36 | MyExternalService externalService= ExternalServicesMockProducer.getExternalService(); 37 | //Since the ExternalServicesMockProducer returns a Mockito mock, we can initialize it 38 | Mockito.when(externalService.doSomething()).thenReturn("Hello World"); 39 | 40 | MyEjbServiceThatCallsAnExternalService service= getBean(MyEjbServiceThatCallsAnExternalService.class); 41 | assertThat(service.callExternalService(), is("Hello World")); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/info/novatec/beantest/extension/AnnotationInstances.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Bean Testing. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package info.novatec.beantest.extension; 17 | 18 | import info.novatec.beantest.transactions.Transactional; 19 | import javax.ejb.Singleton; 20 | import javax.enterprise.context.ApplicationScoped; 21 | import javax.enterprise.context.RequestScoped; 22 | import javax.inject.Inject; 23 | import org.apache.deltaspike.core.util.metadata.AnnotationInstanceProvider; 24 | 25 | /** 26 | * Class that contains constants of annotation instances. 27 | * 28 | * @author Carlos Barragan (carlos.barragan@novatec-gmbh.de) 29 | */ 30 | public final class AnnotationInstances { 31 | 32 | private AnnotationInstances() { 33 | 34 | } 35 | 36 | public static final Transactional TRANSACTIONAL = AnnotationInstanceProvider.of(Transactional.class); 37 | public static final RequestScoped REQUEST_SCOPED = AnnotationInstanceProvider.of(RequestScoped.class); 38 | public static final Inject INJECT = AnnotationInstanceProvider.of(Inject.class); 39 | public static final Singleton SINGLETON = AnnotationInstanceProvider.of(Singleton.class); 40 | public static final ApplicationScoped APPLICATION_SCOPED = AnnotationInstanceProvider.of(ApplicationScoped.class); 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/info/novatec/beantest/demo/entities/MyEntityWithConstraints.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Bean Testing. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package info.novatec.beantest.demo.entities; 18 | 19 | import java.io.Serializable; 20 | import javax.persistence.Column; 21 | import javax.persistence.Entity; 22 | import javax.persistence.GeneratedValue; 23 | import javax.persistence.Id; 24 | 25 | /** 26 | * Demo entity that contains database constraints. 27 | * 28 | * @author Carlos Barragan (carlos.barragan@novatec-gmbh.de) 29 | */ 30 | @Entity 31 | public class MyEntityWithConstraints implements Serializable{ 32 | 33 | @Id 34 | @GeneratedValue 35 | private long id; 36 | 37 | @Column(unique = true) 38 | private String uniqueValue; 39 | 40 | /** 41 | * Constructor for JPA 42 | */ 43 | protected MyEntityWithConstraints() {} 44 | 45 | public MyEntityWithConstraints(String uniqueValue) { 46 | this.uniqueValue = uniqueValue; 47 | } 48 | 49 | public String getUniqueValue() { 50 | return uniqueValue; 51 | } 52 | 53 | public void setUniqueValue(String uniqueValue) { 54 | this.uniqueValue = uniqueValue; 55 | } 56 | 57 | public long getId() { 58 | return id; 59 | } 60 | 61 | 62 | 63 | 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/test/java/info/novatec/beantest/demo/ejb/MyOtherEJBService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Bean Testing. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package info.novatec.beantest.demo.ejb; 17 | 18 | import info.novatec.beantest.demo.entities.MyEntity; 19 | import info.novatec.beantest.demo.entities.MyEntityWithConstraints; 20 | import info.novatec.beantest.demo.exceptions.MyException; 21 | import java.util.Collection; 22 | import javax.ejb.Stateless; 23 | import javax.persistence.EntityManager; 24 | import javax.persistence.PersistenceContext; 25 | import org.slf4j.Logger; 26 | import org.slf4j.LoggerFactory; 27 | 28 | /** 29 | * Demo EJB Service. 30 | * 31 | * @author Carlos Barragan (carlos.barragan@novatec-gmbh.de) 32 | */ 33 | @Stateless 34 | public class MyOtherEJBService { 35 | private static final Logger LOGGER=LoggerFactory.getLogger(MyOtherEJBService.class); 36 | 37 | @PersistenceContext(unitName = "db2") 38 | EntityManager entityManager; 39 | 40 | /** 41 | * It just logs a message. 42 | */ 43 | public void doSomething() { 44 | LOGGER.info("MyOtherEJBService did something"); 45 | } 46 | 47 | public Collection getAllEntities() { 48 | return entityManager.createQuery("Select E from MyEntity as E", MyEntity.class).getResultList(); 49 | } 50 | 51 | /** 52 | * Throws MyException when called. 53 | *

54 | * See the corresponding test to better understand its purpose. 55 | */ 56 | public void throwException() { 57 | throw new MyException("Oops, exception was thrown"); 58 | } 59 | 60 | public void save(MyEntityWithConstraints entity) { 61 | entityManager.persist(entity); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/test/java/info/novatec/beantest/demo/ejb/TestNoRollbackException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Bean Testing. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package info.novatec.beantest.demo.ejb; 18 | 19 | import info.novatec.beantest.api.BaseBeanTest; 20 | import info.novatec.beantest.demo.entities.MyEntity; 21 | import javax.persistence.NoResultException; 22 | import static org.hamcrest.Matchers.hasSize; 23 | import static org.hamcrest.Matchers.not; 24 | import static org.hamcrest.Matchers.nullValue; 25 | import static org.junit.Assert.assertThat; 26 | import static org.junit.Assert.fail; 27 | import org.junit.Test; 28 | 29 | /** 30 | * Tests that the exceptions documented in http://docs.oracle.com/javaee/6/api/javax/persistence/PersistenceException.html don't cause a rollback. 31 | * 32 | * @author Carlos Barragan (carlos.barragan@novatec-gmbh.de) 33 | */ 34 | public class TestNoRollbackException extends BaseBeanTest { 35 | 36 | @Test 37 | public void shouldExceptionNoCauseRollback() { 38 | MyEJBService myEJBService = getBean(MyEJBService.class); 39 | MyOtherEJBService myOtherEJBService = getBean(MyOtherEJBService.class); 40 | 41 | assertThat(myOtherEJBService.getAllEntities(), hasSize(0)); 42 | 43 | MyEntity entity = new MyEntity(); 44 | entity.setName("some name"); 45 | try { 46 | myEJBService.saveEntityAndCausePersistenceExceptionWithoutRollback(entity); 47 | fail("Should have thrown PersistenceException"); 48 | } catch (NoResultException exception) { 49 | assertThat(exception, not(nullValue())); 50 | } 51 | 52 | 53 | //Entity should have been saved 54 | assertThat(myOtherEJBService.getAllEntities(), hasSize(1)); 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/test/java/info/novatec/beantest/demo/ejb/DeltaSpikeEjbJpaTest.java: -------------------------------------------------------------------------------- 1 | package info.novatec.beantest.demo.ejb; 2 | 3 | import info.novatec.beantest.demo.entities.MyEntity; 4 | import info.novatec.beantest.demo.exceptions.MyException; 5 | import org.apache.deltaspike.testcontrol.api.junit.CdiTestRunner; 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | 9 | import javax.inject.Inject; 10 | 11 | import static org.hamcrest.Matchers.*; 12 | import static org.junit.Assert.*; 13 | 14 | /** 15 | * Rewrote adjacent tests from TestEJBInjection with DeltaSpike´s "test control" module in order to compare the 16 | * configurations needs, code style and importantly to check if the module provides an already solution for deploying 17 | * the concepts and ideas of CDI BeanTest. 18 | */ 19 | @RunWith(CdiTestRunner.class) 20 | public class DeltaSpikeEjbJpaTest { 21 | 22 | @Inject 23 | private MyEJBService myService; 24 | @Inject 25 | private MyEJBServiceWithEntityManagerSetter myEJBServiceWithEntityManagerSetter; 26 | 27 | @Test 28 | public void shouldInjectEJBAsCDIBean() { 29 | myService.callOtherServiceAndPersistAnEntity(); 30 | assertThat(myService.getOtherService2().getAllEntities(), hasSize(1)); 31 | } 32 | 33 | @Test 34 | public void shouldPersistEntityInSpiteOfException() { 35 | MyEntity myEntity = new MyEntity(); 36 | myEntity.setName("Foo"); 37 | //An exception is thrown within the following method call, but because it is caught, the entity should have benn saved. 38 | myService.saveEntityAndHandleException(myEntity); 39 | 40 | assertThat(myService.getOtherService2().getAllEntities(), hasSize(1)); 41 | 42 | } 43 | 44 | /** 45 | * Verifies that the transaction is rolled back properly when an Exception is thrown and not handled. 46 | */ 47 | @Test 48 | public void shouldNotPersistEntityBecauseOfException() { 49 | MyEntity myEntity = new MyEntity(); 50 | myEntity.setName("Foo"); 51 | try { 52 | myService.attemptToSaveEntityAndThrowException(myEntity); 53 | fail("Should have thrown an exception"); 54 | } catch (MyException e) { 55 | assertThat(myService.getOtherService2().getAllEntities(), is(empty())); 56 | } 57 | } 58 | 59 | @Test 60 | public void shouldInjectEJBAsCDIBeanUsingSetter() { 61 | assertNotNull(myService.getOtherService2()); 62 | } 63 | 64 | @Test 65 | public void shouldInjectPersistenceContextUsingSetter() { 66 | assertNotNull(myEJBServiceWithEntityManagerSetter.getEm()); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/info/novatec/beantest/api/BeanProviderHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Bean Testing. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package info.novatec.beantest.api; 17 | 18 | import java.lang.annotation.Annotation; 19 | import javax.enterprise.inject.spi.BeanManager; 20 | import org.apache.deltaspike.cdise.api.CdiContainer; 21 | import org.apache.deltaspike.cdise.api.CdiContainerLoader; 22 | import org.apache.deltaspike.core.api.provider.BeanProvider; 23 | 24 | /** 25 | * Entry point to obtain bean references. 26 | * 27 | * @author Carlos Barragan (carlos.barragan@novatec-gmbh.de) 28 | */ 29 | public class BeanProviderHelper { 30 | 31 | private CdiContainer cdiContainer; 32 | private static final BeanProviderHelper INSTANCE= new BeanProviderHelper(); 33 | 34 | public static BeanProviderHelper getInstance() { 35 | return INSTANCE; 36 | } 37 | 38 | private BeanProviderHelper() { 39 | } 40 | 41 | /** 42 | * Starts the CDI Container and initializes its contexts. 43 | */ 44 | private void bootstrapCdiContainer() { 45 | cdiContainer = CdiContainerLoader.getCdiContainer(); 46 | cdiContainer.boot(); 47 | cdiContainer.getContextControl().startContexts(); 48 | 49 | } 50 | 51 | 52 | /** 53 | * Returns a reference of the given bean class. 54 | * 55 | * @param 56 | * the type of the bean. 57 | * @param beanClass 58 | * the class of the bean whose reference should be returned. 59 | * @param qualifiers 60 | * qualifiers for narrowing the bean instance. This attribute is not required. 61 | * @return the reference of the given bean class. 62 | */ 63 | public T getBean(Class beanClass, Annotation... qualifiers) { 64 | if (cdiContainer == null) { 65 | bootstrapCdiContainer(); 66 | } 67 | return BeanProvider.getContextualReference(beanClass, qualifiers); 68 | } 69 | 70 | /** 71 | * Returns the reference of the {@link BeanManager}. 72 | * 73 | * @return the reference of the {@link BeanManager}. 74 | */ 75 | public BeanManager getBeanManager() { 76 | if (cdiContainer == null) { 77 | bootstrapCdiContainer(); 78 | } 79 | 80 | return cdiContainer.getBeanManager(); 81 | } 82 | 83 | /** 84 | * Shuts down the underlying container. 85 | */ 86 | public void shutdown() { 87 | if (cdiContainer != null) { 88 | try { 89 | fireShutdownEvent(); 90 | } finally { 91 | cdiContainer.shutdown(); 92 | cdiContainer = null; 93 | } 94 | 95 | } 96 | } 97 | 98 | /** 99 | * Fires a {@link ContainerShutdown} CDI event before the CDI container shuts down in order to clean up resources (for example an 100 | * EntityManager). 101 | */ 102 | private void fireShutdownEvent() { 103 | CdiContainerShutdown containerShutdown = new CdiContainerShutdown(); 104 | getBeanManager().fireEvent(containerShutdown); 105 | } 106 | 107 | 108 | } 109 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/NovaTecConsulting/BeanTest.svg?branch=master)](https://travis-ci.org/NovaTecConsulting/BeanTest) 2 | 3 | # Bean Testing for Java EE Applications using CDI 4 | 5 | This project attempts to show an interesting approach on testing Java EE 6 | Applications. It uses a CDI Container to resolve dependencies like EJBs 7 | or Resources when running unit test in a standard environment. 8 | 9 | The name "Bean Testing" is used, since it isn't about proper unit tests. 10 | However, the feedback speed is very close to unit test and the tests 11 | look undistinguishable too. 12 | 13 | **Main advantages:** 14 | 15 | - Very fast test feedback (very close to unit test feedback speed). 16 | 17 | - Dependencies are solved automatically without the need of a JEE 18 | Application Server (or Embedded Server). 19 | 20 | - Everything is CDI so you can easliy extend the functionality. 21 | 22 | - You get basic transaction propagation support. 23 | 24 | - You can provide your own mocks to test external dependencies. 25 | 26 | - You use the usual stuff for configuration: persistence.xml, 27 | beans.xml, Junit, etc. 28 | 29 | **Production ready** 30 | 31 | BeanTest is currently being used in some (big) customer projects. The projects are big Java EE Applications with several subsystems (.ear's). Each subsystem consists of several modules (.jar's) as well. We haven't faced any critical problem. Usually one can fix a problem by using standard CDI features. 32 | 33 | **Examples** 34 | 35 | You can find the examples about how a "Bean" test looks like under 36 | *src/test/java* (look for classes whose names begin with Test\*) 37 | 38 | ## How to use it 39 | 40 | * Add the following dependency in your pom: 41 | 42 | ```xml 43 | 44 | 45 | info.novatec 46 | bean-test 47 | {currentVersion} 48 | test 49 | 50 | ``` 51 | 52 | * You also need to add the following repository to your pom: 53 | 54 | ```xml 55 | 56 | Novatec 57 | Novatec Repository 58 | http://repository.novatec-gmbh.de/content/repositories/novatec 59 | 60 | ``` 61 | * Create an empty beans.xml file under src/test/resources/META-INF 62 | 63 | * Create a persistence unit called "beanTestPU" in your 64 | persistence.xml (you can place it under src/test/resources/META-INF) 65 | 66 | * Write a test similar to the ones in the examples using your 67 | production code. 68 | 69 | * Depending on your project structure, you might get an 70 | *UnsatisfiedResolutionException* if some classes are not available 71 | in your classpath. You can solve this by providing a Mock (See the 72 | mock example for this). 73 | 74 | ## Why you should use it 75 | 76 | First of all, this approach is neither a replacement for unit nor 77 | integration tests. This approach is something in the middle. 78 | 79 | You should always write unit tests for essential business logic. 80 | 81 | You should always write integration tests to check that everything works 82 | as expected. 83 | 84 | So, why use this approach? Because you get the best of both worlds: You 85 | get the speed of unit tests with almost the coverage of integration 86 | tests and all this with minimal configuration and with standard and well 87 | known frameworks like JPA, CDI, Mockito and Junit. 88 | 89 | Since you don't need an application server for running your tests, you 90 | can integrate them in your normal unit test build process. In this way, 91 | you get almost integration test coverage in your normal builds. 92 | 93 | ### Requirements 94 | 95 | - JDK 6 and above. 96 | 97 | - Maven 98 | 99 | ### Contribute 100 | 101 | Just fork. 102 | -------------------------------------------------------------------------------- /src/test/java/info/novatec/beantest/demo/ejb/TestEJBInjection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Bean Testing. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package info.novatec.beantest.demo.ejb; 17 | 18 | import static org.hamcrest.Matchers.empty; 19 | import static org.hamcrest.Matchers.hasSize; 20 | import static org.hamcrest.Matchers.is; 21 | import static org.junit.Assert.assertNotNull; 22 | import static org.junit.Assert.assertThat; 23 | import static org.junit.Assert.fail; 24 | import info.novatec.beantest.api.BaseBeanTest; 25 | import info.novatec.beantest.demo.entities.MyEntity; 26 | import info.novatec.beantest.demo.exceptions.MyException; 27 | 28 | import org.junit.Test; 29 | 30 | /** 31 | * This test verifies that all dependencies are properly resolved as well as the 32 | * transaction is properly propagated among calls. 33 | *

34 | * The database schema is recreated for every test method. 35 | * 36 | * @author Carlos Barragan (carlos.barragan@novatec-gmbh.de) 37 | */ 38 | public class TestEJBInjection extends BaseBeanTest { 39 | 40 | @Test 41 | public void shouldInjectEJBAsCDIBean() { 42 | MyEJBService myService = getBean(MyEJBService.class); 43 | //An Entity should be persisted and you should see a message logged in the console. 44 | myService.callOtherServiceAndPersistAnEntity(); 45 | //Let's create a reference of another EJB to query the database. 46 | MyOtherEJBService myOtherService = getBean(MyOtherEJBService.class); 47 | 48 | assertThat(myOtherService.getAllEntities(), hasSize(1)); 49 | 50 | } 51 | 52 | /** 53 | * Verifies that an entity is saved when an exception is caught by its caller. 54 | */ 55 | @Test 56 | public void shouldPersistEntityInSpiteOfException() { 57 | MyEJBService myService = getBean(MyEJBService.class); 58 | MyEntity myEntity=new MyEntity(); 59 | myEntity.setName("Foo"); 60 | //An exception is thrown within the following method call, but because it is caught, the entity should have benn saved. 61 | myService.saveEntityAndHandleException(myEntity); 62 | 63 | MyOtherEJBService myOtherService = getBean(MyOtherEJBService.class); 64 | assertThat(myOtherService.getAllEntities(), hasSize(1)); 65 | 66 | } 67 | 68 | /** 69 | * Verifies that the transaction is rolled back properly when an Exception is thrown and not handled. 70 | */ 71 | @Test 72 | public void shouldNotPersistEntityBecauseOfException() { 73 | MyEJBService myService = getBean(MyEJBService.class); 74 | MyEntity myEntity=new MyEntity(); 75 | myEntity.setName("Foo"); 76 | try { 77 | myService.attemptToSaveEntityAndThrowException(myEntity); 78 | fail("Should have thrown an exception"); 79 | } catch(MyException e) { 80 | MyOtherEJBService myOtherService = getBean(MyOtherEJBService.class); 81 | assertThat(myOtherService.getAllEntities(), is(empty())); 82 | } 83 | 84 | 85 | 86 | } 87 | 88 | @Test 89 | public void shouldInjectEJBAsCDIBeanUsingSetter() { 90 | MyEJBService myService = getBean(MyEJBService.class); 91 | 92 | assertNotNull(myService.getOtherService2()); 93 | } 94 | 95 | @Test 96 | public void shouldInjectPersistenceContextUsingSetter() { 97 | MyEJBServiceWithEntityManagerSetter myService = getBean(MyEJBServiceWithEntityManagerSetter.class); 98 | 99 | assertNotNull(myService.getEm()); 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/test/java/info/novatec/beantest/demo/ejb/MyEJBService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Bean Testing. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package info.novatec.beantest.demo.ejb; 17 | 18 | import info.novatec.beantest.demo.entities.MyEntity; 19 | import info.novatec.beantest.demo.entities.MyEntityWithConstraints; 20 | import info.novatec.beantest.demo.exceptions.MyException; 21 | import javax.ejb.EJB; 22 | import javax.ejb.Stateless; 23 | import javax.persistence.EntityManager; 24 | import javax.persistence.PersistenceContext; 25 | import javax.persistence.PersistenceException; 26 | import org.slf4j.Logger; 27 | import org.slf4j.LoggerFactory; 28 | 29 | /** 30 | * Demo EJB Service. 31 | *

32 | * This EJB serves as a facade. It calls {@link MyOtherEJBService} to simulate transaction propagation. Furthermore, it provides 33 | * some methods to test proper transaction handling when exceptions are thrown. 34 | * 35 | * @author Carlos Barragan (carlos.barragan@novatec-gmbh.de) 36 | */ 37 | @Stateless 38 | public class MyEJBService { 39 | 40 | private static final Logger LOGGER=LoggerFactory.getLogger(MyEJBService.class); 41 | 42 | @EJB 43 | MyOtherEJBService otherService; 44 | 45 | MyOtherEJBService otherService2; 46 | 47 | @PersistenceContext(unitName = "db2") 48 | EntityManager em; 49 | 50 | @EJB 51 | public void setOtherService2(MyOtherEJBService otherService2) { 52 | this.otherService2 = otherService2; 53 | } 54 | 55 | public MyOtherEJBService getOtherService2() { 56 | return otherService2; 57 | } 58 | 59 | /** 60 | * This method calls {@link MyOtherEJBService#doSomething() } and saves a new {@link MyEntity}. 61 | */ 62 | public void callOtherServiceAndPersistAnEntity() { 63 | otherService.doSomething(); 64 | MyEntity entity = new MyEntity(); 65 | entity.setName("Hello"); 66 | em.persist(entity); 67 | LOGGER.info("Entity persisted!"); 68 | } 69 | 70 | /** 71 | * Saves the given entity even though an exception is thrown when calling {@link MyOtherEJBService#throwException()}. 72 | * 73 | * @param entity the entity that should be saved. 74 | */ 75 | public void saveEntityAndHandleException(MyEntity entity) { 76 | try { 77 | em.persist(entity); 78 | otherService.throwException(); 79 | } catch(MyException e) { 80 | //Empty on purpose. Entity was persisted in spite of the exception. 81 | } 82 | } 83 | 84 | /** 85 | * Tries to persist the given entity but an exception is thrown instead because {@link MyOtherEJBService#throwException()} is called. 86 | * 87 | * @param entity the entity that should be saved. 88 | */ 89 | public void attemptToSaveEntityAndThrowException(MyEntity entity) { 90 | em.persist(entity); 91 | otherService.throwException(); 92 | //Entity should have not been persisted. 93 | } 94 | 95 | /** 96 | * Persists the given entity and throws a {@link PersistenceException} that should not rollback the transaction

97 | * 98 | * @param entity the entity that should be persisted 99 | * 100 | * @see http://docs.oracle.com/javaee/6/api/javax/persistence/PersistenceException.html 101 | */ 102 | public void saveEntityAndCausePersistenceExceptionWithoutRollback(MyEntity entity) { 103 | em.persist(entity); 104 | //Throw a NoResultFoundException. 105 | em.createQuery("Select e from MyEntity as e where e.id = :nonExistentId").setParameter("nonExistentId", -42L).getSingleResult(); 106 | } 107 | 108 | /** 109 | * Saves the given entity by delegating the call to {@link MyOtherEJBService#save(info.novatec.beantest.demo.entities.MyEntityWithConstraints)} 110 | * @param entity the entity that should be saved. 111 | */ 112 | public void save(MyEntityWithConstraints entity) { 113 | otherService.save(entity); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/info/novatec/beantest/producers/EntityManagerProducer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Bean Testing. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package info.novatec.beantest.producers; 17 | 18 | import info.novatec.beantest.api.CdiContainerShutdown; 19 | import java.lang.reflect.Member; 20 | import java.lang.reflect.Method; 21 | import javax.annotation.PostConstruct; 22 | import javax.enterprise.context.RequestScoped; 23 | import javax.enterprise.event.Observes; 24 | import javax.enterprise.inject.Produces; 25 | import javax.enterprise.inject.spi.InjectionPoint; 26 | import javax.persistence.EntityManager; 27 | import javax.persistence.EntityManagerFactory; 28 | import javax.persistence.Persistence; 29 | import javax.persistence.PersistenceContext; 30 | import org.slf4j.Logger; 31 | import org.slf4j.LoggerFactory; 32 | 33 | /** 34 | * Entity manager producer. 35 | *

36 | * It initializes the entity manager to be injected in EJBs 37 | * 38 | * @author Carlos Barragan (carlos.barragan@novatec-gmbh.de) 39 | */ 40 | @RequestScoped 41 | public class EntityManagerProducer { 42 | 43 | private static final Logger LOGGER = LoggerFactory.getLogger(EntityManagerProducer.class); 44 | 45 | private static final String DEFAULT_BEAN_TEST_PERSISTENCE_UNIT = "beanTestPU"; 46 | 47 | private EntityManagerFactory emf; 48 | 49 | private EntityManager em; 50 | 51 | @PostConstruct 52 | private void initializeEntityManagerFactory() { 53 | emf = Persistence.createEntityManagerFactory(DEFAULT_BEAN_TEST_PERSISTENCE_UNIT); 54 | LOGGER.info("Entity Manager Factory was successfully initialized"); 55 | 56 | } 57 | 58 | 59 | @Produces 60 | public EntityManager getEntityManager(InjectionPoint ip) { 61 | PersistenceContext ctx = ip.getAnnotated().getAnnotation(PersistenceContext.class); 62 | 63 | if (ctx == null) { 64 | //if @PersisteceContext is declared on method, ctx is null at this point. 65 | //ctx should be retrieved from the Method. 66 | Member member = ip.getMember(); 67 | if (member instanceof Method) { 68 | Method method = (Method) member; 69 | ctx = method.getAnnotation(PersistenceContext.class); 70 | } 71 | } 72 | 73 | LOGGER.debug("PersistenceContext info:"); 74 | //This could happen if the application injects the EntityManager via @Inject instead of @PersistenceContext 75 | if(ctx != null) { 76 | LOGGER.debug("Unit name: {}", ctx.unitName()); 77 | } 78 | 79 | LOGGER.debug("Bean defining the injection point: {}", ip.getBean().getBeanClass()); 80 | LOGGER.debug("Field to be injected: {}", ip.getMember()); 81 | 82 | if (em == null) { 83 | em = emf.createEntityManager(); 84 | } 85 | return em; 86 | } 87 | 88 | /** 89 | * Closes the entity manager and entity manager factory when the event {@link CdiContainerShutdown} is fired. 90 | * 91 | * @param containerShutdown 92 | * the event that indicates that the container is about to shutdown. 93 | */ 94 | public void closeEntityManagerAndEntityManagerFactory(@Observes CdiContainerShutdown containerShutdown) { 95 | closeEntityManager(); 96 | closeEntityManagerFactory(); 97 | } 98 | 99 | private void closeEntityManager() { 100 | if (em == null) { 101 | return; 102 | } 103 | if (em.isOpen()) { 104 | try { 105 | // In case a transaction is still open. 106 | if (em.getTransaction().isActive() && !em.getTransaction().getRollbackOnly()) { 107 | em.getTransaction().commit(); 108 | } 109 | } finally { 110 | LOGGER.debug("Closing entity manager"); 111 | em.close(); 112 | } 113 | 114 | } 115 | } 116 | 117 | private void closeEntityManagerFactory() { 118 | if (emf == null) { 119 | return; 120 | } 121 | if (emf.isOpen()) { 122 | LOGGER.debug("Closing entity manager factory"); 123 | emf.close(); 124 | } 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/info/novatec/beantest/extension/InjectionHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Bean Testing. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package info.novatec.beantest.extension; 17 | 18 | import java.lang.annotation.Annotation; 19 | import java.util.Collections; 20 | import java.util.HashSet; 21 | import java.util.Set; 22 | import javax.annotation.Resource; 23 | import javax.ejb.EJB; 24 | import javax.enterprise.inject.spi.AnnotatedField; 25 | import javax.enterprise.inject.spi.AnnotatedMember; 26 | import javax.enterprise.inject.spi.AnnotatedMethod; 27 | import javax.enterprise.inject.spi.AnnotatedType; 28 | import javax.inject.Inject; 29 | import javax.persistence.PersistenceContext; 30 | import org.apache.deltaspike.core.util.metadata.builder.AnnotatedTypeBuilder; 31 | 32 | /** 33 | * This class provides general convenience methods for injection and validation. 34 | * 35 | * @author Carlos Barragan (carlos.barragan@novatec-gmbh.de) 36 | */ 37 | public final class InjectionHelper { 38 | 39 | private static final Set> JAVA_EE_ANNOTATIONS = createJavaEEAnnotationSet(); 40 | 41 | private static Set> createJavaEEAnnotationSet() { 42 | Set> javaEEAnnotations = new HashSet>(); 43 | javaEEAnnotations.add(Resource.class); 44 | javaEEAnnotations.add(EJB.class); 45 | javaEEAnnotations.add(PersistenceContext.class); 46 | return Collections.unmodifiableSet(javaEEAnnotations); 47 | } 48 | 49 | private InjectionHelper() { 50 | // Empty on purpose. 51 | } 52 | 53 | /** 54 | * Returns true if the member is NOT annotated with {@link Inject} and is annotated with one of the following annotations: 55 | *

60 | * Otherwise, it returns false. 61 | * 62 | * @param 63 | * the type of the annotated member 64 | * @param member 65 | * the annotated member whose annotations should be verified. 66 | * @return true if the member is NOT annotated with {@link Inject} and is annotated with {@link EJB}, 67 | * {@link PersistenceContext} or {@link Resource} 68 | */ 69 | public static boolean shouldInjectionAnnotationBeAddedToMember(AnnotatedMember member) { 70 | return !member.isAnnotationPresent(Inject.class) && hasJavaEEAnnotations(member); 71 | } 72 | 73 | /** 74 | * Returns true if at least one of the following Java EE annotations is present in the given member: 75 | * 80 | * Otherwise, it returns false. 81 | * @param the type of the annotated member. 82 | * @param member the member whose annotations should be verified. 83 | * @return true if the member is at least annotated with one of the following annotations: {@link EJB}, {@link PersistenceContext} or {@link Resource}. 84 | */ 85 | private static boolean hasJavaEEAnnotations(AnnotatedMember member) { 86 | for(Class javaEEannotation : JAVA_EE_ANNOTATIONS) { 87 | if (member.isAnnotationPresent(javaEEannotation)) { 88 | return true; 89 | } 90 | } 91 | return false; 92 | } 93 | 94 | /** 95 | * Adds the {@link Inject} annotation to the fields and setters of the annotated type if required. 96 | * 97 | * @param 98 | * the type of the annotated type 99 | * @param annotatedType 100 | * the annotated type whose fields and setters the inject annotation should be added to 101 | * @param builder 102 | * the builder that should be used to add the annotation. 103 | * @see #shouldInjectionAnnotationBeAddedToMember(AnnotatedMember) 104 | */ 105 | public static void addInjectAnnotation(final AnnotatedType annotatedType, AnnotatedTypeBuilder builder) { 106 | for (AnnotatedField field : annotatedType.getFields()) { 107 | if (shouldInjectionAnnotationBeAddedToMember(field)) { 108 | builder.addToField(field, AnnotationInstances.INJECT); 109 | } 110 | } 111 | for (AnnotatedMethod method : annotatedType.getMethods()) { 112 | if (shouldInjectionAnnotationBeAddedToMember(method)) { 113 | builder.addToMethod(method, AnnotationInstances.INJECT); 114 | } 115 | } 116 | } 117 | 118 | 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/info/novatec/beantest/extension/BeanTestExtension.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Bean Testing. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package info.novatec.beantest.extension; 17 | 18 | import info.novatec.beantest.transactions.Transactional; 19 | import javax.ejb.EJB; 20 | import javax.ejb.MessageDriven; 21 | import javax.ejb.Singleton; 22 | import javax.ejb.Stateless; 23 | import javax.enterprise.context.ApplicationScoped; 24 | import javax.enterprise.context.RequestScoped; 25 | import javax.enterprise.event.Observes; 26 | import javax.enterprise.inject.spi.AnnotatedType; 27 | import javax.enterprise.inject.spi.Extension; 28 | import javax.enterprise.inject.spi.ProcessAnnotatedType; 29 | import javax.enterprise.inject.spi.WithAnnotations; 30 | import javax.inject.Inject; 31 | import javax.interceptor.Interceptor; 32 | import org.apache.deltaspike.core.util.metadata.builder.AnnotatedTypeBuilder; 33 | 34 | /** 35 | * Extension to modify bean meta data. 36 | *

37 | * This extension adds and changes the bean meta data in order to convert EJB injection points into CDI injection points. 38 | * Therefore the extension changes the meta data of Beans annotated with {@link EJB}
39 | * It also changes injection points in interceptors. 40 | * 41 | * @author Carlos Barragan (carlos.barragan@novatec-gmbh.de) 42 | */ 43 | public class BeanTestExtension implements Extension { 44 | 45 | 46 | 47 | /** 48 | * Replaces the meta data of the {@link ProcessAnnotatedType}. 49 | * 50 | *

51 | * The ProcessAnnotatedType's meta data will be replaced, if the annotated type has one of the following annotations: 52 | *

    53 | *
  • {@link Stateless} 54 | *
  • {@link MessageDriven} 55 | *
  • {@link Interceptor} 56 | *
  • {@link Singleton} 57 | *
58 | * 59 | * @param the type of the ProcessAnnotatedType 60 | * @param pat the annotated type representing the class being processed 61 | */ 62 | public void processInjectionTarget(@Observes @WithAnnotations({Stateless.class, MessageDriven.class, Interceptor.class, Singleton.class}) ProcessAnnotatedType pat) { 63 | if (pat.getAnnotatedType().isAnnotationPresent(Stateless.class) || pat.getAnnotatedType().isAnnotationPresent(MessageDriven.class)) { 64 | modifiyAnnotatedTypeMetadata(pat); 65 | } else if (pat.getAnnotatedType().isAnnotationPresent(Interceptor.class)) { 66 | processInterceptorDependencies(pat); 67 | } else if(pat.getAnnotatedType().isAnnotationPresent(Singleton.class)) { 68 | addApplicationScopedAndTransactionalToSingleton(pat); 69 | } 70 | } 71 | 72 | /** 73 | * Adds {@link Transactional} and {@link ApplicationScoped} to the given annotated type and converts 74 | * its EJB injection points into CDI injection points (i.e. it adds the {@link Inject}) 75 | * @param the type of the annotated type. 76 | * @param pat the process annotated type. 77 | */ 78 | private void addApplicationScopedAndTransactionalToSingleton(ProcessAnnotatedType pat) { 79 | AnnotatedType at = pat.getAnnotatedType(); 80 | 81 | AnnotatedTypeBuilder builder = new AnnotatedTypeBuilder().readFromType(at); 82 | 83 | builder.addToClass(AnnotationInstances.APPLICATION_SCOPED).addToClass(AnnotationInstances.TRANSACTIONAL); 84 | 85 | InjectionHelper.addInjectAnnotation(at, builder); 86 | 87 | pat.setAnnotatedType(builder.create()); 88 | } 89 | 90 | /** 91 | * Adds {@link Transactional} and {@link RequestScoped} to the given annotated type and converts 92 | * its EJB injection points into CDI injection points (i.e. it adds the {@link Inject}) 93 | * @param the type of the annotated type 94 | * @param pat the process annotated type. 95 | */ 96 | private void modifiyAnnotatedTypeMetadata(ProcessAnnotatedType pat) { 97 | AnnotatedType at = pat.getAnnotatedType(); 98 | 99 | AnnotatedTypeBuilder builder = new AnnotatedTypeBuilder().readFromType(at); 100 | builder.addToClass(AnnotationInstances.TRANSACTIONAL).addToClass(AnnotationInstances.REQUEST_SCOPED); 101 | 102 | InjectionHelper.addInjectAnnotation(at, builder); 103 | //Set the wrapper instead the actual annotated type 104 | pat.setAnnotatedType(builder.create()); 105 | 106 | } 107 | 108 | /** 109 | * Adds {@link Inject} annotation to all the dependencies of the interceptor. 110 | * 111 | * @param 112 | * the type of the annotated type 113 | * @param pat 114 | * the process annotated type. 115 | */ 116 | private void processInterceptorDependencies(ProcessAnnotatedType pat) { 117 | AnnotatedTypeBuilder builder = new AnnotatedTypeBuilder().readFromType(pat.getAnnotatedType()); 118 | InjectionHelper.addInjectAnnotation(pat.getAnnotatedType(), builder); 119 | pat.setAnnotatedType(builder.create()); 120 | } 121 | 122 | 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/info/novatec/beantest/transactions/TransactionalInterceptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Bean Testing. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package info.novatec.beantest.transactions; 17 | 18 | import java.util.Arrays; 19 | import java.util.HashSet; 20 | import java.util.Set; 21 | import javax.inject.Inject; 22 | import javax.interceptor.AroundInvoke; 23 | import javax.interceptor.Interceptor; 24 | import javax.interceptor.InvocationContext; 25 | import javax.persistence.EntityManager; 26 | import javax.persistence.EntityTransaction; 27 | import javax.persistence.LockTimeoutException; 28 | import javax.persistence.NoResultException; 29 | import javax.persistence.NonUniqueResultException; 30 | import javax.persistence.PersistenceContext; 31 | import javax.persistence.QueryTimeoutException; 32 | import org.slf4j.Logger; 33 | import org.slf4j.LoggerFactory; 34 | 35 | /** 36 | * Transactional interceptor to provide basic transaction propagation. 37 | *

38 | * Note This implementation is intentionally not thread-safe, because unit tests are usually run in one thread.
39 | * If you try to run unit tests in parallel, unexpected behavior may occur. 40 | *

41 | * Alternatively the Apache Deltaspike JPA module can be used. The JPA module provides more advanced transaction handling. 42 | * However, this implementation should suffice for testing purposes. 43 | * 44 | * @author Carlos Barragan (carlos.barragan@novatec-gmbh.de) 45 | */ 46 | @Interceptor 47 | @Transactional 48 | public class TransactionalInterceptor { 49 | 50 | /** 51 | * Exceptions that should not cause the transaction to rollback according to Java EE Documentation. 52 | * (http://docs.oracle.com/javaee/6/api/javax/persistence/PersistenceException.html) 53 | */ 54 | private static final Set> NO_ROLLBACK_EXCEPTIONS=new HashSet>(Arrays.asList( 55 | NonUniqueResultException.class, 56 | NoResultException.class, 57 | QueryTimeoutException.class, 58 | LockTimeoutException.class)); 59 | 60 | 61 | @Inject 62 | @PersistenceContext 63 | EntityManager em; 64 | 65 | private static final Logger LOGGER = LoggerFactory.getLogger(TransactionalInterceptor.class); 66 | 67 | private static int INTERCEPTOR_COUNTER = 0; 68 | 69 | @AroundInvoke 70 | public Object manageTransaction(InvocationContext ctx) throws Exception { 71 | 72 | EntityTransaction transaction = em.getTransaction(); 73 | if (!transaction.isActive()) { 74 | transaction.begin(); 75 | LOGGER.debug("Transaction started"); 76 | } 77 | 78 | INTERCEPTOR_COUNTER++; 79 | Object result = null; 80 | try { 81 | result = ctx.proceed(); 82 | 83 | } catch (Exception e) { 84 | if (isFirstInterceptor()) { 85 | markRollbackTransaction(e); 86 | } 87 | throw e; 88 | } finally { 89 | processTransaction(); 90 | } 91 | 92 | return result; 93 | } 94 | 95 | 96 | 97 | /** 98 | * Commits the current transaction if it is not already marked as rollback via the {@link EntityTransaction#getRollbackOnly()} method. 99 | * In that case, a rollback will be executed. 100 | */ 101 | private void processTransaction() throws Exception { 102 | EntityTransaction transaction = em.getTransaction(); 103 | try { 104 | 105 | if (em.isOpen() && transaction.isActive() && isFirstInterceptor()) { 106 | if (transaction.getRollbackOnly()) { 107 | transaction.rollback(); 108 | LOGGER.debug("Transaction was rollbacked"); 109 | } else { 110 | transaction.commit(); 111 | LOGGER.debug("Transaction committed"); 112 | } 113 | em.clear(); 114 | } 115 | } catch (Exception e) { 116 | LOGGER.warn("Error when trying to commit transaction: {0}", e); 117 | throw e; 118 | } finally { 119 | INTERCEPTOR_COUNTER--; 120 | } 121 | 122 | } 123 | 124 | /** 125 | * Marks the transaction for rollback via {@link EntityTransaction#setRollbackOnly()}. 126 | */ 127 | private void markRollbackTransaction(Exception exception) throws Exception { 128 | try { 129 | if (em.isOpen() && em.getTransaction().isActive() && shouldExceptionCauseRollback(exception)) { 130 | em.getTransaction().setRollbackOnly(); 131 | } 132 | } catch (Exception e) { 133 | LOGGER.warn("Error when trying to roll back the transaction: {0}", e); 134 | throw e; 135 | } 136 | 137 | } 138 | 139 | private static boolean isFirstInterceptor() { 140 | return INTERCEPTOR_COUNTER -1 == 0; 141 | } 142 | 143 | private static boolean shouldExceptionCauseRollback(Exception e ) { 144 | return ! NO_ROLLBACK_EXCEPTIONS.contains(e.getClass()); 145 | } 146 | 147 | } 148 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | info.novatec 5 | bean-test 6 | 0.3-SNAPSHOT 7 | Bean Testing 8 | Java EE Bean Testing framework 9 | http://blog.novatec-gmbh.de/unit-testing-jee-applications-cdi/ 10 | jar 11 | 12 | 13 | UTF-8 14 | 1.2.1 15 | 16 | 17 | 18 | 19 | org.slf4j 20 | slf4j-api 21 | 1.7.5 22 | jar 23 | 24 | 25 | org.hibernate 26 | hibernate-core 27 | 4.2.8.Final 28 | provided 29 | 30 | 31 | org.hibernate 32 | hibernate-entitymanager 33 | 4.2.8.Final 34 | provided 35 | 36 | 37 | org.apache.geronimo.specs 38 | geronimo-ejb_3.1_spec 39 | 1.0 40 | 41 | 42 | org.apache.geronimo.specs 43 | geronimo-jpa_2.0_spec 44 | 1.0 45 | 46 | 47 | org.jboss.weld.se 48 | weld-se 49 | 2.1.2.Final 50 | jar 51 | 52 | 53 | 54 | org.hamcrest 55 | hamcrest-all 56 | 1.3 57 | test 58 | 59 | 60 | 61 | junit 62 | junit 63 | 4.11 64 | 65 | 66 | 67 | org.apache.derby 68 | derby 69 | 10.10.1.1 70 | test 71 | 72 | 73 | org.apache.deltaspike.core 74 | deltaspike-core-impl 75 | ${deltaspike.version} 76 | 77 | 78 | org.apache.deltaspike.cdictrl 79 | deltaspike-cdictrl-weld 80 | ${deltaspike.version} 81 | 82 | 83 | org.apache.deltaspike.modules 84 | deltaspike-test-control-module-api 85 | ${deltaspike.version} 86 | test 87 | 88 | 89 | org.apache.deltaspike.modules 90 | deltaspike-test-control-module-impl 91 | ${deltaspike.version} 92 | test 93 | 94 | 95 | org.mockito 96 | mockito-all 97 | 1.8.4 98 | test 99 | 100 | 101 | 102 | 103 | https://github.com/NovaTecConsulting/BeanTest.git 104 | 105 | 106 | 107 | 108 | 109 | 110 | org.apache.maven.plugins 111 | maven-compiler-plugin 112 | 2.3.2 113 | 114 | 1.6 115 | 1.6 116 | 117 | 118 | 119 | org.apache.maven.plugins 120 | maven-source-plugin 121 | 2.2.1 122 | 123 | 124 | attach-sources 125 | 126 | jar 127 | 128 | 129 | 130 | 131 | 132 | org.apache.maven.plugins 133 | maven-javadoc-plugin 134 | 2.9.1 135 | 136 | 137 | attach-javadocs 138 | 139 | jar 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | The Apache Software License, Version 2.0 150 | http://www.apache.org/licenses/LICENSE-2.0.txt 151 | manual 152 | 153 | 154 | 155 | 156 | github 157 | GitHub Packages 158 | https://maven.pkg.github.com/novatec-gmbh/bean-test 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, and 10 | distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 13 | owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities 16 | that control, are controlled by, or are under common control with that entity. 17 | For the purposes of this definition, "control" means (i) the power, direct or 18 | indirect, to cause the direction or management of such entity, whether by 19 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the 20 | outstanding shares, or (iii) beneficial ownership of such entity. 21 | 22 | "You" (or "Your") shall mean an individual or Legal Entity exercising 23 | permissions granted by this License. 24 | 25 | "Source" form shall mean the preferred form for making modifications, including 26 | but not limited to software source code, documentation source, and configuration 27 | files. 28 | 29 | "Object" form shall mean any form resulting from mechanical transformation or 30 | translation of a Source form, including but not limited to compiled object code, 31 | generated documentation, and conversions to other media types. 32 | 33 | "Work" shall mean the work of authorship, whether in Source or Object form, made 34 | available under the License, as indicated by a copyright notice that is included 35 | in or attached to the work (an example is provided in the Appendix below). 36 | 37 | "Derivative Works" shall mean any work, whether in Source or Object form, that 38 | is based on (or derived from) the Work and for which the editorial revisions, 39 | annotations, elaborations, or other modifications represent, as a whole, an 40 | original work of authorship. For the purposes of this License, Derivative Works 41 | shall not include works that remain separable from, or merely link (or bind by 42 | name) to the interfaces of, the Work and Derivative Works thereof. 43 | 44 | "Contribution" shall mean any work of authorship, including the original version 45 | of the Work and any modifications or additions to that Work or Derivative Works 46 | thereof, that is intentionally submitted to Licensor for inclusion in the Work 47 | by the copyright owner or by an individual or Legal Entity authorized to submit 48 | on behalf of the copyright owner. For the purposes of this definition, 49 | "submitted" means any form of electronic, verbal, or written communication sent 50 | to the Licensor or its representatives, including but not limited to 51 | communication on electronic mailing lists, source code control systems, and 52 | issue tracking systems that are managed by, or on behalf of, the Licensor for 53 | the purpose of discussing and improving the Work, but excluding communication 54 | that is conspicuously marked or otherwise designated in writing by the copyright 55 | owner as "Not a Contribution." 56 | 57 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 58 | of whom a Contribution has been received by Licensor and subsequently 59 | incorporated within the Work. 60 | 61 | 2. Grant of Copyright License. 62 | 63 | Subject to the terms and conditions of this License, each Contributor hereby 64 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 65 | irrevocable copyright license to reproduce, prepare Derivative Works of, 66 | publicly display, publicly perform, sublicense, and distribute the Work and such 67 | Derivative Works in Source or Object form. 68 | 69 | 3. Grant of Patent License. 70 | 71 | Subject to the terms and conditions of this License, each Contributor hereby 72 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 73 | irrevocable (except as stated in this section) patent license to make, have 74 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where 75 | such license applies only to those patent claims licensable by such Contributor 76 | that are necessarily infringed by their Contribution(s) alone or by combination 77 | of their Contribution(s) with the Work to which such Contribution(s) was 78 | submitted. If You institute patent litigation against any entity (including a 79 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 80 | Contribution incorporated within the Work constitutes direct or contributory 81 | patent infringement, then any patent licenses granted to You under this License 82 | for that Work shall terminate as of the date such litigation is filed. 83 | 84 | 4. Redistribution. 85 | 86 | You may reproduce and distribute copies of the Work or Derivative Works thereof 87 | in any medium, with or without modifications, and in Source or Object form, 88 | provided that You meet the following conditions: 89 | 90 | You must give any other recipients of the Work or Derivative Works a copy of 91 | this License; and 92 | You must cause any modified files to carry prominent notices stating that You 93 | changed the files; and 94 | You must retain, in the Source form of any Derivative Works that You distribute, 95 | all copyright, patent, trademark, and attribution notices from the Source form 96 | of the Work, excluding those notices that do not pertain to any part of the 97 | Derivative Works; and 98 | If the Work includes a "NOTICE" text file as part of its distribution, then any 99 | Derivative Works that You distribute must include a readable copy of the 100 | attribution notices contained within such NOTICE file, excluding those notices 101 | that do not pertain to any part of the Derivative Works, in at least one of the 102 | following places: within a NOTICE text file distributed as part of the 103 | Derivative Works; within the Source form or documentation, if provided along 104 | with the Derivative Works; or, within a display generated by the Derivative 105 | Works, if and wherever such third-party notices normally appear. The contents of 106 | the NOTICE file are for informational purposes only and do not modify the 107 | License. You may add Your own attribution notices within Derivative Works that 108 | You distribute, alongside or as an addendum to the NOTICE text from the Work, 109 | provided that such additional attribution notices cannot be construed as 110 | modifying the License. 111 | You may add Your own copyright statement to Your modifications and may provide 112 | additional or different license terms and conditions for use, reproduction, or 113 | distribution of Your modifications, or for any such Derivative Works as a whole, 114 | provided Your use, reproduction, and distribution of the Work otherwise complies 115 | with the conditions stated in this License. 116 | 117 | 5. Submission of Contributions. 118 | 119 | Unless You explicitly state otherwise, any Contribution intentionally submitted 120 | for inclusion in the Work by You to the Licensor shall be under the terms and 121 | conditions of this License, without any additional terms or conditions. 122 | Notwithstanding the above, nothing herein shall supersede or modify the terms of 123 | any separate license agreement you may have executed with Licensor regarding 124 | such Contributions. 125 | 126 | 6. Trademarks. 127 | 128 | This License does not grant permission to use the trade names, trademarks, 129 | service marks, or product names of the Licensor, except as required for 130 | reasonable and customary use in describing the origin of the Work and 131 | reproducing the content of the NOTICE file. 132 | 133 | 7. Disclaimer of Warranty. 134 | 135 | Unless required by applicable law or agreed to in writing, Licensor provides the 136 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, 137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, 138 | including, without limitation, any warranties or conditions of TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are 140 | solely responsible for determining the appropriateness of using or 141 | redistributing the Work and assume any risks associated with Your exercise of 142 | permissions under this License. 143 | 144 | 8. Limitation of Liability. 145 | 146 | In no event and under no legal theory, whether in tort (including negligence), 147 | contract, or otherwise, unless required by applicable law (such as deliberate 148 | and grossly negligent acts) or agreed to in writing, shall any Contributor be 149 | liable to You for damages, including any direct, indirect, special, incidental, 150 | or consequential damages of any character arising as a result of this License or 151 | out of the use or inability to use the Work (including but not limited to 152 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or 153 | any and all other commercial damages or losses), even if such Contributor has 154 | been advised of the possibility of such damages. 155 | 156 | 9. Accepting Warranty or Additional Liability. 157 | 158 | While redistributing the Work or Derivative Works thereof, You may choose to 159 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or 160 | other liability obligations and/or rights consistent with this License. However, 161 | in accepting such obligations, You may act only on Your own behalf and on Your 162 | sole responsibility, not on behalf of any other Contributor, and only if You 163 | agree to indemnify, defend, and hold each Contributor harmless for any liability 164 | incurred by, or claims asserted against, such Contributor by reason of your 165 | accepting any such warranty or additional liability. 166 | 167 | END OF TERMS AND CONDITIONS 168 | 169 | APPENDIX: How to apply the Apache License to your work 170 | 171 | To apply the Apache License to your work, attach the following boilerplate 172 | notice, with the fields enclosed by brackets "[]" replaced with your own 173 | identifying information. (Don't include the brackets!) The text should be 174 | enclosed in the appropriate comment syntax for the file format. We also 175 | recommend that a file or class name and description of purpose be included on 176 | the same "printed page" as the copyright notice for easier identification within 177 | third-party archives. 178 | 179 | Copyright [yyyy] [name of copyright owner] 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. --------------------------------------------------------------------------------