Merge lp:~nik90/ubuntu-clock-app/add-world-qml-tests into lp:ubuntu-clock-app

Proposed by Nekhelesh Ramananthan
Status: Merged
Approved by: Nekhelesh Ramananthan
Approved revision: 168
Merged at revision: 147
Proposed branch: lp:~nik90/ubuntu-clock-app/add-world-qml-tests
Merge into: lp:ubuntu-clock-app
Prerequisite: lp:~nik90/ubuntu-clock-app/edit-alarm-qml-tests
Diff against target: 417 lines (+309/-23)
6 files modified
app/worldclock/WorldCityList.qml (+3/-3)
debian/changelog (+1/-0)
tests/autopilot/ubuntu_clock_app/emulators.py (+20/-20)
tests/unit/CMakeLists.txt (+3/-0)
tests/unit/MockClockApp.qml (+88/-0)
tests/unit/tst_worldClock.qml (+194/-0)
To merge this branch: bzr merge lp:~nik90/ubuntu-clock-app/add-world-qml-tests
Reviewer Review Type Date Requested Status
Leo Arias (community) Approve
Ubuntu Phone Apps Jenkins Bot continuous-integration Approve
Ubuntu Clock Developers Pending
Review via email: mp+238217@code.launchpad.net

Commit message

Added world clock feature qml tests.

Description of the change

Added world clock feature qml tests.

The QML Test Suite now comprises of Feature, Unit and Integration tests and collectively has a far higher test coverage of the clock app than AP test have. A few more MPs later, I might propose removing AP tests.

To post a comment you must log in.
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Leo Arias (elopio) wrote :
Download full text (3.2 KiB)

