I tried to mock three APIs provided by ShortPixel with Wiremock.
For the simplest read domain API, https://kb.shortpixel.com/knowledge-base/article/shortpixels-cdn-api-endpoints/#1-toc-title
To check the status of a domain and its quota, you should use the following.
https://no-cdn.shortpixel.ai/read-domain/example.com
The statuses can be
- 2 = All OK
- 1 = Credits almost exhausted
- -1 = Credits exhausted
- -2 = Credits used up some time ago (can be used e.g. to stop using CDN links)
- -3 = Domain not reachable
I created the following client codes for it.
val domainResult = noCdnWebClient
.get().uri(readDomainPath)
.awaitExchange {
if (it.statusCode().isError) {
throw ImageOptimizerException("Failed to check domain $domain on shortpixel, ${it.awaitBody<String>()}")
}
it.awaitBody(Int::class)
}
There is a little specificity here. I put the above codes into a @PostConstruct
method to check the domain's existence. If the domain is not bound to the APIKEY, then call /add-domain
to bind it.
When using the following stub to mock the request/response.
stubFor(
get(urlPathEqualTo(readDomainPath))
.willReturn(
aResponse()
.withStatus(200)
.withBody("-3")
)
)
When running the test I got the exception info like:
HTTP GET http://localhost:9000/read-domain/localhost
Response 404 NOT_FOUND
Decoded "No response could be served as there are no stub mappings in this WireMock instance."
I am afraid the Wiremock server is not ready when the component is initialized.
The problem is when running the tests, the bean which init method called remote APIs is invoked before Wiremock is ready.
The simplest approach is to adjust the testing codes slightly, make the bean not managed by the Spring application context in your tests, and create an instance manually.
@SpringBootTest(classes=[MyTest.TestConfig.class])
@Wiremock
class MyTest{
@Configuration
@Import...// import dependencies (A, B)
@ImportAutoConfiguration...
class TestConfig() // do not contain the bean in the config
@Autowired lateinit var depA:A
@Autowired lateinit var depB:B
// before running the tests, wiremock is ready now.
// in the test, create the bean instance manually, called the init method and
// execute other tasks
val bean = MyBean(depA, depB)
bean.init()
bean.doOtherThings()
}