I am learning how to run #SpringBootTest the "right way", but is encountering a problem with autowiring in my test classes (in "src/test/java" directory):
I have a class Graphs which is annotated by #Component in a package under "src/main/java":
#Component
public class Graphs {
....
}
Then, I created test classes under "src/test/java". One of them is:
#SpringBootTest
public class GraphsTest {
#Test
public void testRun () {
Graphs graph = new Graphs(); // Using new to create an object
if (graph==null) {
System.out.println("It's null");
} else {
System.out.println("It's not null");
}
}
...
When I test run "testRun" method, it produced "It's not null" as expected.
After unit tests, i wanted to inject a "graph" as Graphs class was annotated by #Component thus a bean should be available for autowiring:
#SpringBootTest
public class GraphTest {
#Autowired
private Graphs graph; // auto inject a bean graph
#Test
public void testRun () {
if (graph==null) {
System.out.println("it's null");
} else {
System.out.println("it's not null");
}
}
....
Now with autowiring, "testRun" always produced: "it's null" even when I attempted the following, ("xxxxxx" is the full name of the package which contains Graphs.java file):
adding #Import(xxxxxx/Graphs.class)
adding #ComponentScan("xxxxxxx")
copying the Graphs.java file into the test package
Adding a #Bean into TestConfiguration.java in the test package.
#Bean
public Graphs graph () {
return new Graphs();
}
I started to suspect that I have fundamentally misunderstood/missed something about setting up a Spring Boot test environment: isn't it #SpringBootTest all I need to get started?
You forgot to add #RunWith(SpringRunner.class) above GraphTest.
SpringRunner is the alias for SpringJUnit4ClassRunner which is responsible, among other things, for load TestContextManager (see docs). Without it, your application context wouldn't be loaded for tests.
Related
I created an Embedded Sftp server bean for my integration tests, i hooked the startup and the shutdown of the server respectively with the afterPropertiesSet and destroy life cycles
public class EmbeddedSftpServer implements InitializingBean, DisposableBean {
//other class content
#Override
public void afterPropertiesSet() throws Exception {
//Code for starting server here
}
#Override
public void destroy() throws Exception {
//Code for stopping server here
}
}
here my config class
#TestConfiguration
public class SftpTestConfig {
#Bean
public EmbeddedSftpServer embeddedSftpServer() {
return new EmbeddedSftpServer();
}
//Other bean definitions
}
Now when i inject the bean in my test classes like the following :
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = SftpTestConfig .class)
class ExampleOneIT {
#Autowired
private EmbeddedSftpServer embeddedSftpServer;
}
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = SftpTestConfig .class)
class ExampleTwoIT {
#Autowired
private EmbeddedSftpServer embeddedSftpServer;
}
#SpringBatchTest
#ContextConfiguration(classes = SftpTestConfig .class)
class ExampleThreeIT {
#Autowired
private EmbeddedSftpServer embeddedSftpServer;
}
And i run all the test classes simultaneously, i found out that for the test classes annotated with #ExtendWith(SpringExtension.class), it's the same context that is used (which is understandable since i guess spring cache it) and therefore the bean lifecycle methods are not executed again, but to my surprise, for the class annotated with #SpringBatchTest i noticed that the life cycle hooks of the bean are executed again! Which is a behavior that is not convenient since i want the application context to start the server one time for all tests and close it at the end of those tests (which is the case if i use only #ExtendWith(SpringExtension.class) for all my test classes).
N.B. : I need to use #SpringBachTest for my ExampleThreeIT test class.
I think you are hitting this issue: https://github.com/spring-projects/spring-batch/issues/3940 which has been fixed in v4.3.4/4.2.8. Upgrading to one of these versions should fix your issue.
We have a spring bean doing some initialization stuff at bootup of a spring-boot application. To do so, the application registers a ApplicationListener<ApplicationReadyEvent>, which queries the database and acts upon the data.
To test this process, we have to inject testdata in the database. Our first try was to init the database with a test-specific ApplicationListener<ApplicationStartedEvent>. This works, however, the code looks rather nasty. The idea was to use the #Sql annotation instead to load the initial data.
This works not as expected, as the data is injected after the ApplicationReadyEvent has been published. I was unable to find means to change the phase during which the #Sql data is written to the database.
Is there a way to ensure, the data of #Sql is written prior to publishing the ApplicationReadyEvent? The test is currently otherwise annotated to run with SpringRunner, #DataJpaTest and with #DirtiesContext.
Edit: Provide Code
The ApplicationListener ist provided as this:
#Component
public class ApplicationStartup implements ApplicationListener<ApplicationReadyEvent> {
#Override
public void onApplicationEvent(final ApplicationReadyEvent event) {
// Do someting with SQL-Data
}
}
While the test looks like this:
#RunWith(SpringRunner.class)
#ComponentScan
#Import(SomeTestConfig.class)
#DataJpaTest
//#SQL("/somedata.sql")
public class SomeTest {
#Test
public void test() {
// Assert ApplicationListener has run
}
}
With the test-config as follows:
#TestConfiguration
#Profile(ReplayTest.PROFILE_SOME_TEST)
class SomeTestConfig {
#Bean
public ApplicationListener<ApplicationStartedEvent> testSetupBean() {
return new ApplicationListener<ApplicationStartedEvent>() {
// Insert data within onApplicationEvent-Method
};
}
}
If I uncomment #SQL and comment-out the #Import, the testdata is visible from within the test itself but not from within the ApplicationListener.
You can make use of the #Order annotation. You can annotate your application listeners and your test with it. By default, this has lowest precedence. So you should provide higher precedence to your application listener and lowest to your test.
Listener:
#Override
#Order(Ordered.HIGHEST_PRECEDENCE)
public void onApplicationEvent(ApplicationReadyEvent event) {
System.out.println("...publishing application event");
}
Test:
#Test
#Sql(scripts = "test.sql")
#Order
public void test() {
System.out.println("...loading data");
}
I am using Spock to create integration tests for my Spring Boot Application, but I'm having trouble figuring out how to mock part of a Service for all my test cases.
My app writes to an Azure Cosmos database running in Gremlin Graph mode, and since I don't know of any in-memory databases to adequately simulate it, I would like to make sure any service that writes to my development database only interacts with entities labeled with the current test's random UUID. So any service that reads/writes to the database needs to make sure to include the current test ID in any query.
What's the best way to mock a couple reused methods in an abstract base class via Spock?
GraphService.groovy <== The abstract class with some methods I wish to mock.
abstract class GraphService {
#Autowired
protected GraphTraversalSource g
protected GraphTraversal buildBaseQuery() {
return g.V()
}
protected GraphTraversal buildBaseCreationQuery(String label) {
return g.addV(label)
}
}
Several database searching/modifying services inherit the above class. For all of my tests, instead of g.V() I want g.V().has("testID", testId) and instead of g.addV(label) I want g.addV(label).property("testID", testId). How can I accomplish this across all my integration tests? I tried creating a base-specification class that specified this behavior, but it didn't work.
TestConfig.groovy
#Configuration
class TestConfig {
#Bean
#Primary
GraphPersistenceService persistenceService(
GraphTraversalSource g) {
DetachedMockFactory mockFactory = new DetachedMockFactory()
GraphPersistenceService persistenceService = mockFactory.Stub( //Mock doesn't work either
[constructorArgs: [g]], GraphPersistenceService)
return graphPersistenceService
}
}
BaseIntegrationTest.groovy
class BaseIntegrationTest extends Specification {
#Autowired
TestUtil testUtil
#Autowired
GraphTraversalSource g
#Autowired
GraphPersistenceService persistenceService
def setup() {
persistenceService.buildBaseQuery >> g.V().has("testID", testUtil.id)
persistenceService.buildBaseCreationQuery(_ as String) >> { label ->
g.addV(label).property("testID", testUtil.id)
}
}
def cleanup() {
testUtil.removeAllEntitiesWithCurrentTestId()
}
}
And then in an actual test:
#SpringBootTest(classes = MyGraphApplication.class)
#ContextConfiguration(classes = [GraphDbConfig.class, TestConfig.class])
#ActiveProfiles("test")
#TestPropertySource(locations = 'classpath:application-local.properties')
class UserOfPersistenceServiceSpec extends BaseIntegrationTest {
#Autowired
UserOfGraphPersistenceService userOfPersistenceService
def "Can create a bunch of vertices"() {
expect:
userOfPersistenceService.createABunchOfVertices() == 5
}
}
PS. I'm using Spring 1.5.10.RELEASE and groovy 2.4.15...
If upgrading to Spock 1.2 is an option for you, I suggest ditching the TestConfig class and using the #SpringBean annotation.
This is an example of how I have it set up in my tests:
#ActiveProfiles(["integrationtest"])
#DirtiesContext
abstract class IntegrationTest extends Specification {
#SpringBean
EurekaClient eurekaClient = Mock() {
getNextServerFromEureka(*_) >> Mock(InstanceInfo) {
getHomePageUrl() >> "test.test"
}
}
// ... other stuff
}
There is the spring-boot application that uses spring-aop. proxy-target-class is true.
I'm trying to create a test for a service class. This service depends on a component class. I want to inject a mock into the service instead of the real component.
I found some similar questions:
Mocking a property of a CGLIB proxied service not working
Injecting Mockito mocks into a Spring bean
I choose this answer to the last question, and I have tried to implement this approach. I chose it because it is not tied to the implementation details of the proxy classes and I can easily use a config class in other tests.
Below there is the example which simulates the real problem.
#org.aspectj.lang.annotation.Aspect
#org.springframework.stereotype.Component
public class Aspect {
#Before("within(demo.Service)")
public void someAdvice() {
System.out.println("advice");
}
}
#org.springframework.stereotype.Service
public class Service {
#Autowired
private Component component;
public void action() {
System.out.println(component.action());
}
}
#org.springframework.stereotype.Component
public class Component {
public String action() {
return "real action";
}
}
#SpringApplicationConfiguration
public class ServiceTest extends BaseTest {
#Autowired
Service service;
#Test
public void testAction() {
service.action();
}
#Configuration
public static class Config {
#Mock Component mock;
public Config() {
MockitoAnnotations.initMocks(this);
}
#Bean
public Component component() {
Mockito.when(mock.action()).thenReturn("mock action");
return mock;
}
}
}
Complete example: https://github.com/eds0404/spring-inject-mock-into-proxy
The above code is not working as I expect, the service does not use mock ("real action" will be printed if you run test). But the above code works fine if the Component class is not marked with #Component annotation, and its objects are created by the method with #Been annotation.
How to solve this issue? If this is wrong approach, what is best practice?
I'm being hit with the issue that spock doesn't allow Mocks to be created outside of the specification - How to create Spock mocks outside of a specification class?
This seems to be still outstanding so am asking is that giving that i've got a complex and nested DI graph what is the most efficient way to 'inject' a mock representation deep in the graph?
Ideally, I have one bean definition set for normal deployment and another when running unit tests and it is this definition set being the applicable Mocks
e.g.
#Configuration
#Profile("deployment")
public class MyBeansForDeployment {
#Bean
public MyInterface myBean() {
return new MyConcreateImplmentation();
}
}
&&
#Configuration
#Profile("test")
public class MyBeansForUnitTests {
#Bean
public MyInterface myBean() {
return new MyMockImplementation();
}
}
Since Spock 1.1, you can create mocks outside of a specification class (detached mocks). One of the options is DetachedMockFactory. Take a look at the documentation or my answer to the question you linked.
You could try to implement a BeanPostProcessor that will replace the beans that you want with test doubles, such as shown below:
public class TestDoubleInjector implements BeanPostProcessor {
...
private static Map<String, Object[]> testDoubleBeanReplacements = new HashMap<>();
public void replaceBeanWithTestDouble(String beanName, Object testDouble, Class testDoubleType) {
testDoubleBeanReplacements.put(beanName, new Object[]{testDouble, testDoubleType});
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (testDoubleBeanReplacements.containsKey(beanName)) {
return testDoubleBeanReplacements.get(beanName)[TEST_DOUBLE_OBJ];
}
return bean;
}
In your test, setup your mocks like shown below before initializing the application context. Make sure to include the TestDoubleInjector as a bean in your test context.
TestDoubleInjector testDoubleInjector = new TestDoubleInjector()
testDoubleInjector.replaceBeanWithTestDouble('beanToReplace', mock(MyBean.class), MyBean.class)
It could be done using HotSwappableTargetSource
#WebAppConfiguration
#SpringApplicationConfiguration(TestApp)
#IntegrationTest('server.port:0')
class HelloSpec extends Specification {
#Autowired
#Qualifier('swappableHelloService')
HotSwappableTargetSource swappableHelloService
def "test mocked"() {
given: 'hello service is mocked'
def mockedHelloService = Mock(HelloService)
and:
swappableHelloService.swap(mockedHelloService)
when:
//hit endpoint
then:
//asserts
and: 'check interactions'
interaction {
1 * mockedHelloService.hello(postfix) >> { ""Mocked, $postfix"" as String }
}
where:
postfix | _
randomAlphabetic(10) | _
}
}
And this is TestApp (override the bean you want to mock with proxy)
class TestApp extends App {
//override hello service bean
#Bean(name = HelloService.HELLO_SERVICE_BEAN_NAME)
public ProxyFactoryBean helloService(#Qualifier("swappableHelloService") HotSwappableTargetSource targetSource) {
def proxyFactoryBean = new ProxyFactoryBean()
proxyFactoryBean.setTargetSource(targetSource)
proxyFactoryBean
}
#Bean
public HotSwappableTargetSource swappableHelloService() {
new HotSwappableTargetSource(new HelloService());
}
}
Have a look at this example https://github.com/sf-git/spock-spring