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
Related
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.
I am using Play Framework and using Java as the language of choice. I have a Controller which makes a REST call to an external service. I intend to mock the external service, so that I can test the functionality of my controller. To achieve this, I have created my test cases as shown below (sample). I am embedding a server within my test to mock the external service.
public class SomeControllerTest extends WithApplication {
private static Server SERVER;
#Override
protected Application provideApplication() {
final Module testModule = new AbstractModule() {
#Override
public void configure() {
bind(AppDao.class).to(MockAppDaoImpl.class);
}
};
return new GuiceApplicationBuilder().in(Environment.simple()).overrides(testModule).build();
}
#BeforeClass
public static void setup() {
Router router = new RoutingDsl()
.POST("/api/users")
.routeTo(() -> created())
.build();
SERVER = Server.forRouter(router, 33373);
PORT = SERVER.httpPort();
}
#AfterClass
public static void tearDown() {
SERVER.stop();
}
#Test
public void testCreateUser() {
ObjectNode obj = Json.newObject();
obj.put("name", "John Doe");
obj.put("email", "john.doe#example.com");
Http.RequestBuilder request = new Http.RequestBuilder()
.method(POST)
.bodyJson(obj)
.uri("/some/url/here");
Result result = route(request);
assertEquals(ERR_MSG_STATUS_CODE, CREATED, result.status());
assertEquals(ERR_MSG_CONTENT_TYPE, Http.MimeTypes.JSON, result.contentType().get());
}
My expectation is that when I run the test, the mock server would run and based on my application's test configuration, my controller will make a call to the mock server which would return 201 and my test case would pass.
But, this doesn't happen, because as soon as setup() method completes, the mock server is killed, and my controller cannot make a call to it.
What am I doing wrong here?
Testing of controller should be rather done by inheritance from WithApplication
public class TestController extends WithApplication {
#Test
public void testSomething() {
Helpers.running(Helpers.fakeApplication(), () -> {
// put test stuff
// put asserts
});
}
}
In order to test a controller method use Helpers.fakeRequest and reverse routing.
The external service may be just mocked with mockito or other mocking framework you like.
You can find here several examples.
I upgraded to camel 2.16 and one of my route Unit Tests started failing.
Here is my route definition:
public class Route extends RouteBuilder{
#Override
public void configure() throws Exception {
from(start).enrich("second");
from("direct:second")
.log(LoggingLevel.DEBUG, "foo", "Route [direct:second] started.");
}
}
Here is my test:
#RunWith(MockitoJUnitRunner.class)
public class RouteTest extends CamelTestSupport {
private Route builder;
#Produce(uri = "direct:start")
protected ProducerTemplate template;
#Before
public void config() {
BasicConfigurator.configure();
}
#Override
protected RouteBuilder createRouteBuilder() {
builder = new Route();
return builder;
}
#Override
protected CamelContext createCamelContext() throws Exception {
SimpleRegistry registry = new SimpleRegistry();
return new DefaultCamelContext(registry);
}
#Test
public void testPrimeRouteForSubscriptionId() {
Exchange exchange = ExchangeBuilder.anExchange(new DefaultCamelContext()).build();
exchange.getIn().setBody(new String("test"));
template.send(exchange);
}
}
The error I'm getting when I run the test is:
org.apache.camel.component.direct.DirectConsumerNotAvailableException: No consumers available on endpoint: Endpoint[direct://second]. Exchange[][Message: test]
Worthy of note is the following line in the camel 2.16 notes:
http://camel.apache.org/camel-2160-release.html
The resourceUri and resourceRef attributes on and has been removed as they now support a dynamic uris computed from an Expression.
Thanks in advance for any help.
Swap the order so the the direct route is started before the enrich.
http://camel.apache.org/configuring-route-startup-ordering-and-autostartup.html
Or use seda instead of direct in your unit test: http://camel.apache.org/seda
Or use ?block=true in the direct uri to tell Camel to block and wait for a consumer to be started and ready before it sends a message to it: http://camel.apache.org/direct
This is a somewhat old issue, but since i pulled out most of my hair out last night, trying to figure out why it was ok to use to("direct:myEndpoint") but not enrich("direct:myEndpoint"), I'll post the answer anyway - maybe it'll save somebody else from getting bald spots ;-)
It turns out to be a test-issue. In case of Direct endpoints, enrich checks whether there is a running route in the context before passing the Exchange to it, but it does so by looking at the CamelContext held by the Exchange it is currently handling. Since you passed your ProducerTemplate an Exchange what was created with a new DefaultCamelContext(), it has no "direct:second" route available.
Luckily there is a couple of simple solutions. Either create the Exchange using the CamelContext from CamelTestSupport, or use the ProducerTemplate sendBody(...) method instead:
#Test
public void testWithSendBody() {
template.sendBody(new String("test"));
}
#Test
public void testPrimeRouteForSubscriptionId() {
Exchange exchange = ExchangeBuilder.anExchange(context()).build();
exchange.getIn().setBody(new String("test"));
template.send(exchange);
}
The blueprint test keeps throwing exception, No Consumers available.
My scenario was that I have an osgi svc which exposes a method which can be called from any another osgi svc.
So the exposed svc method makes a call to a direct:
#EndpointInject(uri = "direct-vm:toRestCall")
ProducerTemplate toRestCall;
svcMethod(Exchange xch){
exchange.setOut(
toRestCall.send("seda:toDirectCall", xch -> {
try{
xch.getIn().setBody("abc");
}catch (Exception ex){
ex.getMessage();
}
}
}).getIn());
And when I tested the direct that it calls, Blueprint advice with JUnit used to keep throwing the following exception:
org.apache.camel.component.direct.DirectConsumerNotAvailableException:
No consumers available on endpoint: Endpoint. Exchange[Message: {..........
I'm developing a web app which is based in Spring 2.5 and hibernate 3. Recently I've introduced JUnit tests and I've done some integration tests using DBUnit framework. DBUnit is supposed to update the database with an xml dataset between one test and another, and it's working well, as I've seen.
However, when I update an element in a test, hibernate seems to catch this information and even I load the element in the following test, the information is the one I've modified. If I look the DB when the execution is paused, the Data Base is properly reseted by DBUnit. So I think it can be an Hibernate problem..
Is there a way to make a tearDown between tests saying I want a new hibernate session for my spring context? By the way, I'm not using Spring annotations and I get the Spring context by code:
String[] contextLocations = new String[2];
contextLocations[0] = "WebContent/WEB-INF/applicationContext.xml";
contextLocations[1] = "src/System_V3/test/applicationContext.xml";
context = new FileSystemXmlApplicationContext(contextLocations);
DBUnit setUp:
#Before
public void setUpBeforeClass() throws Exception {
handleSetUpOperation();
}
private static void handleSetUpOperation() throws Exception {
conn = getConnection();
conn.getConnection().setAutoCommit(false);
final IDataSet data = getDataSet();
try {
DatabaseOperation.REFRESH.execute(conn, data);
} finally {
conn.close();
}
}
private static IDatabaseConnection getConnection() throws ClassNotFoundException, SQLException,
DatabaseUnitException {
Class.forName("org.gjt.mm.mysql.Driver");
return new DatabaseConnection(DriverManager.getConnection(
"jdbc:mysql://localhost:3306/web_database", "root", "pass"));
}
private static IDataSet getDataSet() throws IOException, DataSetException {
ClassLoader classLoader = TestPrueba.class.getClassLoader();
return new FlatXmlDataSetBuilder().build(classLoader
.getResourceAsStream("System_V3/test/dataset.xml"));
}
Tests are done in JUnit 4 using only #Test annotations and test class is not extending any library class.
Any suggestion?
Not sure if this is something that can help you - but just in case...
Try to use session.clear() and use it in teardown method.
Please take a look here http://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/Session.html#clear()
According to spec session.clear() ->
Completely clear the session. Evict all loaded instances and cancel all pending saves, updates and deletions. Do not close open iterators or instances of ScrollableResults.
You need to execute your tests within a transaction. This can be achieved by setting the SpringJUnit4ClassRunner for your test. After this is configured you can use #Transactional annotation per test.
With this approach you can #Autowired your beans directly to your test too.
For instance:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:context-file.xml" })
public class MyTest {
#Autowired
private MyService myService;
#Transactional
#Test
private void myFirstTest() {
...
myService.executeSomething();
...
}
}
and of course, you can set the default behaviour to RollBack on your test class annotating it with #TransactionConfiguration(defaultRollback = true/false)
Essence:
How can I auto-rollback my hibernate transaction in a JUnit Test run with JBehave?
The problem seems to be that JBehave wants the SpringAnnotatedEmbedderRunner but annotating a test as #Transactional requires the SpringJUnit4ClassRunner.
I've tried to find some documentation on how to implement either rollback with SpringAnnotatedEmbedderRunner or to make JBehave work using the SpringJUnit4ClassRunner but I couldn't get either to work.
Does anyone have a (preferably simple) setup that runs JBehave storries with Spring and Hibernate and transaction auto-rollback?
Further infos about my setup so far:
Working JBehave with Spring - but not with auto-rollback:
#RunWith(SpringAnnotatedEmbedderRunner.class)
#Configure(parameterConverters = ParameterConverters.EnumConverter.class)
#UsingEmbedder(embedder = Embedder.class, generateViewAfterStories = true, ignoreFailureInStories = false, ignoreFailureInView = false)
#UsingSpring(resources = { "file:src/main/webapp/WEB-INF/test-context.xml" })
#UsingSteps
#Transactional // << won't work
#TransactionConfiguration(...) // << won't work
// both require the SpringJUnit4ClassRunner
public class DwStoryTests extends JUnitStories {
protected List<String> storyPaths() {
String searchInDirectory = CodeLocations.codeLocationFromPath("src/test/resources").getFile();
return new StoryFinder().findPaths(searchInDirectory, Arrays.asList("**/*.story"), null);
}
}
In my test steps I can #Inject everything nicely:
#Component
#Transactional // << won't work
public class PersonServiceSteps extends AbstractSmockServerTest {
#Inject
private DatabaseSetupHelper databaseSetupHelper;
#Inject
private PersonProvider personProvider;
#Given("a database in default state")
public void setupDatabase() throws SecurityException {
databaseSetupHelper.createTypes();
databaseSetupHelper.createPermission();
}
#When("the service $service is called with message $message")
public void callServiceWithMessage(String service, String message) {
sendRequestTo("/personService", withMessage("requestPersonSave.xml")).andExpect(noFault());
}
#Then("there should be a new person in the database")
public void assertNewPersonInDatabase() {
Assert.assertEquals("Service did not save person: ", personProvider.count(), 1);
}
(yes, the databaseSetupHelper methods are all transactional)
PersonProvider is basicly a wrapper around org.springframework.data.jpa.repository.support.SimpleJpaRepository. So there is access to the entityManager but taking control over the transactions (with begin/rollback) didn't work, I guess because of all the #Transactionals that are done under the hood inside that helper class.
Also I read that JBehave runs in a different context?session?something? which causes loss of controll over the transaction started by the test? Pretty confusing stuff..
edit:
Editet the above rephrasing the post to reflect my current knowledge and shortening the whole thing so that the question becomes more obvious and the setup less obstrusive.
I think you can skip the SpringAnnotatedEmbedderRunner and provide the necessary configuration to JBehave yourself. For example instead of
#UsingEmbedder(embedder = Embedder.class, generateViewAfterStories = true, ignoreFailureInStories = false, ignoreFailureInView = false)
you can do
configuredEmbedder()
.embedderControls()
.doGenerateViewAfterStories(true)
.doIgnoreFailureInStories(false)
.doIgnoreFailureInView(false);
Besides: why do you want to rollback the transaction? Typically you are using JBehave for acceptance tests, which run in a production-like environment. For example you first setup some data in the database, access it via Browser/Selenium and check for the results. For that to work the DB transaction has to be committed. Do you need to clean-up manually after your tests, which you can do in #AfterStories or #AfterScenario annotated methods.
I made it work by controlling transaction scope manually, rolling it back after each scenario. Just follow the official guide how to use Spring with JBehave and then do the trick as shown below.
#Component
public class MySteps
{
#Autowired
MyDao myDao;
#Autowired
PlatformTransactionManager transactionManager;
TransactionStatus transaction;
#BeforeScenario
public void beforeScenario() {
transaction = transactionManager.getTransaction(new DefaultTransactionDefinition());
}
#AfterScenario
public void afterScenario() {
if (transaction != null)
transactionManager.rollback(transaction);
}
#Given("...")
public void persistSomething() {
myDao.persist(new Foo());
}
}
I'm not familiar with JBehave, but it appears you're searching for this annotation.
#TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true).
You could also set defaultRollback to true in your testContext.