Running JUnit with both Spring and Parameterized - java

I have certain integration tests that require the Spring context and will benefit greatly from parameterized testing (will remove lots of duplicate code). I currently have the test running with the class annotation
#RunWith(Parameterized.class)
and I load up the Spring context with
#ClassRule
public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule();
#Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
The above works, but to be frank, I have no clue what it's actually doing. Can anyone explain, and are there any other ways to run parameterized tests simultaneously with Spring?

Any IDE will allow you to jump to the definitions of the SPring rules and debug them. Same for the JUnit Parametrized class.
A JUnit Rule does preparation before and after each method, and before and each after class.
A JUnit Runner does preparation before and after each method, and before and each after class, then runs all methods
Both Spring test rules do the same preparation / cleanup as the Spring Runner would do.
As you can see, rule and runners do mostly the same, the only reason there is a Spring runner is for convenience.
Other ways to run this reasonably would be using JUnit5 or TestNG maybe, instead of JUnit4. Or probably there is a way to setup all spring context in an #Before method rather than using annotations.
But really what you have been doing is the recommended way to combine Spring setup with parametrized tests.

Related

#Rule annotation is not considered in spock unit tests using Junit5

I'm trying to migrate my project to use Junit5. So far I've been using a class "LogSpy" that basically intercepts and saves all the logs so they can be tested easily. Using Junit4 and Spock test I was able to initialize my log interceptor class by using the #Rule annotation (even though it is in a Spock test). After migrating to Junit5 this annotation doesn't seem to initialize the needed log interceptor class and I can't find the reason why. Why did this happen? What are the differences between Junit4 and 5 regarding the #Rule annotation? Is there a way around this issue?
This is how I initialize the LogSpy class. It initializes in JUnit unit tests but not in Spock tests.
#Rule
public LogSpy logSpy = new LogSpy()
From the release notes
JUnit 4 Rules are not supported by spock-core anymore, however, there is a new spock-junit4 module that provides best effort support to ease migration.
In short add the spock-junit4 dependency, if you still need to use JUnit 4 rules.
In the long term, I would suggest to migrate to Spock native extensions.

Use one spring boot context through all SpringBootTests

I want to be able to cache application context through different classes with tests using junit.
Test classes are declared this way:
#SpringBootTest
#RunWith(SpringRunner.class)
public class SomeIntegrationTest {
}
I saw this question Reuse spring application context across junit test classes but in this case I don't use any xml and I want to start context completely, not just few beans from it, so #SpringBootTest is more suitable than #ContextConfiguration, if I got it right.
Ruslan, so your question is on how to reuse the Spring Boot Context for a JUnit Suite, right?
Then, it's almost out-of-the-box provided, you just need to annotate each unit test with the #SpringBootTest annotation.
Also make sure that your main #SpringBootApplication class is loading all the necessary #Configuration classes, this process will be automatically done if the #SpringBootApplication is on the root package above all configuration class and with the inherited #ComponentScan will load up all of them.
From the Spring Boot Testing documentation:
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.
The Spring TestContext framework stores application contexts in a static cache. This means that the context is literally stored in a static variable. In other words, if tests execute in separate processes the static cache will be cleared between each test execution, and this will effectively disable the caching mechanism.
To benefit from the caching mechanism, all tests must run within the same process or test suite. This can be achieved by executing all tests as a group within an IDE
From the Spring Testing documentation:
By default, once loaded, the configured ApplicationContext is reused for each test. Thus the setup cost is incurred only once per test suite, and subsequent test execution is much faster. In this context, the term test suite means all tests run in the same JVM
Check this urls:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/integration-testing.html#testcontext-ctx-management-caching
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/integration-testing.html#testing-ctx-management
Main Takeaways:
Annotate each unit test with #SpringBootTest
Load all beans and necessary configuration classes in your main #SpringBootApplication class
IMPORTANT: Run a JUnit Suite, not a single JUnit test. Execute all tests as a group within your IDE.
#SpringBootTest provides ApplicatonContext caching and sharing in Junit Test Cases naturally.
While some cases may be exceptions. For example, using #MockBean or #SpyBean in a test class will cause the ApplicatonContext cache failure.
While Spring’s test framework caches application contexts between
tests and reuses a context for tests sharing the same configuration,
the use of #MockBean or #SpyBean influences the cache key, which will
most likely increase the number of contexts.
https://docs.spring.io/spring-boot/docs/2.1.5.RELEASE/reference/html/boot-features-testing.html
And this kind of issue is still not solved in the latest SpringBoot release.
https://github.com/spring-projects/spring-boot/issues/21099
https://github.com/spring-projects/spring-boot/issues/7174

