How to run the SpringBootTest with only a single bean and with included resilience4j annotations - spring-test

I would like to run an integration test of a single bean with resilience4j annotated method in a spring boot app. My intent is to test resiliency of bean method calls while not loading the full spring context.
The setup is as follows:
Dependencies include the following:
io.github.resilience4j:resilience4j-spring-boot2
io.github.resilience4j:resilience4j-reactor
org.springframework.boot:spring-boot-starter-aop
The resilience4j time limited spring bean with method to test:
#Service
public class FooService {
#TimeLimiter(name = "fooTimeLimiter")
public FooResponse foo() {
//entertain operation that might timeout
}
}
Configuration:
resilience4j.timelimiter.instances.fooTimeLimiter.timeoutDuration=1s
And the test:
#SpringBootTest
#ContextConfiguration(classes = FooService.class)
public class FooServiceIT {
#Autowired
private FooService service;
#MockBean
private Bar bar;
#Test
void foo_timeout() {
//setup mocks so the operation delays the output and shall end up with timeout
Assertions.assertThrows(TimeoutException.class, () -> service.foo());
}
}
However, the TimeLimiterAdvice.proceed() is not entertained, no timeout exception is thrown and the test fails.
Same question has been asked here: Testing SpringBoot with annotation-style Resilience4j but there is no solution.
I tried both approaches - implement FooService interface and program directly using the concrete class. With the same result.
How can I achieve the time limiter annotation is taken into account in my test?
Edit: I even tried plain spring test (no spring boot) with the following setup:
#ExtendWith(SpringExtension.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class FooServiceIT {
#Configuration
#Import({TimeLimiterConfiguration.class, FallbackConfiguration.class, SpelResolverConfiguration.class})
static class ContextConfiguration {
#Bean
public FooService fooService() {
//prepare bean;
}
#Bean
public TimeLimiterConfigurationProperties timeLimiterConfigurationProperties() {
return new TimeLimiterConfigurationProperties();
}
}
#Autowired
private FooService service;
//tests...
}
Same result (i.e. no timeout exception).

When dealing with SpringBootTest and #CircuitBreaker, it was sufficient to add #EnableAspectJAutoProxy annotation to the test. After this change, the CircuitBreakerAspect was entertained and the test behaves as expected.
In order to make #TimeLimiter working as expected, one need to add #Bulkhead annotation to the method as well.
The updated method looks as follows:
#Bulkhead(name = "fooBulkhead", type = Type.THREADPOOL)
#CircuitBreaker(
name = "fooCircuitBreaker",
fallbackMethod = "fooFallback"
)
#TimeLimiter(
name = "fooTimeLimiter"
)
public CompletableFuture<FooResponse> foo() {
//...
}
and the test:
#SpringBootTest(classes = FooService.class)
#EnableAspectJAutoProxy
#Import(value = {CircuitBreakerAutoConfiguration.class, TimeLimiterAutoConfiguration.class, BulkheadAutoConfiguration.class})
public class FooServiceIT {
//...
}

Related

repository always null after initilization of testing containers

