Merge lp:~nskaggs/autopilot/apdocs-tutorial into lp:autopilot

Proposed by Nicholas Skaggs
Status: Work in progress
Proposed branch: lp:~nskaggs/autopilot/apdocs-tutorial
Merge into: lp:autopilot
Diff against target: 534 lines (+197/-246)
2 files modified
docs/tutorial/getting_started.rst (+196/-245)
docs/tutorial/what_is_autopilot.rst (+1/-1)
To merge this branch: bzr merge lp:~nskaggs/autopilot/apdocs-tutorial
Reviewer Review Type Date Requested Status
Autopilot Hackers Pending
Review via email: mp+247218@code.launchpad.net

Commit message

Rebase autopilot tutorial to use qml

Description of the change

Rebase autopilot tutorial to use qml

To post a comment you must log in.
524. By Nicholas Skaggs

remerge trunk

525. By Nicholas Skaggs

rebase trunk

526. By Nicholas Skaggs

wip

527. By Nicholas Skaggs

rebase trunk

528. By Nicholas Skaggs

rebase trunk

529. By Nicholas Skaggs

fix tutorial

Unmerged revisions

529. By Nicholas Skaggs

fix tutorial

528. By Nicholas Skaggs

rebase trunk

527. By Nicholas Skaggs

rebase trunk

526. By Nicholas Skaggs

wip

525. By Nicholas Skaggs

rebase trunk

524. By Nicholas Skaggs

remerge trunk

523. By Nicholas Skaggs

wip

522. By Nicholas Skaggs

