This page is intended for tips and tricks for writing tests for Melange.
In a nutshell, tests are all about comparing the expected result with the actual result after application of a function/method under test. If you expect to get a particular value, you can assertEqual to the value of the actual result. If you expect that a particular exception should be raised, you can assertRaises the exception. If you expect that a template is used to render a web page, you can assertTemplateUsed the template. If you expect that a mail has been sent to a particular person, you can assertEmailSent(to=the_persons_email_address). On the other hand, if you cannot predict the exact value but you expect that the value will change after the application of a function/method under test, you can assertNotEqual to the value before the application of a function/method under test. In addition, you may also want to assert that the environment before your test is your expected/required testing environment.
Not only should normal situations/use cases be tested but also should other possible situations/use cases, which is particularly important for security tests. For example, we should not only test that an HTTP POST with a correct XSRF token can be successfully applied, we should also test that an HTTP POST with an incorrect XSRF token should be forbidden. For another example, for views that are only accessible to users with developer privilege, we should not only test that the views work if the current user is a developer, we should also test that the views are forbidden if a normal user or even an unregistered user tries to access the views.
Python unit test
All existing logic and models tests of Melange are built on Python unittest TestCase (unittest.TestCase). It gives you several assert methods as well as setUp and tearDown methods which will be respectively called before and after each test case, and also enables your tests to be run by almost all python test runners.
Usage
*
, tests.app.soc.modules.gsoc.logic.*
, tests.app.soc.modules.ghop.logic.*
Django TestCase
Most views tests are built on Django TestCase (django.test.TestCase) which extends Python unittest TestCase by adding extra functions, e.g. Django test client for view tests which works with GAE/Melange and fixtures which does not work with GAE without any modifications, patches or helpers.
Usage
*
, tests.app.soc.modules.gsoc.views.*
, tests.app.soc.modules.ghop.views.*
MailTestCase
All mails related tests are built on gaetestbed.mail.MailTestCase which collects all mails sent and can check if emails satisfying some criteria are sent.
Usage
*
, tests.app.soc.modules.gsoc.tasks.*
, tests.app.soc.modules.ghop.tasks.*
TaskQueueTestCase
All task queues related tests are built on gaetestbed.mail.TaskQueueTestCase which can check if task queues satisfying some criteria are spawned.
Usage
*
, tests.app.soc.modules.gsoc.tasks.*
, tests.app.soc.modules.ghop.tasks.*
The name of the test module for testing a codebase module is the name of the codebase module plus a prefix of “test_
” (without quote), e.g. test_base.py. The name of the test class for testing a codebase class is the name of the codebase class plus a suffix of “Test” (without quotes), e.g. BaseTest. The name of the test method for testing a codebase method is the name of the codebase method with the first letter capitalized plus a prefix of “test” (without quote), e.g. testGetForFields.
All tests are in a separate directory
MELANGE-ROOT/tests
The organization of the tests for a module within the directory is the same with the corresponding module in the codebase but with a “test_
” (without quote) prefix before the module name. For example, the tests for the module app.soc.logic.models.base are placed in the module tests.app.soc.logic.models.test_base.
All are in tests.test_utils module.
MockRequest
This is a shared dummy request object to mock common aspects of a request similar to Django test client.
StuboutHelper
This is a helper class using the the StubOutForTesting class of pymox. It is mainly used to stub out/ replace a function or a method of the codebase with a dummy one during the test.
DjangoTestCase
It extends Django TestCase in order to extend its functions as well as remove the functions which are not supported by Google App Engine, e.g. database flush and fixtures loading without the assistance of Google App Engine Helper or patches for Django. It can be used to do view tests in the replacement of Python unittest TestCase because of its extra assert methods and test client for view tests.
MailTestCase
It extends gaetestbed.mail.MailTestCase by subclassing unittest.TestCase so that all its subclasses need not subclass unittest.TestCase in their code and by overriding assertEmailSent method to extend its functions.
TaskQueueTestCase
It extends gaetestbed.taskqueue.TaskQueueTestCase by subclassing unittest.TestCase so that all its subclasses need not subclass unittest.TestCase in their code.
Data seeder
Data is usually needed to conduct the tests. The seed functions are here to seed data for tests.
{"name": "John Smith", "age": RandomUniformDistributionIntegerProvider(min=0, max=80)}
It is in MELANGE-ROOT/eggs.
It is in MELANGE-ROOT/eggs.
Suppose that the current working directory is the root of your local clone of the Melange project. You can run all the tests by running the following command
bin/run-tests
If you want to get test coverage report, add option --coverage, i.e.
bin/run-tests --coverage
The default covered package is the whole soc package (--cover-package=soc.).
Internally, it uses nose test runner. So, all the options of nosetests should also work here. For example, you can use -e REGEX or --exclude=REGEX to exclude tests that match the regular expression REGEX. E.g. to ignore the functional tests run:
bin/run-tests -e test_functional
Note: prebuilt nose library (MELANGE-ROOT/eggs/nose-0.11.3-py2.5.egg) does not include the coverage plugin, so you cannot use the -with--coverage option to get test coverage report.
If you want to just run all the tests in one module (say test_base), execute
bin/run-tests tests/app/soc/logic/models/test_base.py
or
bin/run-tests tests.app.soc.logic.models.test_base
To just run a group of test cases in one module (say test_base.BaseTest), execute
bin/run-tests tests/app/soc/logic/models/test_base.py:BaseTest
or
bin/run-tests tests.app.soc.logic.models.test_base:BaseTest
To just run a single test case in one module (say test_base.BaseTest.testGetForFields), execute
bin/run-tests tests/app/soc/logic/models/test_base.py:BaseTest.testGetForFields
or
bin/run-tests tests.app.soc.logic.models.test_base:BaseTest.testGetForFields
Note: although running only one test module or a group of test cases can save much time, it is desirable to run all the tests at least once before merging your code just in case it breaks others' code.
The test environment is set up by the tests/run.py file. It adds libraries to Python path, sets up Python environ variables, GAE datastores, memcache etc, creates a Melange core, registers callbacks, and runs tests through nose.