https://github.com/knight76/springboot2-restdocs  참고


Spring Rest Docs 및 Junit Test를 진행할 때 아래와 같이 부모 테스트 클래스를 둔 후, 자식 테스트에서 로직만 테스트하게 했더니 좀 편했다.


부모 테스트 클래스 

package com.example.controller;

import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.*;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.*;

import org.junit.Before;
import org.junit.Rule;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.restdocs.JUnitRestDocumentation;
import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

public class ParentTest {

@Rule
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();

@Autowired
WebApplicationContext context;

RestDocumentationResultHandler document;

MockMvc mockMvc;

@Before
public void setUp() {
this.document = document(
"{class-name}/{method-name}",
preprocessResponse(prettyPrint())
);
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration( this.restDocumentation))
.alwaysDo(document)
.build();
}
}





자식 테스트 클래스 

package com.example.controller;

import static org.hamcrest.CoreMatchers.*;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*;
import static org.springframework.restdocs.payload.PayloadDocumentation.*;
import static org.springframework.restdocs.request.RequestDocumentation.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.ResultActions;

@RunWith(SpringRunner.class)
@AutoConfigureRestDocs
@SpringBootTest
public class HelloWorldTest extends ParentTest {

@Test
public void helloWorldJson() throws Exception {
// given
String response = "{\"result\":\"Welcome, Hello World, Samuel\"}";

// when
ResultActions result =
mockMvc.perform(get("/helloworld/json")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.param("name", "Samuel"))
.andDo(print());

// then
result.andExpect(status().isOk())
.andDo(document.document(
requestParameters(
parameterWithName("name").description("이름입니다.")),
responseFields(
fieldWithPath("result").description("결과입니다."))))
.andExpect(jsonPath("result", is(notNullValue())))
.andExpect(content().string(response));
}
}


Posted by '김용환'
,


Spring Test를 수행하다 엄청 삽질한 것 중의 하나가...


Spring Boot에서 Controller(URL) 테스트를 진행할 때, 

Boot를 실행하고 curl json의 형태로 호출하면 제대로 호출되면 동작하는데, 

MockMvc를 사용하면 동작이 안되는 경우가 있었다.



Boot서비스가 실행되는 것과 달리 Spring Test와 MockMvc를 이용할 때는 굉장히 세밀하게 확인한다. 

그래서 JSON을 사용할 때는 반드시 MediaType을 적용하는 게 삽질을 덜하게 한다. 


@RestController
@RequestMapping(path="/api/1",
consumes = MediaType.APPLICATION_JSON_UTF8_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class CitiesController {


Posted by '김용환'
,


nodejs + chrome headless + puppeteer로 캡쳐하는 앱을 테스트해봤다.

https://github.com/knight76/chrome-headless-puppeteer





chrome headless 단독으로 사용하기 어렵고 nodejs의 puppeteer(https://github.com/GoogleChrome/puppeteer)을 같이 써야 한다.


server.js

const http = require('http')
const puppeteer = require('puppeteer')
const url = require('url')

const DEFAULT_WIDTH = 800
const DEFAULT_HEIGHT = 600
const port = 3000

http.createServer(async (request, response) => {
const urlParts = url.parse(request.url, true)
const urlPathname = urlParts.pathname
const urlParams = urlParts.query
const requrl = urlParams['url']

if (!requrl) {
response.writeHead(200)
response.end('Capture Service.')
return
}

let browerWidth, browerHeight
if (urlPathname === '/capture') {
if (parseInt(urlParams['w'], 0) > 0) {
browerWidth = parseInt(urlParams['w'], 0)
}
if (parseInt(urlParams['h'], 0) > 0) {
browerHeight = parseInt(urlParams['h'], 0)
}
} else if (urlPathname === '/check.html') {
response.writeHead(200)
response.end()
} else {
response.writeHead(500)
response.end()
return
}

if (!browerWidth) {
browerWidth = DEFAULT_WIDTH
}
if (!browerHeight) {
browerHeight = DEFAULT_HEIGHT
}

let page
try {
const browser = await puppeteer.launch({
args: ['--no-sandbox']
});

page = await browser.newPage()
await page.setViewport({
width: browerWidth,
height: browerHeight
})

await page.goto(requrl, {
waitUntil: 'networkidle2'
})

const screenshot = await page.screenshot({
type: 'jpeg',
quality: 100
})

console.log("5")
response.writeHead(200, {
'content-type': 'image/jpg',
'cache-control': 'public,max-age=60,immutable'
})

response.end(screenshot, 'binary')
} catch (e) {
console.log(e)
response.writeHead(500, {
'Content-Type': 'text/plain'
})
response.end('UNKNOWN ERROR(500)!!')
} finally {
if (page && page.isClosed() == false) {
await page.close()
}
}
}).listen(port)

console.log(`Capture server running port : ${port}...`)



Dockerfile

FROM zenato/puppeteer
USER root
COPY . /usr/src/app
RUN cd /usr/src/app && npm install
EXPOSE 3000
WORKDIR /usr/src/app
CMD [ "node", "server.js" ]




참고로 파이썬 + cProto(https://pypi.org/project/cproto/)를 사용하려 했으나 파이썬 쪽으로는 이제 scraping은 좀 되는데, 화면 캡쳐는 괜찮은 라이브러리가 없다. 


chrome headless는 nodejs와 잘 맞는 것 같다. 






Posted by '김용환'
,