wip; initial tutorial work

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'docs/tutorial/getting_started.rst'
2--- docs/tutorial/getting_started.rst 2015-01-25 23:08:51 +0000
3+++ docs/tutorial/getting_started.rst 2015-01-27 20:14:32 +0000
4@@ -3,78 +3,47 @@
5
6 This document contains everything you need to know to write your first autopilot test. It covers writing several simple tests for a sample Qt5/Qml application. However, it's important to note that nothing in this tutorial is specific to Qt5/Qml, and will work equally well with any other kind of application.
7
8-Files and Directories
9-=====================
10-
11-Your autopilot test suite will grow to several files, possibly spread across several directories. We recommend that you follow this simple directory layout::
12-
13- autopilot/
14- autopilot/<projectname>/
15- autopilot/<projectname>/tests/
16-
17-The ``autopilot`` folder can be anywhere within your project's source tree. It will likely contain a `setup.py <http://docs.python.org/3/distutils/setupscript.html>`_ file.
18-
19-The ``autopilot/<projectname>/`` folder is the base package for your autopilot tests. This folder, and all child folders, are python packages, and so must contain an `__init__.py file <http://docs.python.org/3/tutorial/modules.html#packages>`_. If you ever find yourself writing custom proxy classes (This is an advanced topic, and is covered here: :ref:`custom_proxy_classes`), they should be imported from this top-level package.
20-
21-Each test file should be named ``test_<component>.py``, where *<component>* is the logical component you are testing in that file. Test files must be written in the ``autopilot/<projectname>/tests/`` folder.
22+Prerequisites
23+=============
24+For the tutorial you will need to ensure you have installed autopilot. See :ref:`installing_autopilot` for installation instructions.
25+
26+In addition, if you wish to follow along directly with the tutorial you should install the ubuntu sdk. See `developer.ubuntu.com <https://developer.ubuntu.com/en/start/ubuntu-sdk/installing-the-sdk/>`_ for installation instructions. Since you can apply the tutorial to any application introspectable by autopilot this step is optional.
27
28 A Minimal Test Case
29 +++++++++++++++++++
30
31-Autopilot tests follow a similar pattern to other python test libraries: you must declare a class that derives from :class:`~autopilot.testcase.AutopilotTestCase`. A minimal test case looks like this::
32-
33- from autopilot.testcase import AutopilotTestCase
34-
35-
36- class MyTests(AutopilotTestCase):
37-
38- def test_something(self):
39- """An example test case that will always pass."""
40- self.assertTrue(True)
41+Autopilot tests follow a similar pattern to other python test libraries: you must declare a class that derives from :class:`~autopilot.testcase.AutopilotTestCase`. The *AutopilotTestCase* class contains methods to launch and introspect and application, along with the ability to make assertions. Thus, a minimal test case looks like this::
42+
43+ from autopilot.testcase import AutopilotTestCase
44+
45+
46+ class MyTests(AutopilotTestCase):
47+
48+ def test_something(self):
49+ """An example test case that will always pass."""
50+ self.assertTrue(True)
51
52 .. otto:: **Make your tests expressive!**
53
54- It's important to make sure that your tests express your *intent* as clearly as possible. We recommend choosing long, descriptive names for test functions and classes (even breaking :pep:`8`, if you need to), and give your tests a detailed docstring explaining exactly what you are trying to test. For more detailed advice on this point, see :ref:`write-expressive-tests`
55-
56-The Setup Phase
57-===============
58-
59-Before each test is run, the ``setUp`` method is called. Test authors may override this method to run any setup that needs to happen before the test is run. However, care must be taken when using the ``setUp`` method: it tends to hide code from the test case, which can make your tests less readable. It is our recommendation, therefore, that you use this feature sparingly. A more suitable alternative is often to put the setup code in a separate function or method and call it from the test function.
60-
61-Should you wish to put code in a setup method, it looks like this:
62-
63-.. code-block:: python
64-
65- from autopilot.testcase import AutopilotTestCase
66-
67-
68- class MyTests(AutopilotTestCase):
69-
70- def setUp(self):
71- super(MyTests, self).setUp()
72- # This code gets run before every test!
73-
74- def test_something(self):
75- """An example test case that will always pass."""
76- self.assertTrue(True)
77-
78-.. note::
79- Any action you take in the setup phase must be undone if it alters the system state. See :ref:`cleaning-up` for more details.
80+ It's important to make sure that your tests express your *intent* as clearly as possible. We recommend choosing long, descriptive names for test functions and classes (even breaking :pep:`8`, if you need to), and give your tests a detailed docstring explaining exactly what you are trying to test. For more detailed advice on this point, see :ref:`write-expressive-tests`
81+
82
83 Starting the Application
84 ++++++++++++++++++++++++
85
86-At the start of your test, you need to tell autopilot to launch your application. To do this, call :meth:`~autopilot.testcase.AutopilotTestCase.launch_test_application`. The minimum required argument to this method is the application name or path. If you pass in the application name, autopilot will look in the current working directory, and then will search the :envvar:`PATH` environment variable. Otherwise, autopilot looks for the executable at the path specified. Positional arguments to this method are passed to the executable being launched.
87+At the start of your test, you need to tell autopilot to launch your application. To do this, call :meth:`~autopilot.testcase.AutopilotTestCase.launch_test_application`. The minimum required argument to this method is the application name or path. If you pass in the application name, autopilot will look in the current working directory, and then will search the :envvar:`PATH` environment variable. Otherwise, autopilot looks for the executable at the path specified. Positional arguments to this method are passed to the executable being launched. For example, we can launch our Qt5 qml application by launching qmlscene with the page to our qml file::
88+
89+self.launch_test_application('qmlscene', 'my_scene.qml')
90
91 Autopilot will try and guess what type of application you are launching, and therefore what kind of introspection libraries it should load. Sometimes autopilot will need some assistance however. For example, at the time of writing, autopilot cannot automatically detect the introspection type for python / Qt4 applications. In that case, a :class:`RuntimeError` will be raised. To provide autopilot with a hint as to which introspection type to load, you can provide the ``app_type`` keyword argument. For example::
92
93- class MyTests(AutopilotTestCase):
94+ class MyTests(AutopilotTestCase):
95
96- def test_python_qt4_application(self):
97- self.app = self.launch_test_application(
98- 'my-pyqt4-app',
99- app_type='qt'
100- )
101+ def test_python_qt4_application(self):
102+ self.app = self.launch_test_application(
103+ 'my-pyqt4-app',
104+ app_type='qt'
105+ )
106
107 See the documentation for :meth:`~autopilot.testcase.AutopilotTestCase.launch_test_application` for more details.
108
109@@ -82,169 +51,151 @@
110
111 .. otto:: **What is a Proxy Object?**
112
113- Whenever you launch an application, autopilot gives you a "proxy object". These are instances of the :class:`~autopilot.introspection.ProxyBase` class, with all the data from your application mirrored in the proxy object instances. For example, if you have a proxy object for a push button class (say, ``QPushButton``, for example), the proxy object will have attribute to match every attribute in the class within your application. Autopilot automatically keeps the data in these instances up to date, so you can use them in your test assertions.
114-
115- User interfaces are made up of a tree of widgets, and autopilot represents these widgets as a tree of proxy objects. Proxy objects have a number of methods on them for selecting child objects in the introspection tree, so test authors can easily inspect the parts of the UI tree they care about.
116+ Whenever you launch an application, autopilot gives you a "proxy object". These are instances of the :class:`~autopilot.introspection.ProxyBase` class, with all the data from your application mirrored in the proxy object instances. For example, if you have a proxy object for a push button class (say, ``QPushButton``, for example), the proxy object will have attribute to match every attribute in the class within your application. Autopilot automatically keeps the data in these instances up to date, so you can use them in your test assertions.
117+
118+ User interfaces are made up of a tree of widgets, and autopilot represents these widgets as a tree of proxy objects. Proxy objects have a number of methods on them for selecting child objects in the introspection tree, so test authors can easily inspect the parts of the UI tree they care about. Later in the tutorial using :meth:`~autopilot.introspection.ProxyBase.print_tree` and the vis tool (see :ref:`visualise_introspection_tree`) will show you ways to examine a UI tree.
119+
120+Testcase Layout
121+===============
122+
123+Your autopilot test suite will grow to several files, possibly spread across several directories. We recommend that you follow this simple directory layout::
124+
125+ autopilot/
126+ autopilot/<projectname>/
127+ autopilot/<projectname>/tests/
128+
129+The ``autopilot`` folder can be anywhere within your project's source tree. It will likely contain a `setup.py <http://docs.python.org/3/distutils/setupscript.html>`_ file.
130+
131+The ``autopilot/<projectname>/`` folder is the base package for your autopilot tests. This folder, and all child folders, are python packages, and so must contain an `__init__.py file <http://docs.python.org/3/tutorial/modules.html#packages>`_. If you ever find yourself writing custom proxy classes (This is an advanced topic, and is covered here: :ref:`custom_proxy_classes`), they should be imported from this top-level package.
132+
133+Each test file should be named ``test_<component>.py``, where *<component>* is the logical component you are testing in that file. Test files must be written in the ``autopilot/<projectname>/tests/`` folder.
134+
135+So for example, here is a minimal layout following the above recommendations::
136+
137+ autopilot/exampleproject/__init__.py
138+ autopilot/exampleproject/tests/__init__.py
139+ autopilot/exampleproject/tests/test_example_component.py
140+
141+In the example above the **test_example_component.py** file would contain the actual testcases.
142+
143
144 A Simple Test
145 =============
146
147-To demonstrate the material covered so far, this selection will outline a simple application, and a single test for it. Instead of testing a third-party application, we will write the simplest possible application in Python and Qt4. The application, named 'testapp.py', is listed below::
148-
149- #!/usr/bin/env python
150-
151- from PyQt4 import QtGui
152- from sys import argv
153-
154- def main():
155- app = QtGui.QApplication(argv)
156- win = QtGui.QMainWindow()
157- win.show()
158- win.setWindowTitle("Hello World")
159- app.exec_()
160-
161- if __name__ == '__main__':
162- main()
163-
164-As you can see, this is a trivial application, but it serves our purpose. We will write a single autopilot test that asserts that the title of the main window is equal to the string "Hello World". Our test file is named "test_window.py", and contains the following code::
165-
166- from autopilot.testcase import AutopilotTestCase
167- from os.path import abspath, dirname, join
168- from testtools.matchers import Equals
169-
170- class MainWindowTitleTests(AutopilotTestCase):
171-
172- def launch_application(self):
173- """Work out the full path to the application and launch it.
174-
175- This is necessary since our test application will not be in $PATH.
176-
177- :returns: The application proxy object.
178-
179- """
180- full_path = abspath(join(dirname(__file__), '..', '..', 'testapp.py'))
181- return self.launch_test_application(full_path, app_type='qt')
182-
183- def test_main_window_title_string(self):
184- """The main window title must be 'Hello World'."""
185- app_root = self.launch_application()
186- main_window = app_root.select_single('QMainWindow')
187-
188- self.assertThat(main_window.windowTitle, Equals("Hello World"))
189-
190-
191-Note that we have made the test method as readable as possible by hiding the complexities of finding the full path to the application we want to test. Of course, if you can guarantee that the application is in :envvar:`PATH`, then this step becomes a lot simpler.
192+To demonstrate the material covered so far, this selection will outline a simple application and a single test for it. Instead of testing a third-party application, we will write the simplest possible application using Qt5 and qml. The application, named 'testapp.qml', is listed below::
193+
194+ import QtQuick 2.0
195+ import Ubuntu.Components 1.1
196+
197+ MainView {
198+
199+ width: units.gu(100)
200+ height: units.gu(75)
201+
202+ Page {
203+ title: "Hello World"
204+ }
205+ }
206+
207+
208+As you can see, this is a trivial application, but it serves our purpose. We will write a single autopilot test that asserts that the title of the main window is equal to the string "Hello World". Our test file is named "test_page.py", and contains the following code::
209+
210+ from autopilot.testcase import AutopilotTestCase
211+ from testtools.matchers import Equals
212+
213+
214+ class TestPage(AutopilotTestCase):
215+
216+ def launch_application(self):
217+ return self.launch_test_application(
218+ 'qmlscene',
219+ '../../testapp.qml',
220+ app_type='qt')
221+
222+ def test_page_title(self):
223+ """The main page title must be 'Hello World'."""
224+ app = self.launch_application()
225+ page = app.select_single('Page11')
226+
227+ self.assertThat(page.title, Equals("Hello World"))
228+
229+
230+Note that we have made the test method as readable as possible by hiding the complexities of finding the full path to the application we want to test (passing ../../testapp.qml). Of course, if you can guarantee that the application is in :envvar:`PATH`, then this step becomes a lot simpler.
231
232 The entire directory structure looks like this::
233
234- ./example/__init__.py
235- ./example/tests/__init__.py
236- ./example/tests/test_window.py
237- ./testapp.py
238+ ./testapp.qml
239+ ./autopilotqml/__init__.py
240+ ./autopilotqml/tests/__init__.py
241+ ./autopilotqml/tests/test_page.py
242
243 The ``__init__.py`` files are empty, and are needed to make these directories importable by python.
244
245 Running Autopilot
246 +++++++++++++++++
247
248-From the root of this directory structure, we can ask autopilot to list all the tests it can find::
249-
250- $ autopilot3 list example
251- Loading tests from: /home/thomi/code/canonical/autopilot/example_test
252-
253- example.tests.test_window.MainWindowTitleTests.test_main_window_title_string
254-
255-
256- 1 total tests.
257+From the root of this directory structure, we can ask autopilot to list all the tests it can find. Note that we use our python package name, which is the same as the root folder of the tests::
258+
259+ $ autopilot3 list autopilotqml
260+ Loading tests from: /home/nskaggs/projects/ubuntutouch/autopilotqml/tests/autopilot
261+
262+ autopilotqml.test_main.TestPage.test_page_title
263+
264+ 1 total tests.
265
266 Note that on the first line, autopilot will tell you where it has loaded the test definitions from. Autopilot will look in the current directory for a python package that matches the package name specified on the command line. If it does not find any suitable packages, it will look in the standard python module search path instead.
267
268 To run our test, we use the autopilot 'run' command::
269
270- $ autopilot3 run example
271- Loading tests from: /home/thomi/code/canonical/autopilot/example_test
272-
273- Tests running...
274-
275- Ran 1 test in 2.342s
276- OK
277+ $ autopilot3 run autopilotqml
278+ Loading tests from: /home/nskaggs/projects/ubuntutouch/autopilotqml/tests/autopilot
279+
280+ Tests running...
281+
282+ Ran 1 test in 2.503s
283+ OK
284
285 You will notice that the test application launches, and then dissapears shortly afterwards. Since this test doesn't manipulate the application in any way, this is a rather boring test to look at. If you ever want more output from the run command, you may specify the '-v' flag::
286
287- $ autopilot3 run -v example
288- Loading tests from: /home/thomi/code/canonical/autopilot/example_test
289-
290- Tests running...
291- 13:41:11.614 INFO globals:49 - ************************************************************
292- 13:41:11.614 INFO globals:50 - Starting test example.tests.test_window.MainWindowTitleTests.test_main_window_title_string
293- 13:41:11.693 INFO __init__:136 - Launching process: ['/home/thomi/code/canonical/autopilot/example_test/testapp.py', '-testability']
294- 13:41:11.699 INFO __init__:169 - Looking for autopilot interface for PID 12013 (and children)
295- 13:41:11.727 WARNING __init__:185 - Caught exception while searching for autopilot interface: 'DBusException("Could not get PID of name 'org.freedesktop.DBus': no such name",)'
296- 13:41:12.773 WARNING __init__:185 - Caught exception while searching for autopilot interface: 'DBusException("Could not get PID of name 'org.freedesktop.DBus': no such name",)'
297- 13:41:12.848 WARNING __init__:185 - Caught exception while searching for autopilot interface: 'RuntimeError("Could not find Autopilot interface on DBus backend '<session bus :1.5967 /com/canonical/Autopilot/Introspection>'",)'
298- 13:41:12.852 WARNING __init__:185 - Caught exception while searching for autopilot interface: 'RuntimeError("Could not find Autopilot interface on DBus backend '<session bus :1.5968 /com/canonical/Autopilot/Introspection>'",)'
299- 13:41:12.863 WARNING dbus:464 - Generating introspection instance for type 'Root' based on generic class.
300- 13:41:12.864 DEBUG dbus:338 - Selecting objects of type QMainWindow with attributes: {}
301- 13:41:12.871 WARNING dbus:464 - Generating introspection instance for type 'QMainWindow' based on generic class.
302- 13:41:12.886 INFO testcase:380 - waiting for process to exit.
303- 13:41:13.983 INFO testresult:35 - OK: example.tests.test_window.MainWindowTitleTests.test_main_window_title_string
304-
305- Ran 1 test in 2.370s
306- OK
307+ $ autopilot3 run -v autopilotqml
308+ 12:07:50.313 INFO run:235 - Autopilot Source Version: 1.5.0
309+ Autopilot Package Version: 1.5.0+15.04.20141031-0ubuntu1
310+ Loading tests from: /home/nskaggs/projects/ubuntutouch/autopilotqml/tests/autopilot
311+
312+ Tests running...
313+ 12:07:50.323 INFO _logging:40 - ************************************************************
314+ 12:07:50.323 INFO _logging:41 - Starting test autopilotqml.test_main.TestPage.test_page_title
315+ 12:07:50.528 INFO _launcher:373 - Attempting to launch application 'qmlscene' with arguments '../../testapp.qml' as a normal process
316+ 12:07:50.532 INFO _launcher:431 - Launching process: ['/usr/bin/qmlscene', '-testability', '../../testapp.qml']
317+ 12:07:51.739 INFO _launcher:544 - waiting for process to exit.
318+ 12:07:51.739 INFO _launcher:567 - Killing process 31240
319+ 12:07:51.785 INFO testresult:44 - OK: autopilotqml.test_main.TestPage.test_page_title
320+
321+ Ran 1 test in 1.462s
322+ OK
323
324 You may also specify '-v' twice for even more output (this is rarely useful for test authors however).
325
326-Both the 'list' and 'run' commands take a test id as an argument. You may be as generic, or as specific as you like. In the examples above, we will list and run all tests in the 'example' package (i.e.- all tests), but we could specify a more specific run criteria if we only wanted to run some of the tests. For example, to only run the single test we've written, we can execute::
327-
328- $ autopilot3 run example.tests.test_window.MainWindowTitleTests.test_main_window_title_string
329+Both the 'list' and 'run' commands take a test id as an argument. You may be as generic, or as specific as you like. In the examples above, we will list and run all tests in the 'autopilotqml' package (i.e.- all tests), but we could specify a more specific run criteria if we only wanted to run some of the tests. For example, to only run the single test we've written, we can execute::
330+
331+ $ autopilot3 run autopilotqml.test_main.TestPage.test_page_title
332+
333+or run just the tests for TestPage::
334+
335+ $ autopilot3 run autopilotqml.test_main.TestPage.test_page_title
336+
337+or run all the tests inside test_main::
338+
339+ $ autopilot3 run autopilotqml.test_main
340
341 .. _tut_test_with_interaction:
342
343 A Test with Interaction
344 =======================
345
346-Now lets take a look at some simple tests with some user interaction. First, update the test application with some input and output controls::
347-
348- #!/usr/bin/env python
349- # File: testapp.py
350-
351- from PyQt4 import QtGui
352- from sys import argv
353-
354- class AutopilotHelloWorld(QtGui.QWidget):
355- def __init__(self):
356- super(AutopilotHelloWorld, self).__init__()
357-
358- self.hello = QtGui.QPushButton("Hello")
359- self.hello.clicked.connect(self.say_hello)
360-
361- self.goodbye = QtGui.QPushButton("Goodbye")
362- self.goodbye.clicked.connect(self.say_goodbye)
363-
364- self.response = QtGui.QLabel("Response: None")
365-
366- grid = QtGui.QGridLayout()
367- grid.addWidget(self.hello, 0, 0)
368- grid.addWidget(self.goodbye, 0, 1)
369- grid.addWidget(self.response, 1, 0, 1, 2)
370- self.setLayout(grid)
371- self.show()
372- self.setWindowTitle("Hello World")
373-
374- def say_hello(self):
375- self.response.setText('Response: Hello')
376-
377- def say_goodbye(self):
378- self.response.setText('Response: Goodbye')
379-
380-
381- def main():
382- app = QtGui.QApplication(argv)
383- ahw = AutopilotHelloWorld()
384- app.exec_()
385-
386- if __name__ == '__main__':
387- main()
388+Now lets take a look at some simple tests with some user interaction. First, update the test application with some buttons:
389+
390+
391
392 We've reorganized the application code into a class to make the event handling easier. Then we added two input controls, the ``hello`` and ``goodbye`` buttons and an output control, the ``response`` label.
393
394@@ -252,65 +203,65 @@
395
396 Since we're adding a new category of tests, button response tests, we should organize them into a new class. Our tests module now looks like::
397
398- from autopilot.testcase import AutopilotTestCase
399- from os.path import abspath, dirname, join
400- from testtools.matchers import Equals
401-
402- from autopilot.input import Mouse
403- from autopilot.matchers import Eventually
404-
405- class HelloWorldTestBase(AutopilotTestCase):
406-
407- def launch_application(self):
408- """Work out the full path to the application and launch it.
409-
410- This is necessary since our test application will not be in $PATH.
411-
412- :returns: The application proxy object.
413-
414- """
415- full_path = abspath(join(dirname(__file__), '..', '..', 'testapp.py'))
416- return self.launch_test_application(full_path, app_type='qt')
417-
418-
419- class MainWindowTitleTests(HelloWorldTestBase):
420-
421- def test_main_window_title_string(self):
422- """The main window title must be 'Hello World'."""
423- app_root = self.launch_application()
424- main_window = app_root.select_single('AutopilotHelloWorld')
425-
426- self.assertThat(main_window.windowTitle, Equals("Hello World"))
427-
428-
429- class ButtonResponseTests(HelloWorldTestBase):
430-
431- def test_hello_response(self):
432- """The response text must be 'Response: Hello' after a Hello click."""
433- app_root = self.launch_application()
434- response = app_root.select_single('QLabel')
435- hello = app_root.select_single('QPushButton', text='Hello')
436-
437- self.mouse.click_object(hello)
438-
439- self.assertThat(response.text, Eventually(Equals('Response: Hello')))
440-
441- def test_goodbye_response(self):
442- """The response text must be 'Response: Goodbye' after a Goodbye
443- click."""
444- app_root = self.launch_application()
445- response = app_root.select_single('QLabel')
446- goodbye = app_root.select_single('QPushButton', text='Goodbye')
447-
448- self.mouse.click_object(goodbye)
449-
450- self.assertThat(response.text, Eventually(Equals('Response: Goodbye')))
451+ from autopilot.testcase import AutopilotTestCase
452+ from os.path import abspath, dirname, join
453+ from testtools.matchers import Equals
454+
455+ from autopilot.input import Mouse
456+ from autopilot.matchers import Eventually
457+
458+ class HelloWorldTestBase(AutopilotTestCase):
459+
460+ def launch_application(self):
461+ """Work out the full path to the application and launch it.
462+
463+ This is necessary since our test application will not be in $PATH.
464+
465+ :returns: The application proxy object.
466+
467+ """
468+ full_path = abspath(join(dirname(__file__), '..', '..', 'testapp.py'))
469+ return self.launch_test_application(full_path, app_type='qt')
470+
471+
472+ class MainWindowTitleTests(HelloWorldTestBase):
473+
474+ def test_main_window_title_string(self):
475+ """The main window title must be 'Hello World'."""
476+ app_root = self.launch_application()
477+ main_window = app_root.select_single('AutopilotHelloWorld')
478+
479+ self.assertThat(main_window.windowTitle, Equals("Hello World"))
480+
481+
482+ class ButtonResponseTests(HelloWorldTestBase):
483+
484+ def test_hello_response(self):
485+ """The response text must be 'Response: Hello' after a Hello click."""
486+ app_root = self.launch_application()
487+ response = app_root.select_single('QLabel')
488+ hello = app_root.select_single('QPushButton', text='Hello')
489+
490+ self.mouse.click_object(hello)
491+
492+ self.assertThat(response.text, Eventually(Equals('Response: Hello')))
493+
494+ def test_goodbye_response(self):
495+ """The response text must be 'Response: Goodbye' after a Goodbye
496+ click."""
497+ app_root = self.launch_application()
498+ response = app_root.select_single('QLabel')
499+ goodbye = app_root.select_single('QPushButton', text='Goodbye')
500+
501+ self.mouse.click_object(goodbye)
502+
503+ self.assertThat(response.text, Eventually(Equals('Response: Goodbye')))
504
505 In addition to the new class, ``ButtonResponseTests``, you'll notice a few other changes. First, two new import lines were added to support the new tests. Next, the existing ``MainWindowTitleTests`` class was refactored to subclass from a base class, ``HelloWorldTestBase``. The base class contains the ``launch_application`` method which is used for all test cases. Finally, the object type of the main window changed from ``QMainWindow`` to ``AutopilotHelloWorld``. The change in object type is a result of our test application being refactored into a class called ``AutopilotHelloWorld``.
506
507 .. otto:: **Be careful when identifing user interface controls**
508
509- Notice that our simple refactoring of the test application forced a change to the test for the main window. When developing application code, put a little extra thought into how the user interface controls will be identified in the tests. Identify objects with attributes that are likely to remain constant as the application code is developed.
510+ Notice that our simple refactoring of the test application forced a change to the test for the main window. When developing application code, put a little extra thought into how the user interface controls will be identified in the tests. Identify objects with attributes that are likely to remain constant as the application code is developed.
511
512 The ``ButtonResponseTests`` class adds two new tests, one for each input control. Each test identifies the user interface controls that need to be used, performs a single, specific action, and then verifies the outcome. In ``test_hello_response``, we first identify the ``QLabel`` control which contains the output we need to check. We then identify the ``Hello`` button. As the application has two ``QPushButton`` controls, we must further refine the ``select_single`` call by specifing an additional property. In this case, we use the button text. Next, an input action is triggered by instructing the ``mouse`` to click the ``Hello`` button. Finally, the test asserts that the response label text matches the expected string. The second test repeats the same process with the ``Goodbye`` button.
513
514@@ -321,6 +272,6 @@
515
516 .. otto:: **Use Eventually when asserting any user interface condition**
517
518- You may find that when running tests, the application is often ready with the outcome by the time autopilot is able to test the assertion without using :class:`~autopilot.matchers.Eventually`. However, this may not always be true when running your test suite on different hardware.
519+ You may find that when running tests, the application is often ready with the outcome by the time autopilot is able to test the assertion without using :class:`~autopilot.matchers.Eventually`. However, this may not always be true when running your test suite on different hardware.
520
521 .. TODO: Continue to discuss the issues with running tests & application in separate processes, and how the Eventually matcher helps us overcome these problems. Cover the various ways the matcher can be used.
522
523=== modified file 'docs/tutorial/what_is_autopilot.rst'
524--- docs/tutorial/what_is_autopilot.rst 2014-05-15 00:45:08 +0000
525+++ docs/tutorial/what_is_autopilot.rst 2015-01-27 20:14:32 +0000
526@@ -11,7 +11,7 @@
527 Where is Autopilot used?
528 ########################
529
530-Autopilot was designed to test the `Unity 3D <http://unity.ubuntu.com/>`_ shell. However, since then it has been used to test a number of other applications, including:
531+Autopilot was originally designed to test the `Unity 3D <http://unity.ubuntu.com/>`_ shell. However, since then it has been used to test a number of other applications, including:
532
533 * Core Ubuntu GUI applications.
534 * Mobile phone applications for the Ubuntu Phone & Ubuntu Tablet.

Subscribers

People subscribed via source and target branches