简体   繁体   中英

Integration Tests with PowerMock and Spring Boot

I'm doing some integration tests, on a Spring Boot application.

Usually the integration tests that I was used to develop, was regarding the application domain, without any external service involved. Since this time I need to make an integration test on a service which uses both a database and an external service called by an SDK, I've tried doing something like the following:

@RunWith(PowerMockRunner::class)
@SpringBootTest
@PowerMockRunnerDelegate(SpringRunner::class)
@PrepareForTest(McpProductService::class)
class MyServiceIntegration {

    @Mock
    private ExternalService externalService;

    @Autowired
    @InjectMocks
    private MyServiceImpl myService;

    @Test
    public void thisTestShouldWork() {
        ...
    }
}

What is confusing me is: how should I declare myService attribute? Usually when I use Mockito + PowerMock in my Unit Tests, I usually test the implementation, not the whole Service Interface + Spring Injection. But I can't use @Autowired if I'm using just it's implementation, not the Interface.

Is there any best practice for this issue that I'm facing?

Disclaimer: I'm assuming that what you are after is an end-to-end test of a service interface, backed by multiple classes. I assume (and hope) that you don't have a single class handling both database and webservice integration.

I don't see the need to use PowerMock here, it is usually something one would use for testing legacy code with a lot of static stuff. If you are using Spring boot, your code should be of a quality that makes PowerMock unnecessary.

When writing an end-to-end test, the principles are the same as a per-class unit test, only with a larger scope:

  • With a unit test, you create an instance of the class under test, and mock all its external dependencies (other classes)
  • With an end-to-end test, you create an "instance" of your module under test, and mock its external dependencies.

So, here you should find a mechanism to mock the parts of your code that communicates with external sources, like web service clients, database classes (if you don't use an in-memory db for your test (you should)). This will typically be a Spring config that is almost identical to the one used in production, but with said parts mocked out. Then, you just @Inject the parts you need to communicate with in order to complete the test.

Assuming that you use component scan and annotations for all beans, you could mock the endpoint-classes and use profiles:

This code is based on memory only, might not work on copy-paste, but hopefully you could use the concepts..

@Profile("test")
@Configuration
public class TestConfiguration {
    @Bean
    @Primary
    public SomeWebserviceClient someWebserviceClient() {
        return mock(SomeWebserviceClient.class);
    }
}

Production code:

@Service
public class SomeClass {
    @Inject 
    private SomeWebserviceClient client;
}

Then in the test:

@RunWith(PowerMockRunner::class)
@SpringBootTest
@ActiveProfiles("test")
public class SomeTest {
    @Inject
    private SomeClass someClass;

    @Inject
    private SomeWebserviceClient client; //<< will inject mock
}

Mock will also be injected into SomeClass

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM