Better approach to write Junit5 tests for spring boot application - spring-boot

I created a Test class:
void comp() throws IOException{
Foo foo=new Foo()
String file=readFile("xyz.txt")
Model model=foo.generate(file)
Assert.assertEquals(model.getMapA().size(),0);
Assert.assertEquals(model.getMapB().size(),0);
Assert.assertEquals(model.getListC().size(),10);
}
Is this a good approach to writing the Junit test? I mean all the asserts inside one method. An alternate approach I could think of is to have a setup method that will generate the model class and a separate method for checking the size of each attribute. The class Foo is a controller class in a spring boot application.
I am using Junit5. I also saw that Hamcrest is popular, but the last release was on Jul 09, 2012 Which raises a question should we use it or not

The main problem in your test scenario is when the first assertion fails you do not see results of next assertions, I would recommend to start using AssertJ assertions, in that case your assertions would look like (very basic example though):
Model model=foo.generate(file);
SoftAssertions.assertSoftly(softly -> {
softly.assertThat(model.getMapA()).isEmpty();
softly.assertThat(model.getMapB()).isEmpty();
softly.assertThat(model.getListC()).hasSize(10);
});

Related

Sonarqube exclusion on Test Data

Fellow Members,
I am trying to configure Sonarqube on my service.
For tests, I have extracted test Data (setup) into a separate class - testData.java class.
My question is, how should I analyse my testData.java using Sonar. Since Sonar mandates all the files under test package to end with *Test.java. Therefore if I try to rename the file to something say testDataTest.java it asks me to add a Test to the class.
Since my class is a final class and does not contain any test, I have to add a hack to add a meaning less #Test.
I would like to understand what are the best practices here and how could I improvise.
An Example of my class:
#TestConfiguration
#Import({ TestSecurityConfig.class , TestAuthentication.class})
public class ConfigurationSetupForTest{
#Test
#DisplayName("This is a dumb test so Sonar will not give inclusion errors")
void dumbTest() {
assertTrue(true);
}
}
Thanks

Spring test : strange behavior of context config caching between test classes ?

I am writing tests for a Spring Integration project, and I am running into something strange : I've read about how Spring caches the context between tests and how we can force to clean the cache with #DirtiesContext annotation. However, I'm not able to explain the behavior I observe, and it makes me think it's maybe a bug...
I have 2 different tests :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:myInterface-core.xml",
"classpath:myInterface-datasource-test.xml"})
public class PropertyConfigurerTest {
#Test
public void shouldResolvePropertyForOutPutFile(){
}
}
(it does nothing, simply loads the context, intentionnaly)
And another one, more complex with actual tests in it (skipping them in below snippet) :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {
"classpath:myInterface-core.xml",
"classpath:myInterface-rmi.xml",
"classpath:myInterface-datasource-test.xml"})
public class MontranMessagesFlowTest {
...
}
As you can see, these 2 tests don't load exactly the same config : second test loads one extra config file that is not required for first one.
When I run these 2 tests one after the other, second test is failing : in a nutshell, the goal of the test is to insert 2 rows in inMemory DB, start my Spring Integration flow and assert with a listener (inside a jms:listener-container) that I've received 2 JMS messages on the outbound side. I see in debug mode that actually the 2 messages don't go to same listener so I get one message instead of the 2 I expect. Somehow, the fact that I'm loading some elements of the context in first test (even if I don't do anything with them) has an impact on the second test.
I have found 2 different workarounds :
adding #DirtiesContext(classMode=ClassMode.AFTER_CLASS) on my first test.
modify the list of Spring files that I load in my first test, so that it matches exactly the one defined in the second test.
But still, I don't understand the rationale, and it looks like a bug to me.
I am using Spring Test 4.1.4.RELEASE. I've put the minimum code necessary in a separate project to be able to reproduce. I can share it if required.
Does anybody have an explanation for this ? Bug or not ?
Thanks
Vincent
#M. Deinum is correct in his comment.
For what it's worth, in Spring Integration framework tests themselves, we have started adding #DirtiesContext to all tests, to ensure any active components (such as inbound message-driven adapters) are always stopped after the tests complete.
This also has a performance/memory usage improvement for large test suites.

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

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

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.

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