Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you use factory_boy to model a MongoEngine EmbeddedDocument?

I'm trying to use factory_boy to help generate some MongoEngine documents for my tests. I'm having trouble defining EmbeddedDocumentField objects.

Here's my MongoEngine Document:

class Comment(EmbeddedDocument):
    content = StringField()
    name = StringField(max_length=120)

class Post(Document):
    title = StringField(required=True)
    tags = ListField(StringField(), required=True)
    comments = ListField(EmbeddedDocumentField(Comment))

Here's my partially completed factory_boy Factory:

class CommentFactory(factory.Factory):
    FACTORY_FOR = Comment
    content = "Platinum coins worth a trillion dollars are great"
    name = "John Doe"

class BlogFactory(factory.Factory):
    FACTORY_FOR = Blog
    title = "On Using MongoEngine with factory_boy"
    tags = ['python', 'mongoengine', 'factory-boy', 'django']
    comments = [factory.SubFactory(CommentFactory)] # this doesn't work

Any ideas how to specify the comments field? The problem is that factory-boy attempts to create the Comment EmbeddedDocument.

like image 910
Jashugan Avatar asked Jan 15 '13 20:01

Jashugan


2 Answers

I'm not sure if this is what you want but I just started looking at this problem and this seems to work:

from mongoengine import EmbeddedDocument, Document, StringField, ListField, EmbeddedDocumentField
import factory

class Comment(EmbeddedDocument):
    content = StringField()
    name = StringField(max_length=120)

class Post(Document):
    title = StringField(required=True)
    tags = ListField(StringField(), required=True)
    comments = ListField(EmbeddedDocumentField(Comment))


class CommentFactory(factory.Factory):
    FACTORY_FOR = Comment
    content = "Platinum coins worth a trillion dollars are great"
    name = "John Doe"

class PostFactory(factory.Factory):
    FACTORY_FOR = Post
    title = "On Using MongoEngine with factory_boy"
    tags = ['python', 'mongoengine', 'factory-boy', 'django']
    comments = factory.LazyAttribute(lambda a: [CommentFactory()])

>>> b = PostFactory()
>>> b.comments[0].content
'Platinum coins worth a trillion dollars are great'

I wouldn't be surprised if I'm missing something though.

like image 51
Andrew Magee Avatar answered Nov 14 '22 13:11

Andrew Magee


The way that I'm doing it right now is to prevent the Factories based on EmbeddedDocuments from building. So, I've setup up an EmbeddedDocumentFactory, like so:

class EmbeddedDocumentFactory(factory.Factory):

    ABSTRACT_FACTORY = True

    @classmethod
    def _prepare(cls, create, **kwargs):                                        
        return super(EmbeddedDocumentFactory, cls)._prepare(False, **kwargs)

Then I inherit from that to create factories for EmbeddedDocuments:

class CommentFactory(EmbeddedDocumentFactory):

    FACTORY_FOR = Comment

    content = "Platinum coins worth a trillion dollars are great"
    name = "John Doe"

This may not be the best solution, so I'll wait on someone else to respond before accepting this as the answer.

like image 2
Jashugan Avatar answered Nov 14 '22 11:11

Jashugan