Skip to content

Commit a0c80ff

Browse files
JeongA-Shinbinimini
authored andcommitted
feat : 소켓으로 채팅방 구현 테스트 코드 추가
#15
1 parent cd52041 commit a0c80ff

File tree

5 files changed

+312
-12
lines changed

5 files changed

+312
-12
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package oncoding.concoder.config;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import java.util.concurrent.ExecutionException;
6+
import java.util.concurrent.TimeUnit;
7+
import java.util.concurrent.TimeoutException;
8+
import org.junit.jupiter.api.AfterEach;
9+
import org.junit.jupiter.api.BeforeEach;
10+
import org.junit.jupiter.api.Test;
11+
import org.junit.jupiter.api.extension.ExtendWith;
12+
import org.springframework.boot.test.context.SpringBootTest;
13+
import org.springframework.boot.test.web.server.LocalServerPort;
14+
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
15+
import org.springframework.messaging.simp.stomp.StompSession;
16+
import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter;
17+
import org.springframework.test.context.ActiveProfiles;
18+
import org.springframework.test.context.junit.jupiter.SpringExtension;
19+
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
20+
import org.springframework.web.socket.messaging.WebSocketStompClient;
21+
import org.springframework.web.socket.sockjs.client.SockJsClient;
22+
import org.springframework.web.socket.sockjs.client.Transport;
23+
import org.springframework.web.socket.sockjs.client.WebSocketTransport;
24+
25+
@ExtendWith(SpringExtension.class)
26+
@ActiveProfiles("local")
27+
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
28+
public class WebSocketConnTest {
29+
30+
protected StompSession stompSession;
31+
32+
@LocalServerPort
33+
private int port;
34+
35+
private String url;
36+
37+
private WebSocketStompClient websocketClient;
38+
39+
@BeforeEach
40+
void StompSupport() {
41+
this.websocketClient = new WebSocketStompClient(new SockJsClient(createTransport()));
42+
this.websocketClient.setMessageConverter(new MappingJackson2MessageConverter());
43+
this.url = "ws://localhost:";
44+
}
45+
46+
@Test
47+
public void connect() throws ExecutionException, InterruptedException, TimeoutException {
48+
this.stompSession = this.websocketClient
49+
.connect(url + port + "/api/ws-connection", new StompSessionHandlerAdapter() {})
50+
.get(3, TimeUnit.SECONDS);
51+
}
52+
53+
@AfterEach
54+
public void disconnect() {
55+
if (this.stompSession.isConnected()) {
56+
this.stompSession.disconnect();
57+
}
58+
}
59+
60+
private List<Transport> createTransport() {
61+
List<Transport> transports = new ArrayList<>(1);
62+
transports.add(new WebSocketTransport(new StandardWebSocketClient()));
63+
return transports;
64+
}
65+
66+
}
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,86 @@
11
package oncoding.concoder.config;
22

33
import static io.restassured.RestAssured.given;
4-
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
4+
import static org.assertj.core.api.Assertions.assertThat;
55

6+
import io.restassured.RestAssured;
67
import io.restassured.response.ExtractableResponse;
78
import io.restassured.response.Response;
9+
import org.junit.jupiter.api.BeforeEach;
810
import org.junit.jupiter.api.DisplayName;
911
import org.junit.jupiter.api.Test;
1012
import org.springframework.boot.test.context.SpringBootTest;
13+
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
14+
import org.springframework.boot.web.server.LocalServerPort;
1115
import org.springframework.http.HttpStatus;
1216
import org.springframework.test.context.ActiveProfiles;
1317

