Spring 4.12 introduced two JUnit rules: SpringClassRule and SpringMethodRule.
Instead of using @RunWith(SpringRunner.class), we can use these two rules to achieve the same result.
As JUnit framework allows only one Runner in a test class, Spring JUnit rules provide the flexibility to run tests in Spring context environment without using @RunWith(SpringRunner.class), which allows us to use other JUnit runners like Parameterized in the same test class.
Deprecation
Spring 7 deprecated SpringClassRule and SpringMethodRule rules because they were tightly coupled to JUnit 4’s Rule model, which had separate and limited hooks for class-level and method-level test lifecycle management. JUnit 5 introduced a more powerful and unified extension model that provides fine-grained lifecycle callbacks, parameter resolution, and better composition. This allowed Spring to consolidate all test context, dependency injection, and lifecycle handling into a single SpringExtension, eliminating the need for multiple rules and providing a cleaner, more flexible, and more maintainable integration.
Example
Creating a simple Spring application
package com.logicbig.example;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ShoppingCart {
private List<String> orders = new ArrayList<>();
public void addItem(String name, int qty) {
orders.add(String.format("order. Item:%s qty%s", name, qty));
}
public String checkout() {
String msg = placeOrders();
orders.clear();
return msg;
}
public String placeOrders() {
return orders.size() + " orders placed";
}
}
package com.logicbig.example;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.logicbig.example")
public class AppConfig {
}
Writing JUnit test
package com.logicbig.example;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.rules.SpringClassRule;
import org.springframework.test.context.junit4.rules.SpringMethodRule;
@ContextConfiguration(classes = AppConfig.class)
public class ShoppingCartTest {
@ClassRule
public static final SpringClassRule SPRING_CLASS_RULE= new SpringClassRule();
@Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
@Autowired
private ShoppingCart shoppingCart;
@Test
public void testCheckout() {
shoppingCart.addItem("Item1", 3);
shoppingCart.addItem("item2", 5);
String result = shoppingCart.checkout();
System.out.printf("Shopping cart checkout response: %s%n", result);
Assert.assertEquals("2 orders placed", result);
}
}
D:\example-projects\spring-core-testing\spring-core-testing-with-junit-4\spring-junit-rules>mvn -q test Shopping cart checkout response: 2 orders placed
Note:
As seen in the above example, SpringMethodRule should always be annotated with @Rule and SpringClassRule should always be annotated with @ClassRule. That's because SpringMethodRule supports instance level features, whereas, SpringClassRule supports class level features.
Example ProjectDependencies and Technologies Used: - spring-context 4.3.9.RELEASE (Spring Context)
- spring-test 4.3.9.RELEASE (Spring TestContext Framework)
- junit 4.12 (JUnit is a unit testing framework for Java, created by Erich Gamma and Kent Beck)
- JDK 1.8
- Maven 3.9.11
|