How to stub Spring repository to throw an exception in an integration test?

Viewed 2462

I have a service which has a repository as a constructor's parameter.

@Autowired
NodeServiceListInstallService( final BykiListRepository aBykiListRepository )

The BykiListRepository is default Spring repository without implementation

interface BykiListRepository extends JpaRepository<BykiList, Long> {
    //some magic methods
}

My configuration class marked with @EnableJpaRepositories, so, I haven't direct bean's declaration. Service declaration:

@SpringBootApplication
@EnableConfigurationProperties( ApplicationProperties )
@EnableJpaRepositories
@EnableTransactionManagement
@ImportResource( 'classpath:META-INF/spring/application-context.xml' )
class Application extends SpringBootServletInitializer {
    @Bean
    NodeServiceListInstallService nodeServiceListInstallService( final BykiListRepository bykiListRepository ) {
        new NodeServiceListInstallService( bykiListRepository )
    }
}

I'm trying to write a test within of the call of the repository's method save will thrown an exception PersistenceException. I've tried to stub/spy a a repository and declare it as a bean in @TestConfiguration with @Primary, or even implement the interface. But I haven't got the result.

@TestConfiguration
class TestConfig {
    @Bean
    BykiListRepository bykiListRepository() {
        //return Spock's Spy/Stub or new BykiRepositoryBadImpl()
    }

Test:

@ContextConfiguration( classes = TestConfig )
class GlobalErrorHandlerIntegrationTest extends BaseFlowIntegrationTest {
    //test()
}

I write on Groovy-2.4.12 and write tests with Spock-1.1. Spring Boot 1.5.4. Reserved variant is to use aspect, but there's not exactly what I wish. Will be very grateful for help.

Update: Usage of DetachedMockFactory:

Configuration:

@TestConfiguration

class DummyConfiguration {
    private final detachedFactory = new DetachedMockFactory()
    @Bean
    @Primary
    BykiListRepository bykiListRepository() {
        detachedFactory.Mock( BykiListRepository )
    }
}

Test's skeleton:

@SpringBootTest( classes = DummyConfiguration )
@Import( [DummyConfiguration] )
@ContextConfiguration( classes = DummyConfiguration )
class GlobalErrorHandlerIntegrationTest extends BaseFlowIntegrationTest {
    @Autowired
    BykiListRepository bykiListRepositoryMock
    def 'exercise error handling'() {
        given: 'the failing repository'
        bykiListRepositoryMock.save( _ ) >> {
            throw new CannotCreateTransactionException()
        }
        when: 'the message is send to rabbit'
        rabbitOperations.send( configuration.rabbitExchangeName, '', msg )
    }
}

where:

@SpringBootTest( webEnvironment = SpringBootTest.WebEnvironment.NONE )
@ContextConfiguration( classes = Application )
class BaseFlowIntegrationTest extends AbstractIntegrationTest {...}

And

@Category( InboundIntegrationTest )
abstract class AbstractIntegrationTest extends Specification {...}
3 Answers

You can create test configuration like below and using Spock record when calling some function then ex will be thrown. Of course, use @Inject or @Autowire in test class... and do @Import([IntegrationTestMockingConfig])

@TestConfiguration
class IntegrationTestMockingConfig {

     private DetachedMockFactory factory = new DetachedMockFactory()

     @Bean
     ExternalRepository externalRepository() {
         factory.Mock(ExternalRepository)
     }
}

The problem was because I had wrong bean name. I've changed it to the bykiListRepositoryMock (instead of bykiListRepository) and it've solved the problem.

If you want to throw an exception using your mocked repository -

when(repository.yourMethodName(anyString(), anyString(), anyString())).thenThrow(new PersistenceException("Persistence exception occurred"));

If you want to throw any specific persistence exception then you can also throw. https://www.objectdb.com/api/java/jpa/exceptions

Related