What to do?
MessageService를 만들고, Test 코드 작성하기
Reference
Pubspec.yaml
로깅을 하기 위해 라이브러리 설치
dependencies:
flutter:
sdk: flutter
...
logger: ^1.2.2

Message Model
/model/message_model.dart
- from : 메세지를 보낸 유저의 id
- to : 메세지를 받는 유저의 id
class Message {
String get id => _id;
String _id;
/// from : sender id
/// to : receiver id
final String from;
final String to;
final DateTime timestamp;
final String contents;
Message(
{@required this.from,
@required this.to,
@required this.timestamp,
@required this.contents});
toJson() =>
{'from': from, 'to': to, 'timestamp': timestamp, 'contents': contents};
factory Message.fromJson(Map<String, dynamic> json) {
var message = Message(
from: json['from'],
to: json['to'],
timestamp: json['timestamp'],
contents: json['contents']);
message._id = json['id'];
return message;
}
}
MessageService
/service/message_service_interface.dart
- message service에서 구현해야 하는 메써드는 send, messages, dispose
- send : 메세지 보내기
- messages : 특정 유저가 받은 메세지들을 반환
- dispose : 접속 종료
abstract class IMessageService {
Future<bool> send(Message message);
Stream<Message> messages({@required User user});
dispose();
}
/service/user_serivce.dart
class MessageService implements IMessageService {
final Connection _connection;
final Rethinkdb _db;
// 에러 발생시 로깅
var _logger = Logger();
final _controller = StreamController<Message>.broadcast();
StreamSubscription _changefeed;
MessageService(this._db, this._connection);
@override
dispose() {
// 구독 취소
_changefeed?.cancel();
_controller?.close();
}
@override
Stream<Message> messages({User user}) {
_startReceivingMessages(user);
return _controller.stream;
}
@override
Future<bool> send(Message message) async {
// Message를 받아서 데이터베이스에 저장
Map record =
await _db.table('messages').insert(message.toJson()).run(_connection);
// DB에 데이터가 잘 들어갔는지 여부를 반환
return record['inserted'] == 1;
}
_startReceivingMessages(User user) {
_changefeed = _db
.table('messages')
.filter({'to': user.id})
.changes({'include_initial': true})
.run(_connection)
.asStream()
.cast<Feed>()
.listen((event) {
event
.forEach((feedData) {
if (feedData['new_val'] == null) return;
final message = _messageFromFeed(feedData);
_controller.sink.add(message);
_removeDeliveredMessage(message);
})
.catchError((err) => _logger.e(err))
.onError((err, stackTrace) => _logger.e(err));
});
}
Message _messageFromFeed(feedData) {
return Message.fromJson(feedData['new_val']);
}
_removeDeliveredMessage(Message message) {
_db
.table('messages')
.get(message.id)
.delete({'return_changes': false}).run(_connection);
}
}
Test 코드
test/util_for_test.dart
- 테스트 시작 시에 △ test라는 데이터 베이스 생성 △ users 테이블 생성 △ messages 테이블 생성
- 테스트 종료 시에 △ users 테이블 삭제 △ messages 테이블 삭제
Future<void> createDatabase(Rethinkdb db, Connection connection) async {
await db.dbCreate('test').run(connection).catchError((err) => {});
await db.tableCreate('users').run(connection).catchError((err) => {});
await db.tableCreate('messages').run(connection).catchError((err) => {});
}
Future<void> cleanDatabase(Rethinkdb db, Connection connection) async {
await db.table('users').delete().run(connection);
await db.table('messages').delete().run(connection);
}
test/message_service_test.dart
void main() {
Rethinkdb _db = Rethinkdb();
Connection _connection;
MessageService sut;
setUp(() async {
_connection = await _db.connect(host: '127.0.0.1', port: 28015);
await createDatabase(_db, _connection);
sut = MessageService(_db, _connection);
});
tearDown(() async {
sut.dispose();
await cleanDatabase(_db, _connection);
});
final sender =
User.fromJson({'id': '1', 'active': true, 'lastSeen': DateTime.now()});
final receiver =
User.fromJson({'id': '2', 'active': true, 'lastSeen': DateTime.now()});
test('if message sent successfully, then return true', () async {
Message message = Message(
from: sender.id,
to: receiver.id,
timestamp: DateTime.now(),
contents: 'test message');
final result = await sut.send(message);
expect(result, true);
});
test('sending message works successfully', () async {
sut.messages(user: receiver).listen(expectAsync1((message) {
expect(message.to, receiver.id);
expect(message.id, isNotEmpty);
}, count: 2));
Message firstMessage = Message(
from: sender.id,
to: receiver.id,
timestamp: DateTime.now(),
contents: 'test message');
Message secondMessage = Message(
from: sender.id,
to: receiver.id,
timestamp: DateTime.now(),
contents: 'test message');
await sut.send(firstMessage);
await sut.send(secondMessage);
});
test('successfully subscribe and receive messages', () async {
sut.messages(user: receiver).listen(expectAsync1((message) {
expect(message.to, receiver.id);
expect(message.id, isNotEmpty);
}, count: 2));
Message message = Message(
from: sender.id,
to: receiver.id,
timestamp: DateTime.now(),
contents: 'this is a message',
);
Message secondMessage = Message(
from: sender.id,
to: receiver.id,
timestamp: DateTime.now(),
contents: 'this is another message',
);
await sut.send(message);
await sut.send(secondMessage);
});
test('successfully subscribe and receive new messages ', () async {
Message message = Message(
from: sender.id,
to: receiver.id,
timestamp: DateTime.now(),
contents: 'this is a message',
);
Message secondMessage = Message(
from: sender.id,
to: receiver.id,
timestamp: DateTime.now(),
contents: 'this is another message',
);
await sut.send(message);
await sut.send(secondMessage).whenComplete(
() => sut.messages(user: receiver).listen(
expectAsync1((message) {
expect(message.to, receiver.id);
}, count: 2),
),
);
});
}
테스트 실행
docker 컨테이너 실행
docker run -d -p 8080:8080 -p 20815:20815 rethinkdb
테스트 코드 실행

'Flutter' 카테고리의 다른 글
[Flutter] 채팅앱 만들기 #5 (0) | 2023.03.04 |
---|---|
[Flutter] 채팅앱 만들기 #4 (0) | 2023.03.01 |
[Flutter] 채팅앱 만들기 #3 (0) | 2023.03.01 |
[Flutter] 채팅앱 만들기 #1 (0) | 2023.02.27 |
[Flutter] 개발환경 세팅하기 (0) | 2023.02.25 |