A full library to create dynamic model instances for testing purposes.
=Description= A full library to create dynamic model instances for testing purposes. =Installation= pip install django-dynamic-fixture or # Download zip file # Extract it # Execute in the extracted directory: python setup.py install =Upgrade= pip install django-dynamic-fixture --upgrade --no-deps =Motivation= * It is a TERRIBLE practice to use STATIC data in tests. * Create dynamic fixture for each model is boring and it produces a lot of replicated code. * It is a bad idea to use uncontrolled data in tests, like bizarre random data. =Comparison with another fixture tools= * We tried to use another fixture tools in a big Django project but the experience was not satisfactory. * Either they are incomplete, or bugged or it produces erratic tests, because they use random and uncontrolled data. * Also, the syntax of others tools is too verbose, which polutes the tests. * Complete, lean and practice documentation. * It is hard to debug tests with another tools. * List of other tools: http://djangopackages.com/grids/g/fixtures =Features= * Highly customizable: you can customize fields recursively * Deal with unique=True * Deal with cyclic dependencies (including self references) * Deal with many to many relationship (common M2M or M2M with additional data, i.e. through='table') * Deal with custom fields (specially if the custom field inherit of a django field) * It is supported for parallel tests * Deal with auto calculated attributes * It is easy to debug errors =Example of Usage= {{{ from django_dynamic_fixture import N, G, F, P #or use old default names: #from django_dynamic_fixture import new, get, DynamicFixture as F, print_field_values # Models: from django.db import models class ModelA(models.Model): pass class ModelY(models.Model): other_text = models.CharField() other_list = models.ManyToManyField('ModelA') class ModelX(models.Model): some_text = models.CharField(null=True) parent_left = models.ForeingKey('self') y_reference = models.ForeingKey('ModelY') list_a = models.ManyToManyField('ModelA') # Summary: # DynamicFixture (F): receive arguments and create model instances. # new: it is just a wrapper: it creates a F to create a not saved model instance. # get: basically, call the new method and save the instance. You can set ManyToMany fields only after the instance is saved. # Examples: instance_of_modelx = N(ModelX) assert instance_of_modelx.some_text != None assert instance_of_modelx.parent_left != None assert instance_of_modelx.parent_left.parent_left == None assert instance_of_modelx.id == None # new do not save the instance assert instance_of_modelx.y_reference.id != None # save dependencies by default assert len(instance_of_modelx.list_a.all()) == 0 # do not create many2many fields by default instance_of_modelx = N(ModelX, fill_nullable_fields=False) # default = True assert instance_of_modelx.some_text == None # you can ignore fields, but do not ignore required fields (with null=False). instance_of_modelx = G(ModelX, ignore_fields=['some_text']) assert instance_of_modelx.some_text == None # Very nice feature to work with trees instance_of_modelx = N(ModelX, number_of_laps=2) # default = 1 assert instance_of_modelx.parent_left != None assert instance_of_modelx.parent_left.parent_left != None assert instance_of_modelx.parent_left.parent_left.parent_left == None # This feature is specially useful to test search methods instance_of_modelx = N(ModelX, some_text='some fixed data') # attribute accepts static data assert instance_of_modelx.some_text == 'some fixed data' # Use this with attention. First, check for design mistakes instance_of_modelx = N(ModelX, id=99999) # you can define the id too assert instance_of_modelx.id == 99999 # You can create your own function to create data.. instance_of_modelx = N(ModelX, some_text=lambda field: field.name) # attribute accepts callables assert instance_of_modelx.some_text == 'some_text' # Use this with attention, you can get an error if you try to save an instance with not saved dependencies instance_of_modelx = N(ModelX, persist_dependencies=False) assert instance_of_modelx.y_reference.id == None instance_of_modelx = G(ModelX) # get save the model instance assert instance_of_modelx.id != None instance_of_modelx = G(ModelX, list_a=2) # Many2Many can receive a number of instances to be created assert len(instance_of_modelx.list_a.all()) == 2 instance_of_modelx = G(ModelX, list_a=[F(), F(), F()]) # Many2Many can receive a list of DynamicFixtures assert len(instance_of_modelx.list_a.all()) == 3 a = G(ModelA) instance_of_modelx = G(ModelX, list_a=[F(), a, F()]) # Many2Many can receive a list of instances assert len(instance_of_modelx.list_a.all()) == 3 # You can pass arguments to F (DynamicFixture) recursively. This works for ForeignKey and ManyToMany Fields! Easy and customizable! instance_of_modelx = G(ModelX, parent_left=F(other_text='wow', other_list=2)) assert len(instance_of_modelx.y_reference.other_list.all()) == 2 # Highly recursivable example: # X has a ForeignKey to A # A has a ForeignKey to B # B has a ForeignKey to C # this will create instances of C, B, A and X (in this order). Attribute d of C will be 'some value' G(X, a=F(b=F(c=F(d='some value')))) # for debug: P(instance_of_modelx) # Custom FileField: class ModelX(models.Model): my_file = models.FileField(upload_to='/') from tempfile import mkstemp pdf_a = File(open(mkstemp()[1], 'w'), name='a.pdf') G(ModelX, my_file=pdf_a) # Copier G(ModelX, my_field=C('my_field_y.x')) # Shelve/Library G(ModelX, my_field='x', shelve=True) G(ModelX, use_library=True) # Named Shelve G(ModelX, my_field='x', shelve='some name') G(ModelX, use_library=True, named_shelve='some name') # Shelving before all tests of a module: def setUpModule(): N(ModelX, my_field='x', shelve=True) # Shelving before all tests of all modules: Add in ddf_setup.py: N(ModelX, my_field='x', shelve=True) Run with nose plugin: python manage.py test --with-ddf-setup }}} == Decorators == {{{ from django_dynamic_fixture.decorators import skip_for_database, only_for_database, SQLITE3 @only_for_database(SQLITE3) def test_something1(self): pass @skip_for_database(SQLITE3) def test_something2(self): pass @only_for_database("some value used in settings.DATABASES['default']['ENGINE']") def test_something3(self): pass @skip_for_database("some value used in settings.DATABASES['default']['ENGINE']") def test_something4(self): pass }}} == Queries Module == {{{ python manage.py test --with-queries python manage.py count_queries_on_save }}} =Information about the logic of the library= ==List of exceptions== * UnsupportedFieldError: DynamicFixture does not support this field. * InvalidConfigurationError: The specified configuration for the field can not be applied or it is bugged. * InvalidManyToManyConfigurationError: M2M attribute configuration must be a number or a list of DynamicFixture or model instances. * BadDataError: The data passed to a field has some problem (not unique or invalid) or a required attribute is in ignore list. * InvalidCopierExpressionError: The specified expression used in a Copier is invalid. * InvalidModelError: Invalid Model: The class is not a model or it is abstract. ==DynamicFixture assume== * if a field has a default value, it does not have unique=True. * if a field has choices, it does not have unique=True. * if there is a cyclic dependency, in some part the relationship must be nullable. * if any of these requirements is a problem, you have to use customized values or override the behavior. ==DynamicFixture rules== ===General=== * The id (AutoField) is auto filled, unless you set a value to it. * if a field has default value, it will be used by default. * if a field has choices, it select each the first option by default. * it fill nullable fields with data unless fill_nullable_fields is False. * boolean fields will always receive False, unless it has default value. * null boolean fields will always receive None, unless it has default value. * Strings (CharField, Text, Url, Email...) and numbers (IntegerField, Float, Decimal...) are filled with a sequential and unique number for each test (1, '1', 2, '2'...). Each attribute has its own counter. * You can override the behavior of the string and number fillers, if you want to. ===Custom Attributes=== * if it receive a customized data, it do not care about attributes null, unique, default or choices. * if the specified fixture was bugged, it will raise an InvalidConfigurationError. ===Custom Fields=== * if it does not recognize the Field class, it will raise an UnsupportedFieldError. * if a field is not default in Django, but it inherits from a Django field, it will be filled using its config. * if a field is not default in Django and not related with a Django field, it will raise an UnsupportedFieldError. * Customized data is also valid for unsupported fields. ===Related Objects=== * ForeignKey will be filled by default, considering unicity of data. * It deal with cyclic dependencies, including self references (this avoid infinite recursion). * By default, the fixture create just one cycle, but it is possible to specify more maps with number_of_laps parameter. ===Many to Many relationship=== * To add models in a m2m relation, the model must be persisted, so use the 'get' method. * it expect to receive a parameter with the amount of instances that need to be created (using default configuration). * Also, it can receive a list with DynamicFixture (F) or model instances (for custom items). The size of the list is the number of items that will be created. * It works for default Many2Many and Many2Many with through. ===Ignoring fields=== * Useful when some fields need some calculation. For example, Django-MPTT models. * Ignored fields are propagated ONLY to self references. * Do not ignore required fields with 'get', only with 'new'. In other words, do not save an instance without a required field, unless you are expecting for an exception. ===Patterns=== * Use 'new' method for unit tests (not integration tests) for the main model. * Use 'ignore_fields' option to deal with fields filled by listeners. * Use custom values for unsupported fields. * Use 'number_of_laps' option to test trees. ===Anti-Patterns=== * Use a auto generated data in an assertion method. =Links of Comments= * http://www.reddit.com/r/django/comments/fv1re/django_dynamic_fixture * http://djangopackages.com/grids/g/fixtures/ * http://news.ycombinator.com/item?id=2275406 * http://pythonsmalltalk.blogspot.com/2011/02/django-dynamic-fixture.html * http://pythonsmalltalk.blogspot.com/2011/03/django-dynamic-fixture-121.html * http://pypi.python.org/pypi/django-dynamic-fixture/1.2 * http://code.activestate.com/pypm/django-dynamic-fixture * http://groups.google.com/group/django-mptt-dev/browse_thread/thread/d9eb9e22ad4aa656 * http://groups.google.com/group/django-users/browse_thread/thread/1346a60008c21a7b * http://groups.google.com/group/django-brasil/browse_thread/thread/757df09d3c3be81d * http://linux.softpedia.com/get/Internet/HTTP-WWW-/django-dynamic-fixture-68711.shtml =Change Log= ==Version 1.6.0== * 2012/03/31 (yyyy/mm/dd) * http://pypi.python.org/pypi/django-dynamic-fixture/1.6.0 * New features in DDF: * Copier: option to copy a generated value for a field to another one. Useful for denormalizated fields. * Shelve/Library: option to store a default configuration of a specific model. Useful to avoid replicated code of fixtures. Global option: DDF_USE_LIBRARY. * Named Shelve: option to store multiple configurations for a model in the library. * Nose plugin for global set up ==Version 1.5.1== * 2012/03/26 (yyyy/mm/dd) * http://pypi.python.org/pypi/django-dynamic-fixture/1.5.0 * New fatures in DDF: * global option: DDF_VALIDATE_ARGS that enable or disable field names. * BugFixes of 1.5.0: * F feature stop working. ==Version 1.5.0== * 2012/03/25 (yyyy/mm/dd) * http://pypi.python.org/pypi/django-dynamic-fixture/1.5.0 * New features in DDF: * global settings: DDF_DEFAULT_DATA_FIXTURE, DDF_FILL_NULLABLE_FIELDS, DDF_IGNORE_FIELDS, DDF_NUMBER_OF_LAPS, DDF_VALIDATE_MODELS * new data fixture that generates random data * new data fixture that use sequential numbers only for fields that have unique=True * P function now accept a list of model instances * Option to call model_instance.full_clean() validation method before saving the object (DDF_VALIDATE_MODELS). * Validate field names. If a invalid field name is passed as argument, it will raise an InvalidConfigurationError exception. * DateField options 'auto_add_now' and 'auto_add' are disabled if a custom value is used. ==Version 1.4.3== * 2012/02/23 (yyyy/mm/dd) * http://pypi.python.org/pypi/django-dynamic-fixture/1.4.3 * Bugfix in ForeignKeys with default values ==Version 1.4.2== * 2011/11/07 (yyyy/mm/dd) * http://pypi.python.org/pypi/django-dynamic-fixture/1.4.2 * Bugfix in FileSystemDjangoTestCase ==Version 1.4.1== * 2011/11/07 (yyyy/mm/dd) * http://pypi.python.org/pypi/django-dynamic-fixture/1.4.1 * New features in DDF: * Now you can set a custom File to a FileField and the file will be saved in the file storage system. * New features in FileSystemDjangoTestCase: * create_django_file_using_file create a django.File using the content of your file * create_django_file_with_temp_file now accepts a content attribute that will be saved in the generated file * Bugfix in FileSystemDjangoTestCase: * now create_django_file_with_temp_file close the generated file ==Version 1.4.0== * 2011/10/29 (yyyy/mm/dd) * http://pypi.python.org/pypi/django-dynamic-fixture/1.4.0 * New features: * Nose plugin to count queries on each test * Command line to count queries on the save (insert and update) of each model * Bugfixes in DDF: * Field with choice and default must use the default value, not the first choice value * Validation if the class is a models.Model instance * Showing all stack trace, when an exception occurs * Bugfixes in decorators: default values of database engines were not used correctly * Bugfixes in FileSystemDjangoTestCase tests ==Version 1.3.1== * 2011/10/03 (yyyy/mm/dd) * http://pypi.python.org/pypi/django-dynamic-fixture/1.3.1 * Bugfixes in FileSystemDjangoTestCase ==Version 1.3.0== * 2011/10/03 (yyyy/mm/dd) * http://pypi.python.org/pypi/django-dynamic-fixture/1.3.0 * New Feature: File System Django Test Case * New Feature: Decorators skip_for_database and only_for_database * Bugfix: Inheritance problems, before this version the DDF filled fields with the attribute parent_link ==Version 1.2.3== * 2011/06/27 (yyyy/mm/dd) * http://pypi.python.org/pypi/django-dynamic-fixture/1.2.3 * Bugfix in string truncation to max_length ==Version 1.2.2== * 2011/05/05 (yyyy/mm/dd) * http://pypi.python.org/pypi/django-dynamic-fixture/1.2.2 * Improvements in exception messages ==Version 1.2.1== * 2011/03/11 (yyyy/mm/dd) * http://pypi.python.org/pypi/django-dynamic-fixture/1.2.1 * Propagate ignored fields to self references * Refactoring + Bug fixes ==Version 1.2== * 2011/03/04 (yyyy/mm/dd) * http://pypi.python.org/pypi/django-dynamic-fixture/1.2 * New Feature: ignore_fields * New Feature: now it is possible to set the ID ==Version 1.1== * Bug fixes * http://pypi.python.org/pypi/django-dynamic-fixture/1.0 (1.0 has the 1.1 package) ==Version 1.0== * Initial version * Ready to use in big projects = Testing = * python manage.py test * python manage.py test --with-coverage --cover-inclusive --cover-html --cover-package=django_dynamic_fixture.* --with-queries = TODO List = * auto config of denormalizated fields * with_queries documentation and bugfixes (always print 0 queries) * related_name documentation or workaround * today, yesterday, tomorrow on fdf * bugfix in fdf or ddf: some files/directories are not deleted * tests with files in ddf * tests with proxy models * doc factory: example to generate models with validators in fields or in clean methods * tests with GenericRelations, GenericForeignKey etc * more tests with OneToOneField(parent_link=True) * documentation: examples of usage
File | Type | Py Version | Uploaded on | Size | # downloads |
---|---|---|---|---|---|
django-dynamic-fixture-1.6.0.zip (md5) | Python Egg | 2.6 | 2012-04-03 | 42KB | 136 |
- Author: paulocheque
- Home Page: https://bitbucket.org/paulocheque/django-dynamic-fixture
- Keywords: python django testing fixture
- License: MIT
- Categories
- Package Index Owner: paulocheque
- DOAP record: django-dynamic-fixture-1.6.0.xml
0 comments:
Post a Comment