Documentation that describes the Spring ContextConfiguration Evaluation Order with Inheritance? - spring-boot

Lets say I have the following setup for 2 test classes in a SpringBoot Project
class Config1 {
#Bean
public MyObject myObject() {
return Mockito.mock(myObject.class);
}
}
class Config2 {
#Bean
public MyObject myObject() {
return new MyObject();
}
}
#ContextConfiguration(classes = {Config1.class, Config2.class})
class Parent {
....
}
class Test1 extends Parent {
#AutoWired
private MyObject myObject; // Instance of MyObject rather than a Mock
....
}
class Config3 extends Config1 {
....
}
#ContextConfiguration(classes = Config3.class)
class Test2 extends Parent {
#AutoWired
private MyObject myObject; // Instance of a Mock rather than MyObject
....
}
As the comments mention, for Test1 the instance of MyObject comes from Config2, while Test2 receives the Config3 (inherited via Config1) mocked instance of MyObject.
The rule this seems to follow is the last evaluated config wins. i.e. If Parent was defined as
#ContextConfiguration(classes = {Config2.class, Config1.class})
class Parent {
Then only the Mock would be used (I assume).
Where are the the rules, that define the evaluation order, for the #ContextConfiguration described?
There appear to be flags to control whether inheritance is ignored. See https://www.logicbig.com/tutorials/spring-framework/spring-core/test-configuration-inheritance.html. But I did not find anything to describe the evaluation order.
PS: Apologies for the convoluted sample code. It's a simplified version of something I came across.

Related

Spring Configuration - Inject Mock Beans

I am using Spring, Junit and Mockito. I need to override beans defined in the main spring configuration using another mockito test configuration (injecting mock beans only as needed). Nested beans have been #Autowired in the application.
Update:
Based on alfcope's answer below, it is important to add the name attribute so that spring can allow the primary bean (mock) to override the original one. Otherwise you get this exception:
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
The info message in the spring log shows:
Skipping bean definition for [BeanMethod:name=bar,declaringClass=test.package.MockitoTestConfiguration]: a definition for bean 'bar' already exists. This top-level bean definition is considered as an override.
Example:
I have a simplified example below that works. Here, Bar is the nested inside Foo, and I need to mock Bar for testing:
#Component
public class Foo
{
#Autowired
private Bar bar;
public String getResponseFromBar(String request)
{
String response = bar.someMethod(String request);
//do something else with this reponse
return response;
}
}
#Component
public class Bar {
public String someMethod(String request) {
String response = null;
//do something
return response;
}
}
Now for testing, let's say I want to inject a mockbar instead of the real bar. How can I achieve this in my test class below?
#Profile("test")
#Configuration
public class MockitoTestConfiguration {
//adding the name attribute is important.
#Bean(name="mockBar")
#Primary
public Bar bar() {
logger.debug("injecting mock bar");
return Mockito.mock(Bar.class);
}
}
Actual test case:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath*:test-context.xml")
public class FooTest {
#Autowired
Foo foo;
#Autowired
Bar mockBar; //need this to set up the mock response in the test case.
#Test
public void testAMethodInFoo_WithBarInjectedByMockito() {
//set up the mockBar response
Mockito.when(mockBar.someMethod("1")).thenReturn("1-response");
String response = foo.getResponseFromBar();
assertEquals("1-response", response);
}
}
Based on the ConfigurationClassBeanDefinitionReader code I guess you are using xml configuration to define your main bean. If so, just add a name when creating your mockito bean.
#Bean(name="mockbar")
#Primary
public Bar bar() {
logger.debug("injecting mock bar");
return Mockito.mock(Bar.class);
}
This way Spring will allow you to have both beans, and as you are using #Primary it will be the one used by your tests.
Spring overriding primary bean with non-primary bean
ConfigurationClassBeanDefinitionReader Code
Alternatively, if you use Mockito, you can do this and completely do away with the extra MockitoTestConfiguration class and named primary mock beans in the test profile. Just simply do this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath*:test-context.xml")
public class FooTest {
#Autowired
#InjectMocks
Foo foo;
#Mock
Bar mockBar; //need this to set up the mock response in the test case.
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public void testAMethodInFoo_WithBarInjectedByMockito() {
//set up the mockBar response
Mockito.when(mockBar.someMethod("1")).thenReturn("1-response");
String response = foo.getResponseFromBar();
assertEquals("1-response", response);
}
}

How to write test class with #Autowired variable for inherited class

I have problem with writing test cases using SPOCK. Could anyone please help me?
I have classes & interfaces like below,
//Helper class
public class ObjClass{
//Defining all property variables & corresponding getters & setters methods.
}
//Interface
public interface B{
//Declaring custom methods for Mongo repository.
public int getId();
}
public interface A extends MongoRepository<ObjClass, Serializable>, B{
//Defining some standard MongoRepository methods here
}
// Implementation Classes
public class Aimpl implements B{
//implementing all B interface methods
}
public class ctrlClass{
#Autowired
A aObj;
public int getIdValue(){
return aObj.getId();
}
}
And below is the corresponding SPOCK test cases:
class test extends Specification
{
ctrlClass obj1
A obj2 //interface class object
def setup(){
obj1 = new ctrlClass();
obj2 = new Aimpl(); //Creating object for interface using impl class.
obj1.aObj = obj2
}
def "test"(){
when:
def a = obj2.getIdValue()
then:
//validating some conditions here with 'a' value
}
}
Getting below error while executing above test case,
Cannot cast object Aimpl to class A.
The same above scenario is working fine with Spring #Autowired. But not in Spock.
*
Is there any alternate available for #Autowired in SPOCK? Please suggest me some solutions & your comments.
*
The problem you have is the ability of Spring to bind the interface with the related implementation.
If your interface has only one implementation and the single implementation has the annotation #Component with Spring's component scan enabled, than Spring framework success to infer the relationship between the interface and its implementation.
In case the component scan is not enabled, then the bean should be explicitly defined in your spring configuration file (such as application-config.xml).
The casting of Aimpl and A cannot succeed because the inheritance classes/interface are different.
You should change the code like the following:
public class ctrlClass{
#Autowired
Aimpl aObj;
public int getIdValue(){
return aObj.getId();
}
}
And in the test class make the following change:
A obj2 //interface class object
Should be changed to:
Aimpl obj2

How do I post-process beans of #Configuration classes that define more #Beans in JavaConfig?

In Spring XML, I can define a bean that instantiates a class annotated with #Configuration. When I do, that bean is post-processed. Any methods inside that class with #Bean are also added to the container. How do I perform a similar post-processing in JavaConfig?
Here's the XML version:
<bean id="test" class="com.so.Test">
<property name="prop" value="set before instantiating #Beans defined in Test"/>
</bean>
The associated Test class:
#Configuration
class Test {
private String prop;
void setProp(final String prop) {
this.prop = prop;
}
#Bean
NeedThisBean needThisBeanToo() {
return new NeedThisBean(prop);
}
}
If I use Spring XML Config, both test and needThisBeanToo are available in the container. needThisBeanToo is added via a BeanPostProcessor, though I can't recall which one. If I use JavaConfig, only test is available in the container. How do I make needThisBeanToo available to the container? #Import would work, except that prop being set is required for needThisBeanToo to be initialized correctly.
The part that makes all of this complicated is that Test is vended from a library I'm consuming. I don't control Test, nor can I change it. If I drive it from JavaConfig, it would look like this:
#Configuration
class MyConfiguration
{
#Bean
Test test() {
Test test = new Test();
test.setProp("needed to init `needThisBeanToo` and others");
return test;
}
}
The JavaConfig example does not instantiate needThisBeanToo despite it being defined in Test. I need to get needThisBeanToo defined, preferably without doing it myself, since I don't want to copy code I don't own. Delegation isn't attractive, since there are a number of subsequent annotations/scopes defined on needThisBeanToo (and others defined inside Test).
Your problem is is that you're ignoring the #Configuration annotation completely. Why is that?
When code reaches this line Test test = new Test(); it just doesn't do anything with #Configuration. Why? Because annotation is not something that a constructor is aware of. Annotation only marks some meta-data for the class. When spring loads classes it searches for annotations, when you call a constructor of a class, you don't. So the #Configuration is just ignored because you instantiate Test with new Test() and not through spring.
What you need to do is to import Test as a spring bean. Either via XML as you showed in your question OR using #Import. You problem with prop is that the setter isn't called because that's just not the way to do it. What you need to be doing is either do something like that:
#Configuration
class Test {
private String prop = "set before instantiating #Beans defined in Test";
#Bean
NeedThisBean needThisBeanToo() {
return new NeedThisBean(prop);
}
}
Or to create a property in spring (this is a different subject) and inject the value:
#Configuration
class Test {
#Autowired
#Value("${some.property.to.inject}") // You can also use SPeL syntax with #{... expression ...}
private String prop;
#Bean
NeedThisBean needThisBeanToo() {
return new NeedThisBean(prop);
}
}
You can also create a bean of type String and inject it as follows:
#Configuration
class Test {
#Autowired
#Qualifer("nameOfBeanToInject")
private String prop;
#Bean
NeedThisBean needThisBeanToo() {
return new NeedThisBean(prop);
}
}
In the last case you can define your original MyConfiguration with this bean:
#Configuration
#Import(Test.class)
class MyConfiguration
{
#Bean(name = "nameOfBeanToInject")
String test() {
return "needed to init `needThisBeanToo` and others";
}
}
In any case you have to import Test either using #Import or as a normal XML bean. It won't work by calling the constructor explicitly.
Here's a way to handle vended #Configuration classes that require some properties to be set prior to creating their #Beans:
Vended #Configuration class:
#Configuration
class Test {
private String property;
public setProperty(final String property) {
this.property = property;
}
#Bean
PropertyUser propertyUser() {
return new PropertyUser(property);
}
#Bean
SomeBean someBean() {
// other instantiation logic
return new SomeBeanImpl();
}
}
Here's the consuming #Configuration class:
#Configuration
class MyConfig {
#Bean
static String myProperty() {
// Create myProperty
}
/**
* Extending Test allows Spring JavaConfig to create
* the beans provided by Test. Declaring
* Test as a #Bean does not provide the #Beans defined
* within it.
*/
#Configuration
static class ModifiedTest extends Test {
ModifiedTest() {
this.setProperty(myProperty());
}
#Override
#Bean
SomeBean someBean() {
return new SomeBeanCustomImpl(this.propertyUser());
}
}

Testing Mock Bean in Spring with Spock

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

spring 3 autowiring and junit testing

My code:
#Component
public class A {
#Autowired
private B b;
public void method() {}
}
public interface X {...}
#Component
public class B implements X {
...
}
I want to test in isolation class A. Do I have to mock class B? If yes, how? Because it is autowired and there is no setter where i could send the mocked object.
I want to test in isolation class A.
You should absolutely mock B, rather than instantiate and inject an instance of B. The point is to test A whether or not B works, so you should not allow a potentially broken B interfere with the testing of A.
That said, I highly recommend Mockito. As mocking frameworks go, it is extremely easy to use. You would write something like the following:
#Test
public void testA() {
A a = new A();
B b = Mockito.mock(B.class); // create a mock of B
Mockito.when(b.getMeaningOfLife()).thenReturn(42); // define mocked behavior of b
ReflectionTestUtils.setField(a, "b", b); // inject b into the B attribute of A
a.method();
// call whatever asserts you need here
}
Here's an example of how I got my tests working with Spring 3.1, JUnit 4.7, and Mockito 1.9:
FooService.java
public class FooService {
#Autowired private FooDAO fooDAO;
public Foo find(Long id) {
return fooDAO.findById(id);
}
}
FooDAO.java
public class FooDAO {
public Foo findById(Long id) {
/* implementation */
}
}
FooServiceTest.java
#RunWith(MockitoJUnitRunner.class)
public class FooServiceTest {
#Mock private FooDAO mockFooDAO;
#InjectMocks private FooService fooService = new FooService();
#Test public final void findAll() {
Foo foo = new Foo(1L);
when(mockFooDAO.findById(foo.getId()).thenReturn(foo);
Foo found = fooService.findById(foo.getId());
assertEquals(foo, found);
}
}
You can inject the field via reflection using Spring's ReflectionTestUtils.setField (or the junit extension PrivateAccessor) or you can create a mock application context and load that. Though for a simple unit (non-integration) test, I favor using reflection for simplicity.
This forum discussion makes sense to me. You can declare your private member b as a type of InterfaceB which is implemented by the class B (ie: service-oriented) then declare a MockB class would also implement the same interface. In your test environment application context, you declare MockB class and your production application context you declare the normal B class and in either case, the code for class A does not need to be changed since it will be auto-wired.

Resources