corebuilder — Setup devilry core data structures for tests

Deprecated since version 3.0: See How to unit-test Devilry.

devilry.project.develop.testhelpers.corebuilder is a module that makes it easy to create devilry.apps.core.models data for tests.

When to use

Use this for end-to-end tests and tests where you really need real data. Always try to mock objects instead of creating real data unless you are actually testing something that needs real data.

Howto

Each class in the core has a wrapper class in devilry.project.develop.testhelpers.corebuilder that makes it easy to perform operations that we need to setup tests. We call these wrappers builders, and they are all prefixed with the name of their corresponding core model and suffixed with Builder.

Using the builders is very easy:

from devilry.project.develop.testhelpers.corebuilder import NodeBuilder
duck1010builder = NodeBuilder('duckuniversity').add_subject('duck1010')
assert(duck1010builder.subject == Subject.objects.get(short_name='duck1010'))

They can all easily be updated with new attributes:

duck1010builder.update(long_name='DUCK1010 - Programming')
assert(duck1010builder.subject.long_name == 'DUCK1010 - Programming')

And they have sane defaults optimized for testing, so you can easily create a deeply nested core object. This creates the duck1010-subject with an active period that started 3 months ago and ends in 3 months, with a single assignment (week1), with a single group, with deadline one week from now with a single helloworld.txt delivery:

from devilry.project.develop.testhelpers.corebuilder import NodeBuilder
from devilry.project.develop.testhelpers.corebuilder import UserBuilder
peterpan = UserBuilder(username='peterpan')
helloworld_filemetabuilder = NodeBuilder('ducku')\
    .add_subject('duck1010')\
    .add_6month_active_period('current')\
    .add_assignment('week1')\
    .add_group(students=[peterpan.user])\
    .add_deadline_in_x_weeks(weeks=1)\
    .add_delivery()\
    .add_filemeta(filename='helloworld.txt', data='Hello world')

Since we often need to add a single subject or a single active period, we have shortcuts for that:

from devilry.project.develop.testhelpers.corebuilder import SubjectBuilder
from devilry.project.develop.testhelpers.corebuilder import PeriodBuilder
duck1010_builder = SubjectBuilder.quickadd_ducku_duck1010()
currentperiod_builder = PeriodBuilder.quickadd_ducku_duck1010_active()

Note

These shortcuts is not there just to save a couple of keystrokes. They are there to make sure we use a uniform test setup in 98% of our tests. As long as you just need a single subject or period, you MUST use these shortcuts (to get a patch accepted in Devilry).

Magic and defaults

The builders have very little magic, but they have some defaults that make sense when testing:

  • long_name is set to short_name when it is not specified explicitly.

  • All BaseNodes (the models with short and long name) takes the short_name as the first argument and the long_name as the second argument.

  • Time of delivery (for DeliveryBuilder and DealdineBuilder.add_delivery()) default to now.

  • Default publishing_time for assignments is now.

  • UserBuilder defaults to setting email to <username>@example.com.

These defaults are all handled in the constructor of their builder-class. All the defaults can be overridden by specifying a value for them.

Reload from DB

You often need to create an object that is changed by the code you are testing, and then check that the change has made it to the database. All our builders implement ReloadableDbBuilderInterface which includes reload_from_db().

ReloadableDbBuilderInterface

class devilry.project.develop.testhelpers.corebuilder.ReloadableDbBuilderInterface

All the builders implement this interface.

update(**attributes)

Update the object wrapped by the builder with the given attributes. Saves the object, and reloads it from the database.

reload_from_db(**attributes)

Reloads the object wrapped by the builder from the database. Perfect when you create an object that is changed by the code you are testing, and you want to check that the change has made it to the database.

UserBuilder

class devilry.project.develop.testhelpers.corebuilder.UserBuilder

Creates a User object for testing. Also creates the DevilryUserProfile, and methods for editing both the User and the profile.

__init__(username, full_name=None, email=None)

