Skip to content

Commit 715ec46

Browse files
authored
Add unit test for firebase_list (flutter#459)
1 parent d566d02 commit 715ec46

File tree

2 files changed

+266
-0
lines changed

2 files changed

+266
-0
lines changed

packages/firebase_database/pubspec.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ dependencies:
1717
firebase_core: ^0.1.0
1818

1919
dev_dependencies:
20+
mockito: ^2.0.2
21+
test:
2022
flutter_test:
2123
sdk: flutter
2224

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
// Copyright 2018 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:async';
6+
7+
import 'package:firebase_database/firebase_database.dart';
8+
import 'package:firebase_database/ui/firebase_list.dart';
9+
import 'package:mockito/mockito.dart';
10+
import 'package:test/test.dart';
11+
12+
void main() {
13+
group('FirebaseList', () {
14+
StreamController<Event> onChildAddedStreamController;
15+
StreamController<Event> onChildRemovedStreamController;
16+
StreamController<Event> onChildChangedStreamController;
17+
StreamController<Event> onChildMovedStreamController;
18+
MockQuery query;
19+
FirebaseList list;
20+
Completer<ListChange> callbackCompleter;
21+
22+
setUp(() {
23+
onChildAddedStreamController = new StreamController<Event>();
24+
onChildRemovedStreamController = new StreamController<Event>();
25+
onChildChangedStreamController = new StreamController<Event>();
26+
onChildMovedStreamController = new StreamController<Event>();
27+
query = new MockQuery(
28+
onChildAddedStreamController.stream,
29+
onChildRemovedStreamController.stream,
30+
onChildChangedStreamController.stream,
31+
onChildMovedStreamController.stream,
32+
);
33+
callbackCompleter = new Completer<ListChange>();
34+
35+
void completeWithChange(int index, DataSnapshot snapshot) {
36+
callbackCompleter.complete(ListChange.at(index, snapshot));
37+
}
38+
39+
void completeWithMove(int from, int to, DataSnapshot snapshot) {
40+
callbackCompleter.complete(ListChange.move(from, to, snapshot));
41+
}
42+
43+
list = new FirebaseList(
44+
query: query,
45+
onChildAdded: completeWithChange,
46+
onChildRemoved: completeWithChange,
47+
onChildChanged: completeWithChange,
48+
onChildMoved: completeWithMove,
49+
);
50+
});
51+
52+
Future<ListChange> resetCompleterOnCallback() async {
53+
final ListChange result = await callbackCompleter.future;
54+
callbackCompleter = new Completer<ListChange>();
55+
return result;
56+
}
57+
58+
Future<ListChange> processChildAddedEvent(Event event) {
59+
onChildAddedStreamController.add(event);
60+
return resetCompleterOnCallback();
61+
}
62+
63+
Future<ListChange> processChildRemovedEvent(Event event) {
64+
onChildRemovedStreamController.add(event);
65+
return resetCompleterOnCallback();
66+
}
67+
68+
Future<ListChange> processChildChangedEvent(Event event) {
69+
onChildChangedStreamController.add(event);
70+
return resetCompleterOnCallback();
71+
}
72+
73+
Future<ListChange> processChildMovedEvent(Event event) {
74+
onChildMovedStreamController.add(event);
75+
return resetCompleterOnCallback();
76+
}
77+
78+
test('can add to empty list', () async {
79+
final DataSnapshot snapshot = new MockDataSnapshot('key10', 10);
80+
expect(
81+
await processChildAddedEvent(new MockEvent(null, snapshot)),
82+
new ListChange.at(0, snapshot),
83+
);
84+
expect(list, <DataSnapshot>[snapshot]);
85+
});
86+
87+
test('can add before first element', () async {
88+
final DataSnapshot snapshot1 = new MockDataSnapshot('key10', 10);
89+
final DataSnapshot snapshot2 = new MockDataSnapshot('key20', 20);
90+
await processChildAddedEvent(new MockEvent(null, snapshot2));
91+
expect(
92+
await processChildAddedEvent(new MockEvent(null, snapshot1)),
93+
new ListChange.at(0, snapshot1),
94+
);
95+
expect(list, <DataSnapshot>[snapshot1, snapshot2]);
96+
});
97+
98+
test('can add after last element', () async {
99+
final DataSnapshot snapshot1 = new MockDataSnapshot('key10', 10);
100+
final DataSnapshot snapshot2 = new MockDataSnapshot('key20', 20);
101+
await processChildAddedEvent(new MockEvent(null, snapshot1));
102+
expect(
103+
await processChildAddedEvent(new MockEvent('key10', snapshot2)),
104+
new ListChange.at(1, snapshot2),
105+
);
106+
expect(list, <DataSnapshot>[snapshot1, snapshot2]);
107+
});
108+
109+
test('can remove from singleton list', () async {
110+
final DataSnapshot snapshot = new MockDataSnapshot('key10', 10);
111+
await processChildAddedEvent(new MockEvent(null, snapshot));
112+
expect(
113+
await processChildRemovedEvent(new MockEvent(null, snapshot)),
114+
new ListChange.at(0, snapshot),
115+
);
116+
expect(list, isEmpty);
117+
});
118+
119+
test('can remove former of two elements', () async {
120+
final DataSnapshot snapshot1 = new MockDataSnapshot('key10', 10);
121+
final DataSnapshot snapshot2 = new MockDataSnapshot('key20', 20);
122+
await processChildAddedEvent(new MockEvent(null, snapshot2));
123+
await processChildAddedEvent(new MockEvent(null, snapshot1));
124+
expect(
125+
await processChildRemovedEvent(new MockEvent(null, snapshot1)),
126+
new ListChange.at(0, snapshot1),
127+
);
128+
expect(list, <DataSnapshot>[snapshot2]);
129+
});
130+
131+
test('can remove latter of two elements', () async {
132+
final DataSnapshot snapshot1 = new MockDataSnapshot('key10', 10);
133+
final DataSnapshot snapshot2 = new MockDataSnapshot('key20', 20);
134+
await processChildAddedEvent(new MockEvent(null, snapshot2));
135+
await processChildAddedEvent(new MockEvent(null, snapshot1));
136+
expect(
137+
await processChildRemovedEvent(new MockEvent('key10', snapshot2)),
138+
new ListChange.at(1, snapshot2),
139+
);
140+
expect(list, <DataSnapshot>[snapshot1]);
141+
});
142+
143+
test('can change child', () async {
144+
final DataSnapshot snapshot1 = new MockDataSnapshot('key10', 10);
145+
final DataSnapshot snapshot2a = new MockDataSnapshot('key20', 20);
146+
final DataSnapshot snapshot2b = new MockDataSnapshot('key20', 25);
147+
final DataSnapshot snapshot3 = new MockDataSnapshot('key30', 30);
148+
await processChildAddedEvent(new MockEvent(null, snapshot3));
149+
await processChildAddedEvent(new MockEvent(null, snapshot2a));
150+
await processChildAddedEvent(new MockEvent(null, snapshot1));
151+
expect(
152+
await processChildChangedEvent(new MockEvent('key10', snapshot2b)),
153+
new ListChange.at(1, snapshot2b),
154+
);
155+
expect(list, <DataSnapshot>[snapshot1, snapshot2b, snapshot3]);
156+
});
157+
test('can move child', () async {
158+
final DataSnapshot snapshot1 = new MockDataSnapshot('key10', 10);
159+
final DataSnapshot snapshot2 = new MockDataSnapshot('key20', 20);
160+
final DataSnapshot snapshot3 = new MockDataSnapshot('key30', 30);
161+
await processChildAddedEvent(new MockEvent(null, snapshot3));
162+
await processChildAddedEvent(new MockEvent(null, snapshot2));
163+
await processChildAddedEvent(new MockEvent(null, snapshot1));
164+
expect(
165+
await processChildMovedEvent(new MockEvent('key30', snapshot1)),
166+
new ListChange.move(0, 2, snapshot1),
167+
);
168+
expect(list, <DataSnapshot>[snapshot2, snapshot3, snapshot1]);
169+
});
170+
});
171+
}
172+
173+
class MockQuery extends Mock implements Query {
174+
MockQuery(
175+
this.onChildAdded,
176+
this.onChildRemoved,
177+
this.onChildChanged,
178+
this.onChildMoved,
179+
);
180+
181+
@override
182+
final Stream<Event> onChildAdded;
183+
184+
@override
185+
final Stream<Event> onChildRemoved;
186+
187+
@override
188+
final Stream<Event> onChildChanged;
189+
190+
@override
191+
final Stream<Event> onChildMoved;
192+
}
193+
194+
class ListChange {
195+
ListChange.at(int index, DataSnapshot snapshot)
196+
: this._(index, null, snapshot);
197+
198+
ListChange.move(int from, int to, DataSnapshot snapshot)
199+
: this._(from, to, snapshot);
200+
201+
ListChange._(this.index, this.index2, this.snapshot);
202+
203+
final int index;
204+
final int index2;
205+
final DataSnapshot snapshot;
206+
207+
@override
208+
String toString() => '$runtimeType[$index, $index2, $snapshot]';
209+
210+
@override
211+
bool operator ==(Object o) {
212+
return o is ListChange &&
213+
index == o.index &&
214+
index2 == o.index2 &&
215+
snapshot == o.snapshot;
216+
}
217+
218+
@override
219+
int get hashCode => index;
220+
}
221+
222+
class MockEvent implements Event {
223+
MockEvent(this.previousSiblingKey, this.snapshot);
224+
225+
@override
226+
final String previousSiblingKey;
227+
228+
@override
229+
final DataSnapshot snapshot;
230+
231+
@override
232+
String toString() => '$runtimeType[$previousSiblingKey, $snapshot]';
233+
234+
@override
235+
bool operator ==(Object o) {
236+
return o is MockEvent &&
237+
previousSiblingKey == o.previousSiblingKey &&
238+
snapshot == o.snapshot;
239+
}
240+
241+
@override
242+
int get hashCode => previousSiblingKey.hashCode;
243+
}
244+
245+
class MockDataSnapshot implements DataSnapshot {
246+
MockDataSnapshot(this.key, this.value);
247+
248+
@override
249+
final String key;
250+
251+
@override
252+
final dynamic value;
253+
254+
@override
255+
String toString() => '$runtimeType[$key, $value]';
256+
257+
@override
258+
bool operator ==(Object o) {
259+
return o is MockDataSnapshot && key == o.key && value == o.value;
260+
}
261+
262+
@override
263+
int get hashCode => key.hashCode;
264+
}

0 commit comments

Comments
 (0)