Using Spring Framework's #Autowired for instantiating and injecting SUT (System Under Test) in the test fixture - spring

I have seen developers using Spring's #Autowired feature making Spring framework responsible for instantiating and injecting SUT (System Under Test) or CUT (Class Under Test) in the test class or fixture. The following is the snippet showing #Autowired being used in the test fixture:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.ExpectedException;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.springframework.test.context.ContextConfiguration;
interface BackingStore
{
//Some methods
}
class Logger
{
public Logger(BackingStore backingStore)
{
//Capture input parameter into member variables
}
public void Log(String message)
{
//Some logic
}
}
#ContextConfiguration(locations = { "classpath:someconfig.xml" })
public class LoggerTests extends AbstractTestNGSpringContextTests
{
#Autowired
private Logger _logger;
#Test
#ExpectedException(NullPointerException)
public void Log_WhenPassedNull_ShouldThrowException()
{
_logger.Log(null);
}
}
All the dependencies (recursively) required by the SUT are specified as part of the Spring configuration XML file. I do not like this approach. I like all the test (unit or integration) to read like a story (I heard Kent Beck saying the same thing :)). I like instantiating SUT/CUT in the test case itself even though if it is complex. This gives a clear picture about the test case.
I have few concerns regarding #Autowired or any auto injection mechanism being used for injecting SUT in the test fixture:
It reduces test code readability. #Autowire appears like magic. Arrange of AAA (Arrange-Act-Assert) moves to XML file from test code.
It reduces test code maintainability. This is because of #2.
Could not effectively verify constructor of SUT/CUT throwing exception in exceptional cases. I am not 100% sure about this. I do not know if Spring framework has an answer for this.
It seems overkill for unit or integration tests.
I ask experts for 2 cents on this subject.
Thanks.

It only reduces test code readability if you dont know where to look for the #Autowired objects. I would advise using SpringJunitTestRunner that defines the test application context at the top of the unit test class.
Using dependency injection in your test cases allows you to easily test with different objects.
E.g. If your code required a 3rd party service, you could use dependency injection (e.g. Autowiring a Spring bean) to inject a mocked service for your unit tests and the real service for the application.
So for this reason it definitley doesnt decrease the test code maintainability, as it is really encouraging loose coupling between the code youre testing and any external objects.
It may be overkill to inject all objects in such a way, but it is definitely not overkill for unit tests/integration tests in general.

Related

Spring boot test minimal test slice or manual configuration

I have many different SpringBoot tests running. So far the auto configuration slices were really helpful, especially in combination with #MockBean.
But in my current test no such slice fits and booting up the complete context using #SpringBootTest is too slow.
Is there a way to manually set the tip of the object tree to be started with and from there spring autowires all needed beans? Or is there a way to set all needed beans manually?
In my specific case i want to test a MapStruct generated mapper (using componentModel = "spring") this mapper uses two other mappers, each injecting a service to do their work.
The services are provided via #MockBean:
#RunWith(SpringRunner.class)
#SpringBootTest
public class ProductResponsibleUnitMapperTest {
#Autowired
private PRUMapper mapper;
#MockBean
private TradingPartnerService tradingPartnerService;
#MockBean
private ProductHierarchyService productHierarchyService;
#Test
public void mapForthAndBack(){
//works but takes ages to boot
}
}
I could not use constructor injection on the mappers (for the services) because MapStruct won't generate correct implementations.
How to get a Spring-Context only containing the needed beans?
I found one way by explicitly declaring all implementation used:
#SpringBootTest(classes = {ProductResponsibleUnitMapperImpl.class, LegalEntityMapperImpl.class, ProductHierarchyMapperImpl.class})
For more complex setups it will be cumbersome and also dangerous to declare generated classes.
I am still searching for a better cleaner way to let Spring decide what classes needed. It should be possible to set the class in hand and let Spring decide what classes needed and to be instantiated.

Multiple tests with autowired MockHttpServletRequest not working?