Creates a new User with password set to test, and the devilry.apps.core.models.DevilryUserProfile created.

Parameters:
  • username – The username of the new user.

  • full_name – Optional full_name. Defaults to None.

  • email – Optional email. Defaults to <username>@example.com.

update(**attributes)

Update the User with the given attributes. Reloads the object from the database.

update_profile(**attributes)

Update the devilry.apps.core.models.DevilryUserProfile with the given attributes. Reloads the object from the database.

NodeBuilder

class devilry.project.develop.testhelpers.corebuilder.NodeBuilder
node

The Node wrapped by this builder.

__init__(short_name, long_name=None, **kwargs)

Creates a new Node with the given attributes.

Parameters:
  • short_name – The short_name of the Node.

  • long_name – The long_name of the Node. Defaults to short_name if None.

  • kwargs – Other arguments for the Node constructor.

add_node(*args, **kwargs)

Adds a childnode to the node. args and kwargs are forwarded to NodeBuilder with kwargs['parentnode'] set to this node.

Return type:

NodeBuilder.

add_subject(*args, **kwargs)

Adds a subject to the node. args and kwargs are forwarded to SubjectBuilder with kwargs['parentnode'] set to this node.

Return type:

SubjectBuilder.

SubjectBuilder

class devilry.project.develop.testhelpers.corebuilder.SubjectBuilder
subject

The Subject wrapped by this builder.

__init__(short_name, long_name=None, **kwargs)

Creates a new Subject with the given attributes.

Parameters:
  • short_name – The short_name of the Subject.

  • long_name – The long_name of the Subject. Defaults to short_name if None.

  • kwargs – Other arguments for the Subject constructor.

classmethod quickadd_ducku_duck1010()

When we need just a single subject, we use this shortcut method instead of writing:

NodeBuilder('ducku').add_subject('duck1010')

This is not just to save a couple of letters, but also to promote a common setup for simple tests.

add_period(*args, **kwargs)

Adds a period to the subject. args and kwargs are forwarded to PeriodBuilder with kwargs['parentnode'] set to this subject.

Return type:

PeriodBuilder.

add_6month_active_period(*args, **kwargs)

Shortcut for adding add_period() with start_time 3*30 days ago, and end_time in 3*30 days. args and kwargs is forwarded to add_period, but with start_time and end_time set in kwargs.

If no short_name is provided, it defaults to active.

Return type:

PeriodBuilder.

add_6month_lastyear_period(*args, **kwargs)

Shortcut for adding add_period() with start_time 365-30*3 days ago, and end_time 365+3*30 days ago. args and kwargs is forwarded to add_period, but with start_time and end_time set in kwargs.

If no short_name is provided, it defaults to lastyear. :rtype: PeriodBuilder.

add_6month_nextyear_period(*args, **kwargs)

Shortcut for adding add_period() with start_time in 365-30*3 days, and end_time in 365+3*30 days. args and kwargs is forwarded to add_period, but with start_time and end_time set in kwargs.

If no short_name is provided, it defaults to nextyear.

Return type:

PeriodBuilder.

PeriodBuilder

class devilry.project.develop.testhelpers.corebuilder.PeriodBuilder
period

The Period wrapped by this builder.

__init__(short_name, long_name=None, **kwargs)

Creates a new Period with the given attributes.

Parameters:
  • short_name – The short_name of the Period.

  • long_name – The long_name of the Period. Defaults to short_name if None.

  • kwargs – Other arguments for the Period constructor.

add_assignment(*args, **kwargs)

Adds an assignment to the period. args and kwargs are forwarded to AssignmentBuilder with kwargs['parentnode'] set to this period.

Return type:

AssignmentBuilder.

classmethod quickadd_ducku_duck1010_active()

When we need just a single active period, we use this shortcut method instead of writing:

NodeBuilder('ducku').add_subject('duck1010').add_6month_active_period('current')

This is not just to save a couple of letters, but also to promote a common setup for simple tests.

AssignmentBuilder