Mockito and JUnit : Which is the best to use Runner or TestRule?

I'm writing some tests with JUnit and Mockito.
I've noticed that Mockito provides a JUnit Runner and a JUnit TestRule
Which are the pros and cons of each solution ?
In general, a rule provides more flexibility than a runner. There can be only one runner, whereas you can have multiple rules in one test class.
Since Mockitos runner and rule apparently do the same I don't see a reason to use the runner here.
For the sake of completeness, I'd like to mention that there is no need to use Mockito's rules (or runners) unless you want to use mock annotations or validateMockitoUsage().
For consistency I usually create all mocks with mock() as quite often tests have mocked fields as well as mocked local variables.

Why ExpectedException is deprecated in Spring

On the one side, there is a new #RunWith annotation that lets to change unit test framework on the fly.
But on the other side Spring documentations says about org.springframework.test.annotation.ExpectedException:
#deprecated as of Spring 3.1 in favor of using built-in support for declaring expected exceptions in the underlying testing framework (e.g., JUnit, TestNG, etc.)
As a result my code will depend on the unit test framework. Please explain it.
And the 2nd question. At the moment I implement tests with Spring #RunWith annotation. But I also add the jUnit specific org.junit.Test annotation to each test method. Again, if I understand correctly the best way - to write tests, so I could change for example jUnit onto TestNg. And Spring #RunWith helps me to do that. But how can I avoid using of the org.junit.Test annotation?
#RunWith isn't a Spring annotation. It's a JUnit one. It doesn't let you switch between JUnit and TestNG, as you seem to think. Instead, it lets you run JUnit tests in different ways, like with the addition of the Spring Test Framework. In that framework, Spring has provided ExpectedException for some time, but it's no longer needed because recent versions of both JUnit and TestNG provide that functionality now.
You cannot write a test which can run on both JUnit and TestNG, so your code is bound to be dependent on testing framework. #RunWith is not a Spring's annotation for running tests with different testing frameworks, it belongs to JUnit and used to run JUnit with other runners like SpringJUnit4Runner to extend JUnit functionality

what is AbstractTestNGSpringContextTests

I have a TestNG class which is like the following:
public class WebAPITestCase extends AbstractTestNGSpringContextTests{.....}
I was trying to understand what this means extends AbstractTestNGSpringContextTests.
How does it work and what is the use of it?
Please read the javadoc:
AbstractTestNGSpringContextTests is an abstract base test class that
integrates the Spring TestContext Framework with explicit
ApplicationContext testing support in a TestNG environment. When you
extend AbstractTestNGSpringContextTests, you can access a protected
applicationContext instance variable that you can use to perform
explicit bean lookups or to test the state of the context as a whole.
Basically a spring application context will be setup for the test class.
If that still doesn't make sense I'd recommend you read this.
First, TestNG (stands for Test Next Generation) is a testing framework inspired from JUnit and NUnit but introducing some new functionalities that make it more powerful and easier to use like test that your code is multithread safe, powerful execution model, etc.
The class AbstractTestNGSpringContextTests includes the spring ApplicationContext. To make it available when executing TestNG test, AbstractTestNGSpringContextTests has methods annotated with TestNG annotations like #BeforeClass and #BeforeMethod.
So to have this functionality of running TestNG with Spring components, all it left to do is to extend AbstractTestNGSpringContextTests.
BTW, AbstractTransactionalTestNGSpringContextTests extends AbstractTestNGSpringContextTests. It not only provides transactional support but also has some convenience functionality for JDBC access.

Categories

Resources