14-
@SpringBootTest
18+
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
1519
@ActiveProfiles("local")
16-
class WebSocketConnectTest {
20+
public class WebSocketConnectTest {
21+
/*
22+
private StompSession stompSession;
1723
18-
@DisplayName("소켓 연결 테스트")
24+
@LocalServerPort
25+
private int port;
26+
27+
private String url;
28+
29+
private WebSocketStompClient webSocketStompClient;
30+
31+
private List<Transport> createTransport() {
32+
List<Transport> transports = new ArrayList<>(1);
33+
transports.add(new WebSocketTransport(new StandardWebSocketClient()));
34+
return transports;
35+
}
36+
37+
@BeforeEach
38+
public void setUp() {
39+
this.webSocketStompClient = new WebSocketStompClient(new SockJsClient(createTransport()));
40+
this.webSocketStompClient.setMessageConverter(new MappingJackson2MessageConverter());
41+
this.url = "ws://localhost:";
42+
//RestAssured.port = port;
43+
}
44+
45+
@DisplayName("웹 소켓 연결을 시도한다.")
1946
@Test
20-
void webSocketConnectTest(){
47+
void connect() throws ExecutionException, InterruptedException, TimeoutException {
48+
49+
System.out.println("websocket url: "+url + port + "/ws-connection");
50+
51+
this.stompSession = this.webSocketStompClient
52+
.connect(url + port + "/ws-connection", new StompSessionHandlerAdapter() {})
53+
.get(3, TimeUnit.SECONDS);
54+
}
55+
56+
@AfterEach
57+
@Test
58+
public void disconnect() {
59+
if (this.stompSession.isConnected()) {
60+
this.stompSession.disconnect();
61+
}
62+
}
63+
64+
*/
65+
@LocalServerPort
66+
private int port;
67+
68+
@BeforeEach
69+
public void setUp() {
70+
RestAssured.port = port;
71+
}
72+
73+
@DisplayName("웹 소켓 연결을 시도한다.")
74+
@Test
75+
void webSocketConnectTest() {
2176
ExtractableResponse<Response> response = given().log().all()
22-
.when()
23-
.get("/ws-connection")
24-
.then().log().all()
25-
.extract();
77+
.when()
78+
.get("/api/ws-connection")
79+
.then().log().all()
80+
.extract();
2681

2782
// then
2883
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value());
2984
}
30-
}
85+
86+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package oncoding.concoder.controller;
2+
3+
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
4+
5+
import io.restassured.RestAssured;
6+
import java.util.Collections;
7+
import java.util.List;
8+
import java.util.concurrent.BlockingQueue;
9+
import java.util.concurrent.ExecutionException;
10+
import java.util.concurrent.LinkedBlockingDeque;
11+
import java.util.concurrent.TimeUnit;
12+
import java.util.concurrent.TimeoutException;
13+
import oncoding.concoder.dto.ChatDTO.MessageRequest;
14+
import oncoding.concoder.dto.ChatDTO.MessageResponse;
15+
import oncoding.concoder.dto.ChatDTO.SessionRequest;
16+
import oncoding.concoder.dto.ChatDTO.SessionResponse;
17+
import oncoding.concoder.dto.ChatDTO.UserResponse;
18+
import oncoding.concoder.model.Room;
19+
import oncoding.concoder.model.User;
20+
import oncoding.concoder.repository.RoomRepository;
21+
import oncoding.concoder.repository.UserRepository;
22+
import org.junit.jupiter.api.AfterEach;
23+
import org.junit.jupiter.api.BeforeEach;
24+
import org.junit.jupiter.api.DisplayName;
25+
import org.junit.jupiter.api.Test;
26+
import org.springframework.beans.factory.annotation.Autowired;
27+
import org.springframework.boot.test.context.SpringBootTest;
28+
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
29+
import org.springframework.boot.test.web.server.LocalServerPort;
30+
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
31+
import org.springframework.messaging.simp.stomp.StompSession;
32+
import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter;
33+
import org.springframework.test.context.ActiveProfiles;
34+
import org.springframework.util.concurrent.ListenableFuture;
35+
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
36+
import org.springframework.web.socket.messaging.WebSocketStompClient;
37+
import org.springframework.web.socket.sockjs.client.SockJsClient;
38+
import org.springframework.web.socket.sockjs.client.Transport;
39+
import org.springframework.web.socket.sockjs.client.WebSocketTransport;
40+
41+
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
42+
@ActiveProfiles("local")
43+
class ChattingControllerTest {
44+
45+
@LocalServerPort
46+
private int port;
47+
48+
private BlockingQueue<SessionResponse> users;
49+
private BlockingQueue<MessageResponse> messages;
50+
51+
@Autowired
52+
private UserRepository userRepository;
53+
54+
@Autowired
55+
private RoomRepository roomRepository;
56+
57+
private void 유저_초기화(){
58+
userRepository.deleteAll();
59+
}
60+
61+
private void 방_초기화(){
62+
roomRepository.deleteAll();
63+
}
64+
65+
private void 유저_삽입() {
66+
userRepository.save(new User("와일더"));
67+
userRepository.save(new User("마이클"));
68+
userRepository.save(new User("제이슨"));
69+
userRepository.save(new User("오스카"));
70+
}
71+
72+
private void 방_생성() {
73+
roomRepository.save(new Room(2));
74+
roomRepository.save(new Room(4));
75+
roomRepository.save(new Room(5));
76+
}
77+
78+
79+
@BeforeEach
80+
public void setUp() {
81+
RestAssured.port = port;
82+
유저_초기화();
83+
방_초기화();
84+
users = new LinkedBlockingDeque<>();
85+
messages = new LinkedBlockingDeque<>();
86+
유저_삽입();
87+
방_생성();
88+
}
89+
90+
private WebSocketStompClient 웹_소켓_STOMP_CLIENT() {
91+
StandardWebSocketClient standardWebSocketClient = new StandardWebSocketClient();
92+
WebSocketTransport webSocketTransport = new WebSocketTransport(standardWebSocketClient);
93+
List<Transport> transports = Collections.singletonList(webSocketTransport);
94+
SockJsClient sockJsClient = new SockJsClient(transports);
95+
96+
return new WebSocketStompClient(sockJsClient);
97+
}
98+
99+
100+
@DisplayName("유저가 입장하고 메시지를 보내면 해당 방에 메시지가 브로드 캐스팅된다.")
101+
@Test
102+
void enterUserAndBroadCastMessage()
103+
throws InterruptedException, ExecutionException, TimeoutException {
104+
Room room = roomRepository.findAll().get(0);
105+
User user = userRepository.findAll().get(0);
106+
107+
//expected result
108+
UserResponse expectedUser = UserResponse.from(user);
109+
MessageResponse expectedMessage = new MessageResponse(user.getId(), "채팅을 보내 봅니다.");
110+
111+
112+
// Settings
113+
WebSocketStompClient webSocketStompClient = 웹_소켓_STOMP_CLIENT();
114+
webSocketStompClient.setMessageConverter(new MappingJackson2MessageConverter());
115+
116+
117+
// Connection
118+
ListenableFuture<StompSession> connect = webSocketStompClient
119+
.connect("ws://localhost:" + port + "/api/ws-connection", new StompSessionHandlerAdapter() {
120+
});
121+
StompSession stompSession = connect.get(60, TimeUnit.SECONDS);
122+
123+
stompSession.subscribe(String.format("/sub/rooms/%s", room.getId()), new StompFrameHandlerImpl(new SessionResponse(), users));
124+
stompSession.send(String.format("/pub/rooms/%s", room.getId()), new SessionRequest(user.getId(), "1A2B3C4D"));
125+
126+
stompSession.subscribe(String.format("/sub/rooms/%s/chat", room.getId()), new StompFrameHandlerImpl(new MessageResponse(), messages));
127+
stompSession.send(String.format("/sub/rooms/%s/chat", room.getId()), new MessageRequest(user.getId(), "채팅을 보내 봅니다."));
128+
129+
SessionResponse sessionResponse = users.poll(5, TimeUnit.SECONDS);
130+
MessageResponse messageResponse = messages.poll(5, TimeUnit.SECONDS);
131+
132+
// Then
133+
assertThat(sessionResponse.getUserResponses().get(0)).usingRecursiveComparison().isEqualTo(expectedUser);
134+
assertThat(messageResponse).usingRecursiveComparison().isEqualTo(expectedMessage);
135+
}
136+
137+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package oncoding.concoder.controller;
2+
3+
import java.lang.reflect.Type;
4+
import java.util.concurrent.BlockingQueue;
5+
import org.springframework.messaging.simp.stomp.StompFrameHandler;
6+
import org.springframework.messaging.simp.stomp.StompHeaders;
7+
8+
public class StompFrameHandlerImpl<T> implements StompFrameHandler {
9+
10+
private final T response;
11+
private final BlockingQueue<T> responses;
12+
13+
public StompFrameHandlerImpl(final T response, final BlockingQueue<T> responses) {
14+
this.response = response;
15+
this.responses = responses;
16+
}
17+
18+
@Override
19+
public Type getPayloadType(final StompHeaders headers) {
20+
return response.getClass();
21+
}
22+
23+
@Override
24+
public void handleFrame(final StompHeaders headers, final Object payload) {
25+
System.out.println(payload);
26+
responses.offer((T) payload);
27+
}
28+
}

Diff for: src/test/java/oncoding/concoder/service/ChattingServiceTest.java

+15-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
44

5+
import io.restassured.RestAssured;
56
import oncoding.concoder.dto.ChatDTO.SessionRequest;
67
import oncoding.concoder.dto.ChatDTO.SessionResponse;
78
import oncoding.concoder.model.Room;
@@ -10,16 +11,22 @@
1011
import oncoding.concoder.repository.RoomRepository;
1112
import oncoding.concoder.repository.SessionRepository;
1213
import oncoding.concoder.repository.UserRepository;
14+
import org.junit.jupiter.api.BeforeEach;
1315
import org.junit.jupiter.api.DisplayName;
1416
import org.junit.jupiter.api.Test;
1517
import org.springframework.beans.factory.annotation.Autowired;
1618
import org.springframework.boot.test.context.SpringBootTest;
19+
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
20+
import org.springframework.boot.test.web.server.LocalServerPort;
1721
import org.springframework.test.context.ActiveProfiles;
1822

19-
@SpringBootTest
23+
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
2024
@ActiveProfiles("local")
2125
public class ChattingServiceTest {
2226

27+
@LocalServerPort
28+
private int port;
29+
2330
@Autowired
2431
private UserRepository userRepository;
2532

@@ -32,7 +39,13 @@ public class ChattingServiceTest {
3239
@Autowired
3340
private ChattingService service;
3441

35-
42+
@BeforeEach
43+
public void setUp() {
44+
RestAssured.port = port;
45+
userRepository.deleteAll();
46+
roomRepository.deleteAll();
47+
sessionRepository.deleteAll();
48+
}
3649
@DisplayName("세션을 생성(방에 유저 입장)한다 - 세션이 생성은 되었지만 user의 session Id는 업뎃 전")
3750
@Test
3851
void createSession() {

0 commit comments

Comments
 (0)