Autowire failed in cucumber Step Def test - spring

I have a series cucumber feature files and a list of associated step def tests in current project
in the step def tests package, I have this Hook definition
#ContextConfiguration(classes = Application.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class Hooks {
....
}
and RunCukesTest
#RunWith(Cucumber.class)
#CucumberOptions(features = "src/test/resources/features", glue = { "com.myapp.test.jersey.rest.v1" })
#ContextConfiguration(classes = Application.class)
public class RunCukesTest {
....
}
above classpaths are correct.
And there is one of the step def test
package com.myapp.test.jersey.rest.v1;
....
#ContextConfiguration(classes = Application.class)
public class OrderCreateServiceTest {
....
#Autowired
private OrderRepository repository;
}
However I got follow error by Spring Boot
Exception in thread "main" java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'entityManagerFactory': Requested bean is currently in creation: Is there an unresolvable circular reference?
Then if I take out the #ContextConfiguration from step def class, like this
//#ContextConfiguration(classes = Application.class)
public class CashpointCreateServiceTest {
apparently autowire of repository object will fail by throwing NullPointerException
It's greatly appreciated if anyone can share
(1) With Hook and CukeTest configuration, how to autowire bean in the step def class?
(2) Is it ok to have #ContextConfiguration in both Hook and CukeTest class?

You don't need the ContextConfiguration on RunCukesTest class. For running cucumber tests I use the following setup.
Junit test class to launch the cucumber tests:
package mypackage.test;
import org.junit.runner.RunWith;
import cucumber.api.CucumberOptions;
import cucumber.api.junit.Cucumber;
#RunWith(Cucumber.class)
#CucumberOptions(features = "src/test-integration/resources/features")
public class RunFeatures {
}
Base class which all step definition classes extend. This is so that all the step classes have the same spring annotations.
package mypackage.test.steps;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.context.ContextConfiguration;
import mypackage.Application;
#ContextConfiguration(classes = { Application.class })
#SpringBootTest(webEnvironment=WebEnvironment.DEFINED_PORT)
public class BaseSteps {
}
Then the tests steps
package mypackage.test.steps;
import mypackage.repo.SampleRepo;
import org.springframework.beans.factory.annotation.Autowired;
import cucumber.api.java.en.When;
public class SampleSteps extends BaseSteps {
#Autowired
private SampleRepo sampleRepo;
#When("^Sample repo is called$")
public void no_words_are_saved() {
sampleRepo.findAll("something");
}
I don't understand what your Hooks class is for. I've only used annotated hooks in cucumber-jvm (e.g. #Before or #After). If you need some other hook, can you explain a bit more about what you're trying to do?

Related

Could not autowire JobLauncherTestUtils

I am attempting to test a simple spring batch application.
Using the Spring Batch documentation as a guide (found here), I have created the following test class:
import org.junit.runner.RunWith;
import org.springframework.batch.test.JobLauncherTestUtils;
import org.springframework.batch.test.context.SpringBatchTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import static org.junit.jupiter.api.Assertions.assertNotNull;
#SpringBatchTest
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = BatchConfig.class)
class BatchConfigTest {
#Autowired
private JobLauncherTestUtils jobLauncherTestUtils;
#Test
void userStep() {
assertNotNull(jobLauncherTestUtils, "jobLauncherTestUtils should not be null");
}
}
According to docs #SpringBatchTest should inject the JobLaucherTestUtils bean. However, when I run the test, the assertion fails. I have also tried defining the bean in an inner configuration class and had the same result:
static class TestConfiguration {
#Autowired
#Qualifier("userJob")
private Job userJob;
#Bean
public JobLauncherTestUtils jobLauncherTestUtils() {
JobLauncherTestUtils utils = new JobLauncherTestUtils();
utils.setJob(userJob);
return utils;
}
}
Is there something I'm missing? The full source code can be found here.
I am using Spring Batch v4.2.0 and JUnit 5
You are using #RunWith(SpringRunner.class) which is for JUnit 4. You need to use #ExtendWith(SpringExtension.class) for JUnit 5 tests:
#SpringBatchTest
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = BatchConfig.class)
class BatchConfigTest {
// ...
}
I had a same problem with Spring Batch 4.1.3 and JUnit 4.12.
Replacing #SpringBatchTest with #SpringBootTest solved the problem.
I was seeing the same error in IntelliJ and spent ages looking into why before I ran the test. It was being injected just fine, IntelliJ was incorrectly telling me the bean wasn't there.
:facepalm:
Posting this in case someone faces the same issue.

SpringBoot Junit5 fails when renaming package of class holding #SpringBootApplication annotation

I'm struggling with JUnit 5 when moving the #SpringBootApplication to a different package.
I have setup a new SpringBoot-project (2.2.1.RELEASE) with Maven and Eclipse (had to upgrade this from "Eclipse Photon" to support the SpringBoot-Release
My package layout looks like this:
/src/main/java
com.package.sample.appl1
StartSamples.java
com.package.sample.appl1.start
com.package.sample.appl1.dbaccess
com.package.sample.appl1.run
com.package.sample.appl1.utils
com.package.sample.appl2.run
com.package.sample.appl2.run
/src/test/java
com.package.sample.appl1.dbaccess
SimpleTest.java
The class holding the #SpringBootApplication is:
#ComponentScan({
"com.package.sample"
})
#SpringBootApplication
public class StartSamples {
public static void main(String[] args) {
System.out.println("Start");
try {
SpringApplication.run(StartSamples.class, args);
} catch (Exception e) {
LOGGER.error("", e);
System.exit(-1);
}
}
And the test is this:
import static org.junit.Assert.assertEquals;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.junit.jupiter.SpringExtension;
/**
* Test the Query-statements and the DAO methods
*
* #author U005078
*
*/
#SpringBootTest
#ExtendWith(SpringExtension.class)
#ComponentScan({
"com.package.sample"})
#EnableAutoConfiguration
public class SimpleTest {
#SuppressWarnings("unused")
private static final Logger LOGGER = LoggerFactory.getLogger(SimpleTest.class);
#Test
#DisplayName("SimpleTest")
public void testTotalRows() {
With this configuration all is fine, "StartSamples" works as expected and aqlso the SimpleTest.
But when moving "StartSamples" to a different package (e.g. "com.package.sample.start" would make more sense to me - "StartSamples" is still ok but "SimpleTest" does not fail nor succeed - test seems not to become executed.
I see a message:
class path resource [com/package/sapmle/appl1/dbaccess/SimpleTest-context.xml] does not exist
class path resource [com/package/sapmle/appl1/dbaccess/SimpleTestContext.groovy] does not exist
.SimpleTest]: SimpleTest does not declare any static, non-private, non-final, nested classes annotated with #Configuration.
I also found:
Neither #ContextConfiguration nor #ContextHierarchy found for test class [com.package.sample.appl1.dbaccess.SimpleTest], using SpringBootContextLoader
So I defined the #ContextConfiguration to the "SimpleTest", then it worked. But I do not understand at all why the move of the #SpringBootApplication did change this behaviour.
With another try of setting up this project I ended up with "No tests found with test runner 'JUnit 5'" and could also not find any reason. I started over again with the current approach and get to here. And do do nat any clue what gives me the error - for either of the problems.
Any explanation witld be appreciated. I tried for lots of hours now to find something in the internet - but I only found recommendations like "try this", "try that" but no help in understanding.
So any help is appreciated.
Define your SpringBoot Main class like below
#SpringBootTest(classes = {StartSamples.class})
public class SimpleTest {
...
}

No qualifying bean of type repository when running test but not main application

I'm developing a Spring Boot application following TDD methodology. I've created the main classes (controller, service and repository) this way:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class CrimeServiceImpl implements CrimeService{
#Autowired
private CrimeRepository repository;
...
Controller:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class CrimeController {
#Autowired
private CrimeServiceImpl service = new CrimeServiceImpl();
Repository:
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface CrimeRepository extends JpaRepository<Crime, Long>{
}
This is the project structure:
If I run the application normally, no error. The classes' methods are empty. Then I've created a test class like this:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = CrimeServiceImpl.class)
#ComponentScan("com.springmiddleware")
#AutoConfigureMockMvc
#SpringBootTest
public class TestCrimeService {
//Calling method getAllCrimes works
#Test
public void returnAllCrimesExists() throws NoSuchMethodException, SecurityException {
List<Crime> list = new ArrayList<>();
assertTrue(this.service.getAllCrimes() == list);
}
And if I run this, the following error is shown and the test fails:
NoSuchBeanDefinitionException: No qualifying bean of type 'com.springmiddleware.repository.CrimeRepository' available: expected at least 1 bean which qualifies as autowire candidate.
I've checked all annotations and it seems to me that all is ok, and I thought if I missed something, even in the normal run the application would fail. What did I got wrong?
I wanted also to make a test class for a JPARepository, and I also encountered the same error message:
NoSuchBeanDefinitionException: No qualifying bean of type
'SomethingRepository' available:
expected at least 1 bean which qualifies as autowire candidate.
I could make it work by adding the 2 following annotations on top of the test class:
#EnableJpaRepositories(basePackageClasses = SomethingRepository.class) // repository
#EntityScan(basePackageClasses = Something.class) // entity of the repository
Now it looks like:
#RunWith(SpringRunner.class)
#EnableJpaRepositories(basePackageClasses = SomethingRepository.class) // repository
#EntityScan(basePackageClasses = Something.class) // entity of the repository
#SpringBootTest(classes = MyDbUnitTestApp.class) // does some #ComponentScan and #EntityScan on the repositories/entities package, and #EnableAutoConfiguration
#ActiveProfiles(Profiles.myTestProfile)
#DatabaseSetup(value = {
"/datasets/dataset1.xml" }, type = DatabaseOperation.CLEAN_INSERT)
public class SomethingRepositoryTest {
#Autowired
private SomethingRepository sut;
#Test
public void findById() {
Something something= sut.findById(1L);
Assert.assertEquals("foobar", something.getName());
}
}

Clear Spring application context after test

How can I clear the application context after each test execution, with Junit5 and Spring Boot? I want all beans created in the test to be destroyed after its execution, since I am creating the same beans in multiple tests. I don't want to have one configuration class for all tests, but configuration class per test, as shown bellow.
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = MyTest.ContextConfiguration.class)
public class MyTest{
...
public static class ContextConfiguration {
// beans defined here...
}
}
Putting #DirtiesContext(classMode = BEFORE_CLASS) doesn't work with Junit5.
You have claimed twice that #DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) does not work; however, the following shows that it works as documented.
import javax.annotation.PreDestroy;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
#ExtendWith(SpringExtension.class)
#ContextConfiguration
#DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
class MyTest {
#Test
void test1(TestInfo testInfo) {
System.err.println(testInfo.getDisplayName());
}
#Test
void test2(TestInfo testInfo) {
System.err.println(testInfo.getDisplayName());
}
#Configuration
static class Config {
#Bean
MyComponent myComponent() {
return new MyComponent();
}
}
}
class MyComponent {
#PreDestroy
void destroy() {
System.err.println("Destroying " + this);
}
}
Executing the above test class results in output to STDERR similar to the following.
test1(TestInfo)
Destroying MyComponent#dc9876b
test2(TestInfo)
Destroying MyComponent#30b6ffe0
Thus, #DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) is indeed how you "clear the application context after each test execution".
Putting #DirtiesContext(classMode = BEFORE_CLASS) doesn't work with Junit5.
Based on my testing, classMode = BEFORE_CLASS works with TestNG, JUnit 4, and JUnit Jupiter (a.k.a., JUnit 5).
So, if that does not work in your test class, that would be a bug which you should report to the Spring Team.
Do you have any example where you can demonstrate that not working?
FYI: using classMode = BEFORE_CLASS would only ever make sense if the context had already been created within the currently executing test suite. Otherwise, you are instructing Spring to close and remove an ApplicationContext from the cache that does not exist... just before Spring actually creates it.
Regards,
Sam (author of the Spring TestContext Framework)
According to the docs, try #DirtiesContext(classMode = AFTER_EACH_TEST_METHOD)

Unable to inject dependency in Junit test

Having some trouble injecting a dependency in one of my JUnit test classes.
I believe the TestApplication is not package scanning or is not being loaded.
Code below:
package com.mitto.repositories;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
import com.github.springtestdbunit.DbUnitTestExecutionListener;
import com.github.springtestdbunit.annotation.DatabaseSetup;
import com.mitto.MittoApplicationTests;
import com.mitto.domain.User;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration( classes= { MittoApplicationTests.class } )
#TestExecutionListeners({DependencyInjectionTestExecutionListener.class,
TransactionalTestExecutionListener.class,
DbUnitTestExecutionListener.class})
#DatabaseSetup("UserRepositoryTest.xml")
public class UserRepositoryTest {
#Autowired
UserRepository repository;
private static final long FACEBOOK_ID = 1234567;
#Test
public void getUserById() {
User user = repository.findOne(1L);
assertNotNull(user);
assertEquals( user.getFacebookId(), FACEBOOK_ID );
}
}
MittoApplicationTests.java
package com.mitto;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#SpringBootTest
public class MittoApplicationTests {
#Test
public void contextLoads() {
}
}
UserRepository.java
package com.mitto.repositories;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;
import com.mitto.domain.User;
#Repository
public interface UserRepository extends PagingAndSortingRepository<User, Long>{
User findByFacebookId( long facebookId );
User findByAuthToken( String token );
}
I can't see anything wrong with this.
Sometimes, a working example is better than fixes.
Here is a working example:
First, in your configuration class
#SpringBootApplication
#ComponentScan(value = "com.mitto")
#EnableJpaRepositories(value = "com.mitto")
#EntityScan(basePackages = {"com.mitto.domain"}, basePackageClasses = {Jsr310JpaConverters.class})
public class MittoApplicationTests {
}
Second, in your test class
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = MittoApplicationTests.class) // replace the #ContextConfiguration with #SpringBootTest
// rest of of your annotations ...
public class UserRepositoryTest {
#Autowired
UserRepository repository;
// your test cases
}
A Spring Boot application is just a Spring ApplicationContext, so nothing very special has to be done to test it beyond what you would normally do with a vanilla Spring context. One thing to watch out for though is that the external properties, logging and other features of Spring Boot are only installed in the context by default if you use SpringApplication to create it.
Spring Boot provides a #SpringBootTest annotation which can be used as an alternative to the standard spring-test #ContextConfiguration annotation when you need Spring Boot features. The annotation works by creating the ApplicationContext used in your tests via SpringApplication.
Please read the documentation for more details:
SpringBootTest annotation
boot-features-testing

Resources