class devilry.project.develop.testhelpers.corebuilder.AssignmentBuilder
assignment

The Assignment wrapped by this builder.

__init__(short_name, long_name=None, **kwargs)

Creates a new Assignment with the given attributes.

Parameters:
  • short_name – The short_name of the Assignment.

  • long_name – The long_name of the Assignment. Defaults to short_name if None.

  • publishing_time – The publishing_time of the Assignment. Defaults to now.

  • kwargs – Other arguments for the Assignment constructor.

add_group(*args, **kwargs)

Adds an assignment group to the period. args and kwargs are forwarded to AssignmentGroupBuilder with kwargs['parentnode'] set to this assignment.

Return type:

AssignmentGroupBuilder.

AssignmentGroupBuilder

class devilry.project.develop.testhelpers.corebuilder.AssignmentGroupBuilder
assignment_group

The AssignmentGroup wrapped by this builder.

__init__(students=[], candidates=[], examiners=[], **kwargs)

Creates a new AssignmentGroup with the given attributes.

Parameters:
  • students – Forwarded to add_students().

  • candidates – Forwarded to add_candidates().

  • examiners – Forwarded to add_examiners().

  • kwargs – Arguments for the AssignmentGroup constructor.

add_students(*users)

Add the given users as candidates without a candidate ID on this assignment group.

Returns:

self (to enable us to nest the method call).

add_examiners(*users)

Add the given users as examiners on this assignment group.

Returns:

self (to enable us to nest the method call).

add_students(*candidates)

Add the given candidates to this assignment group.

Parameters:

candidatesdevilry.apps.core.models.Candidate objects.

Returns:

self (to enable us to nest the method call).

add_deadline(*args, **kwargs)

Adds an deadline to the assignment. args and kwargs are forwarded to DeadlineBuilder with kwargs['assignment_group'] set to this assignment_group.

Return type:

AssignmentGroupBuilder.

add_deadline_in_x_weeks(weeks, *args, **kwargs)

Calls add_deadline() with kwargs[deadline] set weeks weeks in the future.

Return type:

AssignmentGroupBuilder.

add_deadline_x_weeks_ago(weeks, *args, **kwargs)

Calls add_deadline() with kwargs[deadline] set weeks weeks in the past.

Return type:

DeadlineBuilder.

DeadlineBuilder

class devilry.project.develop.testhelpers.corebuilder.DeadlineBuilder
deadline

The Deadline wrapped by this builder.

__init__(**kwargs)

Creates a new AssignmentGroup with the given attributes.

Parameters:

kwargs – Arguments for the Deadline constructor.

add_delivery(**kwargs)

Adds a delivery to the deadline. args and kwargs are forwarded to DeliveryBuilder with kwargs['deadline'] set to this deadline and kwargs['successful'] defaulting to True.

Parameters:

kwargs – Extra kwargs for the DeliveryBuilder constructor.

Return type:

DeliveryBuilder.

add_delivery_after_deadline(timedeltaobject, **kwargs)

Add a delivery timedeltaobject time after this deadline expires.

Shortcut that calls add_delivery() with kwargs['time_of_delivery'] set to deadline.deadline + timedeltaobject.

Example - add delivery 3 weeks and 2 hours after deadline:

from datetime import datetime, timedelta
deadlinebuilder = DeadlineBuilder(deadline=datetime(2010, 1, 1))
deadlinebuilder.add_delivery_after_deadline(timedelta(weeks=3, hours=2))
Parameters:

kwargs – Extra kwargs for the DeliveryBuilder constructor.

Return type:

DeliveryBuilder.

add_delivery_before_deadline(timedeltaobject, **kwargs)

Add a delivery timedeltaobject time before this deadline expires.

Shortcut that calls add_delivery() with kwargs['time_of_delivery'] set to deadline.deadline + timedeltaobject.

Example - add delivery 5 hours before deadline:

from datetime import datetime, timedelta
deadlinebuilder = DeadlineBuilder(deadline=datetime(2010, 1, 1))
deadlinebuilder.add_delivery_before_deadline(timedelta(hours=5))
Parameters:

kwargs – Extra kwargs for the DeliveryBuilder constructor.

Return type:

DeliveryBuilder.

add_delivery_x_hours_after_deadline(timedeltaobject, **kwargs)

Add a delivery hours hours after this deadline expires.

Shortcut that calls add_delivery_after_deadline() with timedeltaobject set to timedelta(hours=hours).

Parameters:
  • hours – Number of hours.

  • kwargs – Extra kwargs for the DeliveryBuilder constructor.

Return type:

DeliveryBuilder.

add_delivery_x_hours_before_deadline(timedeltaobject, **kwargs)

Add a delivery hours hours before this deadline expires.

Shortcut that calls add_delivery_before_deadline() with timedeltaobject set to timedelta(hours=hours).

Parameters:
  • hours – Number of hours.

  • kwargs – Extra kwargs for the DeliveryBuilder constructor.

Return type:

DeliveryBuilder.

DeliveryBuilder

class devilry.project.develop.testhelpers.corebuilder.DeliveryBuilder
delivery

The Delivery wrapped by this builder.

__init__(**kwargs)

Creates a new Delivery with the given attributes. If time_of_delivery is not provided, it defaults to now.

Parameters:

kwargs – Arguments for the Delivery constructor.

add_filemeta(**kwargs)

Adds a filemeta to the delivery. kwargs is forwarded to FilteMetaBuilder with kwargs['delivery'] set to this delivery.

Example:

deliverybuilder.add_filemeta(
    filename='test.txt',
    data='This is a test.'
)
Parameters:

kwargs – Kwargs for the FileMetaBuilder constructor.

Return type:

FileMetaBuilder.

add_feedback(**kwargs)

Adds a feedback to the delivery. kwargs is forwarded to StaticFeedbackBuilder with kwargs['delivery'] set to this delivery.

Example:

deliverybuilder.add_feedback(
    points=10,
    grade='10/100',
    is_passing_grade=False,
    saved_by=UserBuilder('testuser').user
)
Parameters:

kwargs – Kwargs for the StaticFeedbackBuilder constructor.

Return type:

StaticFeedbackBuilder.

add_passed_feedback(**kwargs)

Shortcut that adds a passed feedback to the delivery. kwargs is forwarded to add_feedback() with:

  • points=1

  • grade="Passed"

  • is_passing_grade=True

Example:

deliverybuilder.add_passed_feedback(saved_by=UserBuilder('testuser').user)
Parameters:

kwargs – Extra kwargs for add_feedback(). Is updated with :points, grade and is_passing_grade as documented above.

Return type:

StaticFeedbackBuilder.

add_failed_feedback(**kwargs)

Shortcut that adds a failed feedback to the delivery. kwargs is forwarded to add_feedback() with:

  • points=0

  • grade="Failed"

  • is_passing_grade=False

Example:

deliverybuilder.add_failed_feedback(saved_by=UserBuilder('testuser').user)
Parameters:

kwargs – Extra kwargs for add_feedback(). Is updated with :points, grade and is_passing_grade as documented above.

Return type:

StaticFeedbackBuilder.

FileMetaBuilder

class devilry.project.develop.testhelpers.corebuilder.FileMetaBuilder
filemeta

The FileMeta wrapped by this builder.

__init__(delivery, filename, data)

Creates a new FileMeta. Since FileMeta just points to files on disk, and creating those files requires iterators and extra stuff that is almost never needed for tests, we provide an easier method for creating files with FileMetaBuilder.

Parameters:
  • delivery – The Delivery object.

  • filename – A filename.

  • data – The file contents as a string.

StaticFeedbackBuilder

class devilry.project.develop.testhelpers.corebuilder.StaticFeedbackBuilder
feedback

The StaticFeedback wrapped by this builder.

__init__(**kwargs)

Creates a new StaticFeedback with the given attributes.

Parameters:

kwargs – Arguments for the StaticFeedback constructor.