I use an #Autowired MockHttpServletRequest in some of my Spring tests. TestNG is used as testing framework. If I only have one test method in the class this works fine. However, if there are multiple test methods, only the first run test uses my MockHttpServletRequest. Let me illustrate with an example:
#WebAppConfiguration
#ContextConfiguration({"classpath:applicationContext.xml"})
public class FooTest extends AbstractTestNGSpringContextTests {
#Autowired
private MockHttpServletRequest servletRequest;
#Test
public void test1() {
assertEquals(((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(), servletRequest);
}
#Test
public void test2() {
assertEquals(((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(), servletRequest);
}
}
In this example, test1() passes, but test2() fails! If you run the test methods individually, they both pass. Why does one test fail if they are run together?
I tried to dig in the code, there seems to be some kind of reset of the request attributes after a test method have run, but I didn't find a way to turn it off. My Spring version is 3.2.8.RELEASE.
UPDATE: This has been fixed in Spring Framework 3.2.9, 4.0.4, and 4.1. See SPR-11626 for details.
Well, my friend... you have discovered a bug in the Spring TestContext Framework.
The reason for this behavior is that ServletTestExecutionListener resets the request attributes after each test method, but DependencyInjectionTestExecutionListener does not re-inject dependencies before each test method (by default). When the second test method is executed, the servletRequest field is still referencing the MockHttpServletRequest that was created for the previous test method; whereas, ServletTestExecutionListener creates a new instance of MockHttpServletRequest for each test method and sets it in the request attributes. Thus, the injected request and the one stored in the RequestContextHolder are only the same for the first test method that executes in TestNG.
Since I am the author of this code, I have to personally apologize, but... I'll make sure it gets fixed ASAP. See SPR-11626 for details on the status of the fix. ;)
Note: this bug only applies to TestNG tests; this does not apply to JUnit tests.
As a work-around, you can annotate the affected test methods with #DirtiesContext (or annotate your test class with #DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)). This will allow your tests to pass as you expect.
The use of #DirtiesContext will make Spring close your test ApplicationContext after each test method, and this will likely have a negative impact on the speed of your tests; however, as of Spring 3.2.8 and 4.0.3, this is the only non-custom solution.
Having said that, the following is a much more efficient work-around. Just define this custom TestExecutionListener in your project:
public class AlwaysReinjectDependenciesTestExecutionListener extends AbstractTestExecutionListener {
public void afterTestMethod(TestContext testContext) throws Exception {
testContext.setAttribute(DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE, Boolean.TRUE);
}
}
And then annotate your test class like this:
#TestExecutionListeners(AlwaysReinjectDependenciesTestExecutionListener.class)
That should clear up any issues and keep your test suite running quickly.
Regards,
Sam

Can I exclude logging in JUnit tests on ZK controller using Log4j and not look for a file

I need some help on logging and unit testing. The class under test is a Zk GenericForwardComposer and i want to exclude concrete logging and logging configuration from the test. Im following a kind of TDD and have a test failure because of logging. Ive posted the class under test and the test. My test doesnt have any configuration for log4j because I want as pure a unit test as possible and as simple as possible.
The test failure:
log4j:ERROR setFile(null,true) call failed.
java.io.FileNotFoundException: /log/t2-console.log (No such file or directory)
at java.io.FileOutputStream.open(Native Method)
at java.io.FileOutputStream.<init>(FileOutputStream.java:212)
at java.io.FileOutputStream.<init>(FileOutputStream.java:136)
at org.apache.log4j.FileAppender.setFile(FileAppender.java:294)
at org.apache.log4j.RollingFileAppender.setFile(RollingFileAppender.java:207)
at org.apache.log4j.FileAppender.activateOptions(FileAppender.java:165)
at org.apache.log4j.config.PropertySetter.activate(PropertySetter.java:307)
at org.apache.log4j.xml.DOMConfigurator.parseAppender(DOMConfigurator.java:295)
at org.apache.log4j.xml.DOMConfigurator.findAppenderByName(DOMConfigurator.java:176)
at org. apache.log4j.xml.DOMConfigurator.findAppenderByReference(DOMConfigurator.java:191)
at org.apache.log4j.xml.DOMConfigurator.parseChildrenOfLoggerElement(DOMConfigurator.java:523)
at org.apache.log4j.xml.DOMConfigurator.parseCategory(DOMConfigurator.java:436)
at org.apache.log4j.xml.DOMConfigurator.parse(DOMConfigurator.java:1004)
at org.apache.log4j.xml.DOMConfigurator.doConfigure(DOMConfigurator.java:872)
at org.apache.log4j.xml.DOMConfigurator.doConfigure(DOMConfigurator.java:778)
at org.apache.log4j.helpers.OptionConverter.selectAndConfigure(OptionConverter.java:526)
at org.apache.log4j.LogManager.<clinit>(LogManager.java:127)
at org.apache.log4j.Logger.getLogger(Logger.java:117)
at com.t2.integration.controller.IntegrationSearchController.<clinit>(IntegrationSearchController.java:60)
at com.t2.integration.controller.IntegrationSearchControllerTest.doesSomeCalling(IntegrationSearchControllerTest.java:14)
I haven't configured the unit test for Log4j and I want to follow Red-Green-Refactor. I think I could handle the logging call in the test but want to find a way to exclude logging entirely if that's possible.
public class IntegrationSearchControllerTest {
#Test
public void doesSomeCalling() {
IntegrationSearchController searchController = new IntegrationSearchController();
}
}
I don't want any ZK context or ZK integration testing components to leak into my unit tests. And I want the tests to be as simple as possible. Is it AOP, interfaces, dependency injection or refactoring?
The class under test:
package ...
import org.apache.log4j.Logger;
public class IntegrationSearchController extends IntegrationBaseController {
private static final Logger LOGGER = Logger.getLogger(IntegrationSearchController.class);
...
The controller is ZK managed
As your Logger is static (normal) you will need to use a mock framework that can mock statics. This is much more difficult to do (from a framework perspective as it usually involves manipulating byte code). Anyway it's been done and you have options.
Here's how it would look using PowerMock with Mockito:
#RunWith(PowerMockRunner.class)
#PrepareForTest(Logger.class)
public class TestIntegrationSearchController {
#Before
public void initMockLogger() {
mockStatic(Logger.class);
when(Logger.getLogger(any(Class.class))).thenReturn(mock(Logger.class));
}
#Test
public void test() {
IntegrationSearchController controller = new IntegrationSearchController();
// controller.LOGGER is a Mockito mocked Logger
}
}
Note: you don't need to set up mocking in an #Before but I find it reads easier.
Now.. all that said, I think you're solving the wrong problem here. Rather than mocking logging in every test, consider alternatives that obviate the need. For example, you could use a different Log4J configuration in tests which logs to STDOUT.

How can a test 'dirty' a spring application context?

The spring framework documentation states:
In the unlikely case that a test may
'dirty' the application context,
requiring reloading - for example, by
changing a bean definition or the
state of an application object -
Spring's testing support provides
mechanisms to cause the test fixture
to reload the configurations and
rebuild the application context before
executing the next test.
Can someone elaborate this? I am just not getting it. Examples would be nice.
Each JUnit test method is assumed to be isolated, that is does not have any side effects that could cause another test method to behave differently. This can be achieved by modifying the state of beans that are managed by spring.
For example, say you have a bean managed by spring of class MySpringBean which has a string property with a value of "string". The following test method testBeanString will have a different result depending if it is called before or after the method testModify.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"/base-context.xml"})
public class SpringTests {
#Autowired
private MySpringBean bean;
#Test public void testModify() {
// dirties the state of a managed bean
bean.setString("newSring");
}
#Test public void testBeanString() {
assertEquals("string", bean.getString());
}
}
use the #DirtiesContext annotation to indicate that the test method may change the state of spring managed beans.

Spring beans redefinition in unit test environment

We are using Spring for my application purposes, and Spring Testing framework for unit tests. We have a small problem though: the application code loads a Spring application context from a list of locations (XML files) in the classpath. But when we run our unit tests, we want some of the Spring beans to be mocks instead of full-fledged implementation classes. Moreover, for some unit tests we want some beans to become mocks, while for other unit tests we want other beans to become mocks, as we are testing different layers of the application.
All this means I want to redefine specific beans of the application context and refresh the context when desired. While doing this, I want to redefine only a small portion of the beans located in one (or several) original XML beans definition file. I cannot find an easy way to do it. It's always regarded that Spring is a unit-testing-friendly framework, so I must be missing something here.
Do you have any ideas how to do it?
Thanks!
I would propose a custom TestClass and some easy rules for the locations of the spring bean.xml:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {
"classpath*:spring/*.xml",
"classpath*:spring/persistence/*.xml",
"classpath*:spring/mock/*.xml"})
#Transactional
#TestExecutionListeners({
DependencyInjectionTestExecutionListener.class,
TransactionalTestExecutionListener.class,
DirtiesContextTestExecutionListener.class})
public abstract class AbstractHibernateTests implements ApplicationContextAware {
/**
* Logger for Subclasses.
*/
protected final Logger log = LoggerFactory.getLogger(getClass());
/**
* The {#link ApplicationContext} that was injected into this test instance
* via {#link #setApplicationContext(ApplicationContext)}.
*/
protected ApplicationContext applicationContext;
/**
* Set the {#link ApplicationContext} to be used by this test instance,
* provided via {#link ApplicationContextAware} semantics.
*/
#Override
public final void setApplicationContext(
final ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}
If there are mock-bean.xml in the specified location, they will override all "real" bean.xml files in the "normal" locations - your normal locations might differ.
But … I would never mix mock and non-mock beans, as it's hard to trace problems when the application grows older.
One of the reasons spring is described as test-friendly is because it may be easy to just new or mock stuff in the unit test.
Alternately we have used the following setup with great success, and I think it is quite close to what you want, I would strongly recommend it:
For all beans that need different implementations in different contexts, switch to annotation based wiring. You can leave the others as-is.
Implement the following set of annotations
<context:component-scan base-package="com.foobar">
<context:include-filter type="annotation" expression="com.foobar.annotations.StubRepository"/>
<context:include-filter type="annotation" expression="com.foobar.annotations.TestScopedComponent"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
Then you annotate your live implementations with #Repository, your stub implementations with #StubRepository, any code that should be present in the unit-test fixture ONLY with #TestScopedComponent. You may run into needing a couple more annotations, but these are a great start.
If you have a lot of spring.xml, you will probably need to make a few new spring xml files that basically only contain the component-scan definitions. You'd normally just append these files to your regular #ContextConfiguration list. The reason for this is because you frequently end up with different configurations of the context-scans (trust me, you will make at least 1 more annotations if you're doing web-tests, which makes for 4 relevant combinations)
Then you basically use the
#ContextConfiguration(locations = { "classpath:/path/to/root-config.xml" })
#RunWith(SpringJUnit4ClassRunner.class)
Note that this setup does not allow you to have alternating combinations of stub/live data. We tried this, and I think that resulted in a mess I wouldn't recommend anyone ;) We either wire inn the full set of stubs or the full set of live services.
We mainly use auto-wired stub dependencies when testing gui near stuff where the dependencies are usually quite substantial. In cleaner areas of the code we use more regular unit-testing.
In our system we have the following xml-files for component-scan:
for regular web production
for starting web with stubs only
for integration tests (in junit)
for unit tests (in junit)
for selenium web tests (in junit)
This means we totally have 5 different system-wide configurations that we can start the application with. Since we only use annotations, spring is fast enough to autowire even those unit tests we want wired. I know this is untraditional, but it's really great.
Out integration tests run with full live setup, and once or twice I have decided to get really pragmatic and want to have a 5 live wirings and a single mock:
public class HybridTest {
#Autowired
MyTestSubject myTestSubject;
#Test
public void testWith5LiveServicesAndOneMock(){
MyServiceLive service = myTestSubject.getMyService();
try {
MyService mock = EasyMock.create(...)
myTestSubject.setMyService( mock);
.. do funky test with lots of live but one mock object
} finally {
myTestSubject.setMyService( service);
}
}
}
I know the test purists are going to be all over me for this. But sometimes it's just a very pragmatic solution that turns out to be very elegant when the alternative would be really really ugly. Again it's usually in those gui-near areas.
See this tutorial with #InjectedMock annotation
It saved me a lot of time. You just use
#Mock
SomeClass mockedSomeClass
#InjectMock
ClassUsingSomeClass service
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
and all your problems are solved. Mockito will replace the spring dependency injection with a mock. I just used it myself and it works great.
There are some very complicated and powerful solutions listed here.
But there is a FAR, FAR simpler way to accomplish what Stas has asked, which doesn't involve modifying anything other than one line of code in the test method. It works for unit tests and Spring integration tests alike, for autowired dependencies, private and protected fields.
Here it is:
junitx.util.PrivateAccessor.setField(testSubject, "fieldName", mockObject);
You can also write your unit tests to not require any lookups at all:
#ContextConfiguration(locations = { "classpath:/path/to/test-config.xml" })
#RunWith(SpringJUnit4ClassRunner.class)
public class MyBeanTest {
#Autowired
private MyBean myBean; // the component under test
#Test
public void testMyBean() {
...
}
}
This gives an easy way to mix and match real config files with test config files.
For example, when using hibernate, I might have my sessionFactory bean in one config file (to be used in both the tests and the main app), and have by dataSource bean in another config file (one might use a DriverManagerDataSource to an in-memory db, the other might use a JNDI-lookup).
But, definitely take heed of #cletus's warning ;-)
Easy. You use a custom application context for your unit tests, or you don't use one at all and you manually create and inject your beans.
It sounds to me like your testing might be a bit too broad. Unit testing is about testing, well, units. A Spring bean is a pretty good example of a unit. You shouldn't need an entire application context for that. I find that if your unit testing is so high-level that you need hundreds of beans, database connections etc., you have a really fragile unit test that is going to break on the very next change, will be hard to maintain and really isn't adding a lot of value.
You can use the import feature in your test app context to load in the prod beans and override the ones you want. For example, my prod data source is usually acquired via JNDI lookup, but when I test I use a DriverManager data source so I don't have to start the app server to test.
I don't have the reputation points to pile on duffymo's answer, but I just wanted to chime in and say his was the "right" answer for me.
Instantiate a FileSystemXmlApplicationContext in your unit test's setup with a custom applicationContext.xml. In that custom xml, at the top, do an as duffymo indicates. Then declare your mock beans, non-JNDI data sources, etc, that will override the id's declared in the import.
Worked like a dream for me.
You do not need to use any test contexts (doesn't matter is XML or Java based). Since Spring boot 1.4 there is available new annotation #MockBean which introduced native support for mocking and Spying of Spring Beans.
Perhaps you could use qualifiers for your beans? You would redefine the beans you want to mock up in a separate application context and label them with a qualifier "test". In your unit tests, when wiring your beans always specify the qualifier "test" to use the mock ups.
I want to do the same thing, and we're finding it essential.
The current mechanism we use is fairly manual but it works.
Say for instance, you wish to mock out bean of type Y. What we do is every bean that has that dependency we make implement an interface - "IHasY". This interface is
interface IHasY {
public void setY(Y y);
}
Then in our test we call the util method...
public static void insertMock(Y y) {
Map invokers = BeanFactory.getInstance().getFactory("core").getBeansOfType(IHasY.class);
for (Iterator iterator = invokers.values().iterator(); iterator.hasNext();) {
IHasY invoker = (IHasY) iterator.next();
invoker.setY(y);
}
}
I do not want to create a whole xml file just to inject this new dependency and that is why I like this.
If you're willing to create an xml config file then the way to go would be to create a new factory with the mock beans and make your default factory a parent of this factory. Make sure then that you load all your beans from the new child factory. When doing this the sub-factory will override the beans in the parent factory when the bean id's are the same.
Now if, in my test, If I could programmatically create a factory, that would be awesome. Having to use xml is just too cumbersome. I'm looking to create that child factory with code. Then each test can configure its factory the way it wants. There's no reason why a factory like that won't work.
spring-reinject is designed to substitute beans with mocks.
Since the OP this has come along: Springockito

Resources