I am attempting to use TestingContainers. I was able to get it to run but my tests are always null. I am trying to avoid mocking but rather having real data.
Repository
#Sql("classpath:data.sql")
class OrderDataRepositoryTest extends AbstractTestConfiguration {
//#Mock
#MockBean
//#Autowired
private OrderDataRepository orderRepository;
private AutoCloseable closeable;
#BeforeEach
public void init() {
closeable = MockitoAnnotations.openMocks(this);
}
#AfterEach
void closeService() throws Exception {
closeable.close();
}
#Test
void getAllUsersTest() {
List<Order> orders = orderRepository.findAll();
orders.toString();
}
}
config
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
#Testcontainers
public abstract class AbstractTestConfiguration {
#Container
private MySQLContainer database = new MySQLContainer("mysql:8.0");
#Test
public void test() {
assertTrue(database.isRunning());
}
}
main
#SpringBootTest
#Sql("classpath:init.sql")
#TestPropertySource("classpath:application-test.yml")
class TentingContainerApplicationTests {
}
application.properties
spring:
application:
datasource:
url: jdbc:mysql:8.0:///test?TC_INITSCRIPT=file:src/main/resources/init.sql
driver-class-name: com.mysql.jdbc.Driver
The commented out
//#Mock
#MockBean
//#Autowired
is what I tried. Of course mock works out but I want real data for the #services and #repository classes.
advice?
If you want to test your database-related code in isolation (I assume you're using Spring Data JPA) then #DataJpaTest fits perfectly.
This annotation will create a sliced Spring context for you that contains only persistence relevant beans like: DataSource, EntityManager, YourRepository. This doesn't include your service classes, your #Component classes, or #RestController.
By default, this annotation tries to configure an embedded in-memory database as the DataSource. We can override this (and you already did with some of your code examples) behavior to use Testcontainers:
#DataJpaTest
#Testcontainers
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class OrderDataRepositoryTest {
#Container
static MySQLContainer database = new MySQLContainer("mysql:8.0");
#DynamicPropertySource
static void setDatasourceProperties(DynamicPropertyRegistry propertyRegistry) {
propertyRegistry.add("spring.datasource.url", database::getJdbcUrl);
propertyRegistry.add("spring.datasource.password", database::getPassword);
propertyRegistry.add("spring.datasource.username", database::getUsername);
}
#Autowired
private OrderDataRepository orderRepository;
#Test
void shouldReturnOrders() {
}
}
If you want to write another test that includes all your beans and also starts the embedded servlet container, take a look at #SpringBootTest for writing integration tests.
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
#Testcontainers
class MyIntegrationTest {
#Container
static MySQLContainer database = new MySQLContainer("mysql:8.0");
#DynamicPropertySource
static void setDatasourceProperties(DynamicPropertyRegistry propertyRegistry) {
propertyRegistry.add("spring.datasource.url", database::getJdbcUrl);
propertyRegistry.add("spring.datasource.password", database::getPassword);
propertyRegistry.add("spring.datasource.username", database::getUsername);
}
#Autowired
private ServiceA serviceA;
#Autowired
private OrderDataRepository orderDataRepository;
}
When working with a Spring TestContext for your test and Mockito, make sure to understand the difference between #Mock and #MockBean.

How do you Unit Test a ForeachWriter implementation?

I've been trying to setup some unit tests to verify the logic in a ForeachWriter custom implementation but am running into a bit of mocking / duplication trouble.
I'd like to Mock an injected dependency in the ForeachWriter, but my mocks seem to be duplicated during execution. Originally I thought the mocked dependencies weren't getting called, but during debug inspection I've found that multiple versions of them seem to exist (based on hashCode).
Here's some quick sample code of what I've been trying to do:
//Class I'd like to test
public class TestForeachSink extends ForeachWriter<String> {
#Inject
SomeDependency dep;
public TestForeachSink(SomeDependency dep) {
this.dep = dep;
}
#Override
public boolean open(long partitionId, long version) {
dep.doSomethingStartupRelatedOrThrow();
return true;
}
#Override
public void process(String value) {
dep.processSomething(value);
}
#Override
public void close(Throwable errorOrNull) {
dep.closeConnections();
}
}
//Testing Class
public class TestForeachSinkTests {
#Mock SomeDependency _dep;
TestForeachSink target;
#BeforeEach
public void init() {
_dep = mock(SomeDependency.class, withSettings().serializable());
target = new TestForeachSink(_dep);
}
#Test
pubic void shouldVerifyDependencyInteractions() {
//setup stream, add data to it
stream.toDS().writeStream().foreach(target).start().processAllAvailable();
//VERIFY INTERACTIONS WITH MOCK HERE
}
}
The added data runs through the stream as expected but it seems like the mock I've passed in of SomeDependency is replaced during execution with a copy. I think that makes sense if the execution is running as though it were performing on a separate worker, but I'd still like to be able to test the ForeachWriter.
Is anyone else testing this part of the code? I haven't come across any other tests for ForeachSink custom implementations but direction on moving forward would be very appreciated!

JukitoRunner, bind mock of final class

How to bind mock of final class in Jukito ?
For example :
public final class SomeFinalClass(){
public SomeFinalClass(String someString){
}
}
//Testing class
#Runwith(JukitoRunner.class)
public class TestingClass(){
#Inject
private SomeFinalClass someFinalClassMock;
public static class TestModule extends JukitoModule {
#Override
protected void configureTest() {
// bind(SomeClient.class).in(TestSingleton.class);
}
#Provides
public SomeFinalClass getSomkeFinalClass() {
return Mokito.mock(SomeFinalClass.class); //throws error
}
}
}
Is there a way i can use PowerMockito with JukitoRunner ?
You can mock a final class if you're using Mockito 2. From Mockito 2 Wiki:
Mocking of final classes and methods is an incubating, opt-in feature. It uses a combination of Java agent instrumentation and subclassing in order to enable mockability of these types. As this works differently to our current mechanism and this one has different limitations and as we want to gather experience and user feedback, this feature had to be explicitly activated to be available ; it can be done via the mockito extension mechanism by creating the file src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker containing a single line: mock-maker-inline.
After you created this file, Mockito will automatically use this new engine and one can do :
final class FinalClass {
final String finalMethod() { return "something"; }
}
FinalClass concrete = new FinalClass();
FinalClass mock = mock(FinalClass.class);
given(mock.finalMethod()).willReturn("not anymore");
assertThat(mock.finalMethod()).isNotEqualTo(concrete.finalMethod());

How can I use multiple mocks of an interface for a Java EE "#Inject private Instance<T> implementations"?

I have an interface:
public interface Service {
void doService();
}
And a class:
public class ServiceUser {
#Inject
#Any
private Instance<Service> serviceImplementations;
public void work() {
serviceImplementations.forEach(service -> service.doService());
}
}
And a testcase:
public class ServiceUserTest {
#Mock
private Service firstImpl;
#Mock
private Service secondImpl;
#InjectMocks
private ServiceUser serviceUser;
#Test
public void testAllImplementationsCalled() {
serviceUser.work();
verify(firstImpl).doService();
verify(secondImpl).doService();
}
}
When I run the testcase, I get a NullPointerException in ServiceUser, where on debugging serviceImplementations is null. If I create two fields in ServiceUser I can get the two instances injected (both mocks are injected, I checked during debugging).
public class ServiceUser {
#Inject
#Any
private Instance<Service> serviceImplementations;
#EJB
private Service firstImpl;
#EJB
private Service secondImpl;
public void work() {
serviceImplementations.forEach(service -> service.doService());
}
}
How can I make this work?
Surprisingly, I couldn't find a good existing fake or test double for Instance<T>, as in Instance<Service> serviceInstance = TestingInstance.of(firstImpl, secondImpl);.
You might want to create one—not using Mockito, but rather as an actual implementation—and test and use it. The interface is relatively small, particularly if you just throw UnsupportedOperationException on the select methods, and then you'll have an implementation you can use in all of your test cases.
Because it's not a mock, you would need an alternative to #InjectMocks, though; given the fragility of #InjectMocks as dependencies change, this may not be a bad thing anyway.

i want to launch a class method periodically using spring

i have the following code.
#Configuration
#EnableAsync
#EnableScheduling
public class AsyncConfiguration implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(10000);
executor.setThreadNamePrefix("Executor-");
executor.initialize();
return executor;
}
}
and if i want to run the recommend method after every certain interval of time. What can be the java spring bean configuration way to do that.?
public class UserBrandsRecommender {
public List<RecommendedItem> recommend(Long userId, int number) throws TasteException{
}
}
You should look into the #Scheduled annotation. For example:
#Scheduled(fixedDelay=5000)
public void doSomething() {
// something that should execute periodically
}
You'll probably need to create a new Spring bean with a method similar to above. The bean could have the UserBrandsRecommender injected into it. The new bean will need to implement some logic to pass proper values for the "userId" and "number" parameters to the "recommend" method.
More information here:
http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/htmlsingle/#scheduling-annotation-support

Resources