You have a couple of ifs there that are really hard to understand.
You can refactor them like this: http://paste.ubuntu.com/8556979/
(I haven't run the tests with the refactor, so please double check the code, I could make typos).

Also, please remember that on python, we should name the variables with all lower case separating words with spaces. And that we should prefer single quotes instead of double quotes. Looking at the emulators file, I see some style errors. They can be fixed in a separate branch, unless you are planning to kill that file soon.

126 +/*
127 + This file is meant to create a fake but fully fleshed clock app with its
128 + own database and settings. This will avoid messing with the user data while
129 + running the tests.
130 +*/

Interesting, I like it. You just need to take a lot of care that this test double is as close to the real as possible. Ideally without duplicating any code, because otherwise they will be hard to maintain. Usually, devs add things to the real one and forget to update the double, so they end up testing a different thing.

Could you make a QML file that takes the app name and any other things you want to change on the mock as parameters, and then make both app/ubuntu-clock-app.qml and tests/unit/MockClockApp.qml extend that file?

250 + function _findWorldCity(cityList, type, cityName, countryName) {

It's slightly clearer when you make two methods instead of passing a type argument:
findUserWorldCity and findDefaultWorldCity.
Those are the methods you call from a test. Then, you make them call an internal _findWorldCity where you pass the right prefix. But you are encapsulating the prefix in the internal methods, your tests don't know about the exact prefix. So by changing the tests to call a method instead of passing a string, it will be easier for you if you ever have to change the prefix. Instead of changing it in all the tests that call findWorldCity, you will just have to change it on the non-internal methods and the name can remain the same.

271 + function _confirmWorldCityAddition(cityName, countryName) {

Here again, I prefer assert. confirm sounds like a step of the test, like confirm a dialog or something like that.

295 + tryCompareFunction(function() { return cityList.count > 0}, true)

And here again, this shouldn't be needed if you are already asserting the successful creation during a test.

327 + // Wait for the list to be populated with results
328 + tryCompareFunction(function() { return cityList.count > 0}, true)

Now this is a valid usage of an assertion on the middle of the steps. But it's just because this framework doesn't have a proper wait. On autopilot we would do it with a wait_for or something similar, that's a little different than an assertion.

373 + function test_addCityBySearchingOnline() {

You have to be careful here. The unit tests many times are run in a confined machine without internet access. As the tests are passing, I'm guessing that's not the case for the jenkins runners. But ideally, you would replace the online model with a hard-coded one for a unit test. And then do an integration test for the service that provides you with cities.

This is great, ...

Read more...

160. By Nekhelesh Ramananthan

merged trunk

161. By Nekhelesh Ramananthan

Migrated tst_worldClock over to ClockTestCase

162. By Nekhelesh Ramananthan

renamed confirmWorldCityAddition() to assertWorldCityAddition()

163. By Nekhelesh Ramananthan

Removed unnecessary confirm statement iat the start of a test

164. By Nekhelesh Ramananthan

Added TODO comment for mocking the searchOnline function

165. By Nekhelesh Ramananthan

Improved if logic in emulators.py

166. By Nekhelesh Ramananthan

Simplified code a bit

Revision history for this message
Nekhelesh Ramananthan (nik90) wrote :
Download full text (3.2 KiB)

> You have a couple of ifs there that are really hard to understand.
> You can refactor them like this: http://paste.ubuntu.com/8556979/
> (I haven't run the tests with the refactor, so please double check the code, I
> could make typos).
>
> Also, please remember that on python, we should name the variables with all
> lower case separating words with spaces. And that we should prefer single
> quotes instead of double quotes. Looking at the emulators file, I see some
> style errors. They can be fixed in a separate branch, unless you are planning
> to kill that file soon.

Thnx I fixed that as well. I was only planning to make minimal changes to emulators.py in fear of breaking the AP test ;). But now the if logic looks much simpler despite taking me longer than usual to fix that (hence my uneasiness in touch python code)

> 250 + function _findWorldCity(cityList, type, cityName, countryName) {
>
> It's slightly clearer when you make two methods instead of passing a type
> argument:
> findUserWorldCity and findDefaultWorldCity.
> Those are the methods you call from a test. Then, you make them call an
> internal _findWorldCity where you pass the right prefix. But you are
> encapsulating the prefix in the internal methods, your tests don't know about
> the exact prefix. So by changing the tests to call a method instead of passing
> a string, it will be easier for you if you ever have to change the prefix.
> Instead of changing it in all the tests that call findWorldCity, you will just
> have to change it on the non-internal methods and the name can remain the
> same.
>

Done. I hope what I did is what you had in mind as well.

> 271 + function _confirmWorldCityAddition(cityName, countryName) {
>
> Here again, I prefer assert. confirm sounds like a step of the test, like
> confirm a dialog or something like that.

Trivial fix. Done. I will use assert from now on.

>
> 295 + tryCompareFunction(function() { return cityList.count > 0}, true)
>
> And here again, this shouldn't be needed if you are already asserting the
> successful creation during a test.
>

Done

> 327 + // Wait for the list to be populated with results
> 328 + tryCompareFunction(function() { return cityList.count > 0}, true)
>
> Now this is a valid usage of an assertion on the middle of the steps. But it's
> just because this framework doesn't have a proper wait. On autopilot we would
> do it with a wait_for or something similar, that's a little different than an
> assertion.
>

Done

> 373 + function test_addCityBySearchingOnline() {
>
> You have to be careful here. The unit tests many times are run in a confined
> machine without internet access. As the tests are passing, I'm guessing that's
> not the case for the jenkins runners. But ideally, you would replace the
> online model with a hard-coded one for a unit test. And then do an integration
> test for the service that provides you with cities.
>

This is an easy one. I have added a TODO comment about mocking that. I am not sure *yet* how to do this without touching the clock app code which at the moment (for RTM) I don't want to do.

> This is great, as always. Ping me on IRC if you want to ...

Read more...

Revision history for this message
Nekhelesh Ramananthan (nik90) wrote :

>
> 126 +/*
> 127 + This file is meant to create a fake but fully fleshed clock app with
> its
> 128 + own database and settings. This will avoid messing with the user
> data while
> 129 + running the tests.
> 130 +*/
>
> Interesting, I like it.

This is my first attempt at mocking the clock app. And I was surprised at how easy it was to do this, something which wouldn't be possible with AP without touching the actual app config files.

> You just need to take a lot of care that this test
> double is as close to the real as possible. Ideally without duplicating any
> code, because otherwise they will be hard to maintain. Usually, devs add
> things to the real one and forget to update the double, so they end up testing
> a different thing.

Yup. I agree this can be a potential issue. However this late in the cycle (close to RTM) there wouldn't be any more changes to ubuntu-clock-app.qml. And even if it does, it will be really minor and I will make sure to sync it up to MockClockApp.qml.

>
> Could you make a QML file that takes the app name and any other things you
> want to change on the mock as parameters, and then make both app/ubuntu-clock-
> app.qml and tests/unit/MockClockApp.qml extend that file?
>

At this point, I don't want to avoid touching the ubuntu-clock-app.qml if I can avoid it. I hope that is okay. But I think I have an idea of how you want to proceed with this. I will have another MP which plays with that idea.

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
167. By Nekhelesh Ramananthan

Merged prerequisite branch

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Leo Arias (elopio) :
review: Approve
168. By Nekhelesh Ramananthan

Merged prerequisite branch

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'app/worldclock/WorldCityList.qml'
2--- app/worldclock/WorldCityList.qml 2014-10-11 18:39:12 +0000
3+++ app/worldclock/WorldCityList.qml 2014-10-14 16:02:15 +0000
4@@ -302,7 +302,7 @@
5
6 delegate: ListItem.Base {
7 showDivider: false
8- objectName: "worldCityItem" + index
9+ objectName: "defaultWorldCityItem" + index
10
11 Column {
12 id: worldCityDelegateColumn
13@@ -316,7 +316,7 @@
14
15 Label {
16 text: city
17- objectName: "cityNameText"
18+ objectName: "defaultCityNameText"
19 width: parent.width
20 elide: Text.ElideRight
21 color: UbuntuColors.midAubergine
22@@ -324,7 +324,7 @@
23
24 Label {
25 text: country
26- objectName: "countryNameText"
27+ objectName: "defaultCountryNameText"
28 fontSize: "xx-small"
29 width: parent.width
30 elide: Text.ElideRight
31
32=== modified file 'debian/changelog'
33--- debian/changelog 2014-10-14 16:02:15 +0000
34+++ debian/changelog 2014-10-14 16:02:15 +0000
35@@ -14,6 +14,7 @@
36 * Fixed alarm page header width warning and corrected icon spacing.
37 * Added edit alarm qml tests and added a library helper which contains commonly
38 used test functions to avoid code duplication in future tests.
39+ * Added world clock feature qml tests
40 * Delayed loading of xmltimezone model until add world city page is loaded and
41 dynamically load/unload the jsontimezone model only when necessary.
42
43
44=== modified file 'tests/autopilot/ubuntu_clock_app/emulators.py'
45--- tests/autopilot/ubuntu_clock_app/emulators.py 2014-10-10 22:12:50 +0000
46+++ tests/autopilot/ubuntu_clock_app/emulators.py 2014-10-14 16:02:15 +0000
47@@ -133,16 +133,15 @@
48 old_cities_count = self.get_num_of_saved_cities()
49 index = 0
50 for index in range(old_cities_count):
51- if self.wait_select_single(
52- objectName='userWorldCityItem{}'.format(index)).\
53- wait_select_single("Label", objectName="userCityNameText").\
54- text == city_Name:
55- if self.wait_select_single(
56- objectName='userWorldCityItem{}'.format(index)).\
57- wait_select_single(
58- "Label", objectName="userCountryNameText").\
59- text == country_Name:
60- self._delete_userWorldCityItem(index)
61+ world_city_item = self.wait_select_single(
62+ objectName='userWorldCityItem{}'.format(index))
63+ city_name_label = world_city_item.wait_select_single(
64+ 'Label', objectName='userCityNameText')
65+ country_name_label = world_city_item.wait_select_single(
66+ 'Label', objectName='userCountryNameText')
67+ if (city_name_label.text == city_Name and
68+ country_name_label.text == country_Name):
69+ self._delete_userWorldCityItem(index)
70
71 # FIXME -----------------------------------------------------------------
72 # Commenting the following lines as deleting a world city when there is
73@@ -266,16 +265,17 @@
74 cityList.count.wait_for(GreaterThan(0))
75
76 for index in range(int(cityList.count)):
77- if cityList.wait_select_single(
78- objectName="worldCityItem{}".format(index)).wait_select_single(
79- "Label", objectName="cityNameText").text == city_Name:
80- if cityList.wait_select_single(
81- objectName="worldCityItem{}".format(index)).\
82- wait_select_single("Label", objectName="countryNameText").\
83- text == country_Name:
84- cityList.click_element(
85- "worldCityItem{}".format(index), direction=None)
86- break
87+ world_city_item = self.wait_select_single(
88+ objectName='defaultWorldCityItem{}'.format(index))
89+ city_name_label = world_city_item.wait_select_single(
90+ 'Label', objectName='defaultCityNameText')
91+ country_name_label = world_city_item.wait_select_single(
92+ 'Label', objectName='defaultCountryNameText')
93+ if (city_name_label.text == city_Name and
94+ country_name_label.text == country_Name):
95+ cityList.click_element(
96+ 'defaultWorldCityItem{}'.format(index), direction=None)
97+ break
98
99 @autopilot_logging.log_action(logger.info)
100 def search_world_city_(self, city_Name, country_Name):
101
102=== modified file 'tests/unit/CMakeLists.txt'
103--- tests/unit/CMakeLists.txt 2014-10-14 16:02:15 +0000
104+++ tests/unit/CMakeLists.txt 2014-10-14 16:02:15 +0000
105@@ -26,6 +26,7 @@
106 declare_qml_test("Alarm" tst_alarm.qml)
107 declare_qml_test("AlarmSound" tst_alarmSound.qml)
108 declare_qml_test("AlarmUtils" tst_alarmUtils.qml)
109+ declare_qml_test("WorldClock" tst_worldClock.qml)
110 else()
111 if (NOT QMLTESTRUNNER_BIN)
112 message(WARNING "Qml tests disabled: qmltestrunner not found")
113@@ -40,10 +41,12 @@
114 tst_alarm.qml
115 tst_alarmSound.qml
116 tst_alarmUtils.qml
117+ tst_worldClock.qml
118 )
119 add_custom_target(tst_QmlFiles ALL SOURCES ${QML_TST_FILES})
120
121 set(QML_TST_UTILS
122+ MockClockApp.qml
123 ClockTestCase.qml
124 )
125
126
127=== added file 'tests/unit/MockClockApp.qml'
128--- tests/unit/MockClockApp.qml 1970-01-01 00:00:00 +0000
129+++ tests/unit/MockClockApp.qml 2014-10-14 16:02:15 +0000
130@@ -0,0 +1,88 @@
131+/*
132+ * Copyright (C) 2014 Canonical Ltd
133+ *
134+ * This file is part of Ubuntu Clock App
135+ *
136+ * Ubuntu Clock App is free software: you can redistribute it and/or modify
137+ * it under the terms of the GNU General Public License version 3 as
138+ * published by the Free Software Foundation.
139+ *
140+ * Ubuntu Clock App is distributed in the hope that it will be useful,
141+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
142+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
143+ * GNU General Public License for more details.
144+ *
145+ * You should have received a copy of the GNU General Public License
146+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
147+ */
148+
149+import QtQuick 2.3
150+import DateTime 1.0
151+import U1db 1.0 as U1db
152+import Ubuntu.Components 1.1
153+import "../../app/clock"
154+
155+/*
156+ This file is meant to create a fake but fully fleshed clock app with its
157+ own database and settings. This will avoid messing with the user data while
158+ running the tests.
159+*/
160+
161+MainView {
162+ id: clockApp
163+
164+ // Property to store the state of an application (active or suspended)
165+ property bool applicationState: Qt.application.active
166+
167+ width: units.gu(40)
168+ height: units.gu(70)
169+ useDeprecatedToolbar: false
170+ applicationName: "com.ubuntu.fakeclock.test"
171+
172+ U1db.Database {
173+ id: clockDB
174+ path: "user-preferences"
175+ }
176+
177+ U1db.Document {
178+ id: clockModeDocument
179+ create: true
180+ database: clockDB
181+ docId: "clockModeDocument"
182+ defaults: { "digitalMode": false }
183+ }
184+
185+ DateTime {
186+ id: localTimeSource
187+ updateInterval: 1000
188+ }
189+
190+ PageStack {
191+ id: mainStack
192+ objectName: "pageStack"
193+
194+ Component.onCompleted: push(clockPage)
195+
196+ ClockPage {
197+ id: clockPage
198+
199+ Loader {
200+ id: alarmModelLoader
201+ asynchronous: false
202+ }
203+
204+ alarmModel: alarmModelLoader.item
205+ bottomEdgeEnabled: alarmModelLoader.status === Loader.Ready
206+ clockTime: new Date
207+ (
208+ localTimeSource.localDateString.split(":")[0],
209+ localTimeSource.localDateString.split(":")[1]-1,
210+ localTimeSource.localDateString.split(":")[2],
211+ localTimeSource.localTimeString.split(":")[0],
212+ localTimeSource.localTimeString.split(":")[1],
213+ localTimeSource.localTimeString.split(":")[2],
214+ localTimeSource.localTimeString.split(":")[3]
215+ )
216+ }
217+ }
218+}
219
220=== added file 'tests/unit/tst_worldClock.qml'
221--- tests/unit/tst_worldClock.qml 1970-01-01 00:00:00 +0000
222+++ tests/unit/tst_worldClock.qml 2014-10-14 16:02:15 +0000
223@@ -0,0 +1,194 @@
224+/*
225+ * Copyright (C) 2014 Canonical Ltd
226+ *
227+ * This file is part of Ubuntu Clock App
228+ *
229+ * Ubuntu Clock App is free software: you can redistribute it and/or modify
230+ * it under the terms of the GNU General Public License version 3 as
231+ * published by the Free Software Foundation.
232+ *
233+ * Ubuntu Clock App is distributed in the hope that it will be useful,
234+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
235+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
236+ * GNU General Public License for more details.
237+ *
238+ * You should have received a copy of the GNU General Public License
239+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
240+ */
241+
242+import QtQuick 2.0
243+import QtTest 1.0
244+import Ubuntu.Test 1.0
245+import Ubuntu.Components 1.1
246+
247+MockClockApp {
248+ id: clockApp
249+
250+ ClockTestCase {
251+ id: worldClockFeatureTest
252+ name: "WorldClockFeatureTest"
253+
254+ when: windowShown
255+
256+ property var header
257+ property var backButton
258+ property var clockPage
259+ property var pageStack
260+
261+ function initTestCase() {
262+ header = findChild(clockApp, "MainView_Header")
263+ backButton = findChild(header, "customBackButton")
264+ pageStack = findChild(clockApp, "pageStack")
265+ clockPage = findChild(clockApp, "clockPage")
266+ }
267+
268+ // *********** Helper Functions ************
269+
270+ function pressAddWorldCityButton() {
271+ var addWorldCityButton = findChild(clockApp, "addWorldCityButton")
272+ pressButton(addWorldCityButton)
273+ }
274+
275+ function _findWorldCity(cityList, type, cityName, countryName) {
276+ /*
277+ The list view for the user world city list and the available world
278+ city list have the same structure with some minor object name
279+ changes. The 'objectPrefix' varible is used to handle that.
280+ */
281+ var objectPrefix = type === "user" ? "user" : "default"
282+
283+ for(var i=0; i<cityList.count; i++) {
284+ var cityListItem = findChild(clockApp, objectPrefix+"WorldCityItem"+i)
285+ var city = findChild(cityListItem, objectPrefix+"CityNameText")
286+ var country = findChild(cityListItem, objectPrefix+"CountryNameText")
287+
288+ if (city.text === cityName && country.text === countryName) {
289+ return i
290+ }
291+ }
292+
293+ return -1;
294+ }
295+
296+ function findUserWorldCity(cityList, cityName, countryName) {
297+ return _findWorldCity(cityList, "user", cityName, countryName)
298+ }
299+
300+ function findDefaultWorldCity(cityList, cityName, countryName) {
301+ return _findWorldCity(cityList, "default", cityName, countryName)
302+ }
303+
304+ function assertWorldCityAddition(cityName, countryName) {
305+ var cityList = findChild(clockApp, "userWorldCityRepeater")
306+
307+ // Wait for the user list to be populated with results
308+ tryCompareFunction(function() { return cityList.count > 0}, true)
309+
310+ var cityIndex = findUserWorldCity(cityList, cityName, countryName)
311+
312+ if (cityIndex === -1) {
313+ // If city couldn't be found in the saved city list, fail the test
314+ fail("City added during the test cannot be found in the user world city list!")
315+ }
316+ }
317+
318+ function deleteWorldCity(cityName, countryName) {
319+ var cityList = findChild(clockApp, "userWorldCityRepeater")
320+
321+ var oldCount = cityList.count
322+ var cityIndex = findUserWorldCity(cityList, cityName, countryName)
323+
324+ if (cityIndex === -1) {
325+ fail("City added during the test cannot be found in the user world city list!")
326+ }
327+ else {
328+ var cityListItem = findChild(clockApp, "userWorldCityItem"+cityIndex)
329+ swipeToDeleteItem(cityListItem)
330+ }
331+
332+ /*
333+ #FIXME: Commented out the following line as deleting a world city
334+ when there is only one world city does not decrease the count to 0
335+ but leaves it as 1 causing the test to fail. This has been reported
336+ in bug #1368393. (Also fails in Autopilot)
337+
338+ tryCompare(cityList, "count", oldCount-1, 5000, "city list count did not decrease")
339+
340+ The wait() call below is to ensure that the world city is deleted properly
341+ which wouldn't be required if could do the count decrease check mentioned above.
342+ */
343+
344+ wait(1000)
345+ }
346+
347+ function addCityFromList(cityName, countryName) {
348+ var worldCityPage = getPage(pageStack, "worldCityList")
349+ var cityList = findChild(worldCityPage, "cityList")
350+
351+ // Wait for the list to be populated with results
352+ tryCompareFunction(function() { return cityList.count > 0}, true)
353+
354+ var cityIndex = findDefaultWorldCity(cityList, cityName, countryName)
355+
356+ if (cityIndex === -1) {
357+ fail("City cannot be found in the local world city list")
358+ }
359+
360+ var cityListItem = findChild(cityList, "defaultWorldCityItem"+cityIndex)
361+ mouseClick(cityListItem, centerOf(cityListItem).x, centerOf(cityListItem).y)
362+ }
363+
364+ function addCityBySearchingOnline(cityName, countryName) {
365+ pressHeaderButton(header, "searchButton")
366+ var searchField = findChild(clockApp, "searchField")
367+ tryCompare(searchField, "visible", true, 5000, "Search field is not visible")
368+ typeString(cityName)
369+ addCityFromList(cityName, countryName)
370+ }
371+
372+ // *********** Test Functions *************
373+
374+ /*
375+ Test to check if a city found in the world city list can be added
376+ to the user world city list.
377+ */
378+ function test_addCityAlreadyPresentInWorldCityList() {
379+ var clockPage = getPage(pageStack, "clockPage")
380+
381+ pressAddWorldCityButton()
382+
383+ var worldCityPage = getPage(pageStack, "worldCityList")
384+ waitForRendering(worldCityPage)
385+
386+ addCityFromList("Amsterdam", "Netherlands")
387+ assertWorldCityAddition("Amsterdam", "Netherlands")
388+
389+ // Clean up after the test by deleting the city which was added during the test
390+ deleteWorldCity("Amsterdam", "Netherlands")
391+ }
392+
393+ /*
394+ Test to check if a city now found in the world city list can be added
395+ by searcing it online and then adding it from the results returned.
396+ */
397+ function test_addCityBySearchingOnline() {
398+ var clockPage = getPage(pageStack, "clockPage")
399+
400+ pressAddWorldCityButton()
401+
402+ var worldCityPage = getPage(pageStack, "worldCityList")
403+ waitForRendering(worldCityPage)
404+
405+ /*
406+ #TODO: Jenkins machine may run in a confined environment with no
407+ access to internet to run this function. Ideally we should mock
408+ the data given to this function.
409+ */
410+ addCityBySearchingOnline("Venice", "Provincia di Venezia, Veneto, Italy")
411+ assertWorldCityAddition("Venice", " Veneto, Italy")
412+
413+ // Clean up after the test by deleting the city which was added during the test
414+ deleteWorldCity("Venice", " Veneto, Italy")
415+ }
416+ }
417+}

Subscribers

People subscribed via source and target branches