Mock default attribute of SQLAlchemy












1















I have some issues to mock a SQLAlchemy object when using the default and onupdate fields in my models :



def get_uuid():
return str(uuid.uuid4())

def get_now():
return db.func.now()

class BaseModel(db.Model):
__abstract__ = True

id = db.Column(UUIDType(binary=False), primary_key=True, nullable=False, default=get_uuid)
created_at = db.Column(db.DateTime(timezone=True), default=get_now(), nullable=False, index=True)


The get_now() and get_uuid() behaviour do not change even when I try to mock them in my tests :



def test_create_source(client, mocker):

mock = mocker.MagicMock(return_value='123e4567-e89b-12d3-a456-426655440000')
mocker.patch('myproject.models.get_uuid', mock)
mock = mocker.MagicMock(return_value=datetime.datetime(2019, 1, 1))
mocker.patch('myproject.models.get_now', mock)

resp = client.post('/sources', json={'name': 'My source'})
assert resp.json == {
'name': 'My source',
'id': '123e4567-e89b-12d3-a456-426655440000',
'createdAt': 'Tue, 01 Jan 2019 00:00:00 GMT',
'updatedAt': 'Tue, 01 Jan 2019 00:00:00 GMT'
}

### Results :

> assert resp.json == {
'name': 'My source',
'id': '123e4567-e89b-12d3-a456-426655440000',
'createdAt': 'Tue, 01 Jan 2019 00:00:00 GMT',
'updatedAt': 'Tue, 01 Jan 2019 00:00:00 GMT'
}
E AssertionError: assert {'createdAt':...17:38:38 GMT'} == {'createdAt': ...00:00:00 GMT'}
E Omitting 1 identical items, use -vv to show
E Differing items:
E {'id': '8eb074c0-41e9-436c-8f71-b4c6842f4809'} != {'id': '123e4567-e89b-12d3-a456-426655440000'}
E {'createdAt': 'Fri, 18 Jan 2019 17:38:38 GMT'} != {'createdAt': 'Tue, 01 Jan 2019 00:00:00 GMT'}
E {'updatedAt': 'Fri, 18 Jan 2019 17:38:38 GMT'} != {'updatedAt': 'Tue, 01 Jan 2019 00:00:00 GMT'}
E Use -v to get the full diff

tests/test_sources.py:17: AssertionError


I think it's because my models and its attributes are already imported and evaluated before doing my test, so the mock is useless here. It's explained in the "Mocking class helpers" part of this post but I still was not able to fix my issue :(



The full runnable code to reproduce the problem is available here : https://github.com/ncrocfer/flaskmock



Do you have some ideas please ?










share|improve this question



























    1















    I have some issues to mock a SQLAlchemy object when using the default and onupdate fields in my models :



    def get_uuid():
    return str(uuid.uuid4())

    def get_now():
    return db.func.now()

    class BaseModel(db.Model):
    __abstract__ = True

    id = db.Column(UUIDType(binary=False), primary_key=True, nullable=False, default=get_uuid)
    created_at = db.Column(db.DateTime(timezone=True), default=get_now(), nullable=False, index=True)


    The get_now() and get_uuid() behaviour do not change even when I try to mock them in my tests :



    def test_create_source(client, mocker):

    mock = mocker.MagicMock(return_value='123e4567-e89b-12d3-a456-426655440000')
    mocker.patch('myproject.models.get_uuid', mock)
    mock = mocker.MagicMock(return_value=datetime.datetime(2019, 1, 1))
    mocker.patch('myproject.models.get_now', mock)

    resp = client.post('/sources', json={'name': 'My source'})
    assert resp.json == {
    'name': 'My source',
    'id': '123e4567-e89b-12d3-a456-426655440000',
    'createdAt': 'Tue, 01 Jan 2019 00:00:00 GMT',
    'updatedAt': 'Tue, 01 Jan 2019 00:00:00 GMT'
    }

    ### Results :

    > assert resp.json == {
    'name': 'My source',
    'id': '123e4567-e89b-12d3-a456-426655440000',
    'createdAt': 'Tue, 01 Jan 2019 00:00:00 GMT',
    'updatedAt': 'Tue, 01 Jan 2019 00:00:00 GMT'
    }
    E AssertionError: assert {'createdAt':...17:38:38 GMT'} == {'createdAt': ...00:00:00 GMT'}
    E Omitting 1 identical items, use -vv to show
    E Differing items:
    E {'id': '8eb074c0-41e9-436c-8f71-b4c6842f4809'} != {'id': '123e4567-e89b-12d3-a456-426655440000'}
    E {'createdAt': 'Fri, 18 Jan 2019 17:38:38 GMT'} != {'createdAt': 'Tue, 01 Jan 2019 00:00:00 GMT'}
    E {'updatedAt': 'Fri, 18 Jan 2019 17:38:38 GMT'} != {'updatedAt': 'Tue, 01 Jan 2019 00:00:00 GMT'}
    E Use -v to get the full diff

    tests/test_sources.py:17: AssertionError


    I think it's because my models and its attributes are already imported and evaluated before doing my test, so the mock is useless here. It's explained in the "Mocking class helpers" part of this post but I still was not able to fix my issue :(



    The full runnable code to reproduce the problem is available here : https://github.com/ncrocfer/flaskmock



    Do you have some ideas please ?










    share|improve this question

























      1












      1








      1


      1






      I have some issues to mock a SQLAlchemy object when using the default and onupdate fields in my models :



      def get_uuid():
      return str(uuid.uuid4())

      def get_now():
      return db.func.now()

      class BaseModel(db.Model):
      __abstract__ = True

      id = db.Column(UUIDType(binary=False), primary_key=True, nullable=False, default=get_uuid)
      created_at = db.Column(db.DateTime(timezone=True), default=get_now(), nullable=False, index=True)


      The get_now() and get_uuid() behaviour do not change even when I try to mock them in my tests :



      def test_create_source(client, mocker):

      mock = mocker.MagicMock(return_value='123e4567-e89b-12d3-a456-426655440000')
      mocker.patch('myproject.models.get_uuid', mock)
      mock = mocker.MagicMock(return_value=datetime.datetime(2019, 1, 1))
      mocker.patch('myproject.models.get_now', mock)

      resp = client.post('/sources', json={'name': 'My source'})
      assert resp.json == {
      'name': 'My source',
      'id': '123e4567-e89b-12d3-a456-426655440000',
      'createdAt': 'Tue, 01 Jan 2019 00:00:00 GMT',
      'updatedAt': 'Tue, 01 Jan 2019 00:00:00 GMT'
      }

      ### Results :

      > assert resp.json == {
      'name': 'My source',
      'id': '123e4567-e89b-12d3-a456-426655440000',
      'createdAt': 'Tue, 01 Jan 2019 00:00:00 GMT',
      'updatedAt': 'Tue, 01 Jan 2019 00:00:00 GMT'
      }
      E AssertionError: assert {'createdAt':...17:38:38 GMT'} == {'createdAt': ...00:00:00 GMT'}
      E Omitting 1 identical items, use -vv to show
      E Differing items:
      E {'id': '8eb074c0-41e9-436c-8f71-b4c6842f4809'} != {'id': '123e4567-e89b-12d3-a456-426655440000'}
      E {'createdAt': 'Fri, 18 Jan 2019 17:38:38 GMT'} != {'createdAt': 'Tue, 01 Jan 2019 00:00:00 GMT'}
      E {'updatedAt': 'Fri, 18 Jan 2019 17:38:38 GMT'} != {'updatedAt': 'Tue, 01 Jan 2019 00:00:00 GMT'}
      E Use -v to get the full diff

      tests/test_sources.py:17: AssertionError


      I think it's because my models and its attributes are already imported and evaluated before doing my test, so the mock is useless here. It's explained in the "Mocking class helpers" part of this post but I still was not able to fix my issue :(



      The full runnable code to reproduce the problem is available here : https://github.com/ncrocfer/flaskmock



      Do you have some ideas please ?










      share|improve this question














      I have some issues to mock a SQLAlchemy object when using the default and onupdate fields in my models :



      def get_uuid():
      return str(uuid.uuid4())

      def get_now():
      return db.func.now()

      class BaseModel(db.Model):
      __abstract__ = True

      id = db.Column(UUIDType(binary=False), primary_key=True, nullable=False, default=get_uuid)
      created_at = db.Column(db.DateTime(timezone=True), default=get_now(), nullable=False, index=True)


      The get_now() and get_uuid() behaviour do not change even when I try to mock them in my tests :



      def test_create_source(client, mocker):

      mock = mocker.MagicMock(return_value='123e4567-e89b-12d3-a456-426655440000')
      mocker.patch('myproject.models.get_uuid', mock)
      mock = mocker.MagicMock(return_value=datetime.datetime(2019, 1, 1))
      mocker.patch('myproject.models.get_now', mock)

      resp = client.post('/sources', json={'name': 'My source'})
      assert resp.json == {
      'name': 'My source',
      'id': '123e4567-e89b-12d3-a456-426655440000',
      'createdAt': 'Tue, 01 Jan 2019 00:00:00 GMT',
      'updatedAt': 'Tue, 01 Jan 2019 00:00:00 GMT'
      }

      ### Results :

      > assert resp.json == {
      'name': 'My source',
      'id': '123e4567-e89b-12d3-a456-426655440000',
      'createdAt': 'Tue, 01 Jan 2019 00:00:00 GMT',
      'updatedAt': 'Tue, 01 Jan 2019 00:00:00 GMT'
      }
      E AssertionError: assert {'createdAt':...17:38:38 GMT'} == {'createdAt': ...00:00:00 GMT'}
      E Omitting 1 identical items, use -vv to show
      E Differing items:
      E {'id': '8eb074c0-41e9-436c-8f71-b4c6842f4809'} != {'id': '123e4567-e89b-12d3-a456-426655440000'}
      E {'createdAt': 'Fri, 18 Jan 2019 17:38:38 GMT'} != {'createdAt': 'Tue, 01 Jan 2019 00:00:00 GMT'}
      E {'updatedAt': 'Fri, 18 Jan 2019 17:38:38 GMT'} != {'updatedAt': 'Tue, 01 Jan 2019 00:00:00 GMT'}
      E Use -v to get the full diff

      tests/test_sources.py:17: AssertionError


      I think it's because my models and its attributes are already imported and evaluated before doing my test, so the mock is useless here. It's explained in the "Mocking class helpers" part of this post but I still was not able to fix my issue :(



      The full runnable code to reproduce the problem is available here : https://github.com/ncrocfer/flaskmock



      Do you have some ideas please ?







      python testing flask sqlalchemy pytest






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Jan 18 at 17:45









      ncrocferncrocfer

      1,64541533




      1,64541533
























          1 Answer
          1






          active

          oldest

          votes


















          0















          I think it's because my models and its attributes are already imported and evaluated before doing my test, so the mock is useless here.




          Close: you've passed a reference to function get_uuid() as default to id and the result of calling get_now() (a function expression object) as default to created_at. Changing what those names are bound to in the module after the fact has no effect anymore on the columns, which already hold references to the objects themselves.



          In case of get_uuid() you should mock the function it is calling:



          mock = mocker.MagicMock(return_value='123e4567-e89b-12d3-a456-426655440000')
          mocker.patch('uuid.uuid4', mock)


          and in case of get_now() you should consider not calling it during model construction:



          class BaseModel(db.Model):
          ...
          created_at = db.Column(..., default=get_now, ...)


          so that you can again mock the actual function it is calling:



          mock = mocker.MagicMock(return_value=datetime.datetime(2019, 1, 1))
          mocker.patch('sqlalchemy.func.now', mock)


          On the other hand perhaps you should not be asserting anything about id and the timestamps in the test, since it seems that the important thing is to verify that the name was set correctly.






          share|improve this answer


























          • Thank you for your answzer and your explanation Ilja ! Your solution works very well for the uuid mock, but SQLAlchemy does not seem to like when I do not call the get_now function during the model construction : sqlalchemy.exc.StatementError: (builtins.TypeError) SQLite DateTime type only accepts Python datetime and date objects as input. [SQL: 'INSERT INTO sources (id, created_at, updated_at, name) VALUES (?, ?, ?, ?)'] [parameters: [{'name': 'My source'}]]

            – ncrocfer
            Jan 19 at 14:18











          • That's an oversight on my part, and a bit trickier to handle. Will have to ponder a bit. It's a result of the default function producing something other than a Python datetime during execution (SQLA special cases passing an sql expression object as default during construction, or so).

            – Ilja Everilä
            Jan 19 at 14:43













          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54259056%2fmock-default-attribute-of-sqlalchemy%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          0















          I think it's because my models and its attributes are already imported and evaluated before doing my test, so the mock is useless here.




          Close: you've passed a reference to function get_uuid() as default to id and the result of calling get_now() (a function expression object) as default to created_at. Changing what those names are bound to in the module after the fact has no effect anymore on the columns, which already hold references to the objects themselves.



          In case of get_uuid() you should mock the function it is calling:



          mock = mocker.MagicMock(return_value='123e4567-e89b-12d3-a456-426655440000')
          mocker.patch('uuid.uuid4', mock)


          and in case of get_now() you should consider not calling it during model construction:



          class BaseModel(db.Model):
          ...
          created_at = db.Column(..., default=get_now, ...)


          so that you can again mock the actual function it is calling:



          mock = mocker.MagicMock(return_value=datetime.datetime(2019, 1, 1))
          mocker.patch('sqlalchemy.func.now', mock)


          On the other hand perhaps you should not be asserting anything about id and the timestamps in the test, since it seems that the important thing is to verify that the name was set correctly.






          share|improve this answer


























          • Thank you for your answzer and your explanation Ilja ! Your solution works very well for the uuid mock, but SQLAlchemy does not seem to like when I do not call the get_now function during the model construction : sqlalchemy.exc.StatementError: (builtins.TypeError) SQLite DateTime type only accepts Python datetime and date objects as input. [SQL: 'INSERT INTO sources (id, created_at, updated_at, name) VALUES (?, ?, ?, ?)'] [parameters: [{'name': 'My source'}]]

            – ncrocfer
            Jan 19 at 14:18











          • That's an oversight on my part, and a bit trickier to handle. Will have to ponder a bit. It's a result of the default function producing something other than a Python datetime during execution (SQLA special cases passing an sql expression object as default during construction, or so).

            – Ilja Everilä
            Jan 19 at 14:43


















          0















          I think it's because my models and its attributes are already imported and evaluated before doing my test, so the mock is useless here.




          Close: you've passed a reference to function get_uuid() as default to id and the result of calling get_now() (a function expression object) as default to created_at. Changing what those names are bound to in the module after the fact has no effect anymore on the columns, which already hold references to the objects themselves.



          In case of get_uuid() you should mock the function it is calling:



          mock = mocker.MagicMock(return_value='123e4567-e89b-12d3-a456-426655440000')
          mocker.patch('uuid.uuid4', mock)


          and in case of get_now() you should consider not calling it during model construction:



          class BaseModel(db.Model):
          ...
          created_at = db.Column(..., default=get_now, ...)


          so that you can again mock the actual function it is calling:



          mock = mocker.MagicMock(return_value=datetime.datetime(2019, 1, 1))
          mocker.patch('sqlalchemy.func.now', mock)


          On the other hand perhaps you should not be asserting anything about id and the timestamps in the test, since it seems that the important thing is to verify that the name was set correctly.






          share|improve this answer


























          • Thank you for your answzer and your explanation Ilja ! Your solution works very well for the uuid mock, but SQLAlchemy does not seem to like when I do not call the get_now function during the model construction : sqlalchemy.exc.StatementError: (builtins.TypeError) SQLite DateTime type only accepts Python datetime and date objects as input. [SQL: 'INSERT INTO sources (id, created_at, updated_at, name) VALUES (?, ?, ?, ?)'] [parameters: [{'name': 'My source'}]]

            – ncrocfer
            Jan 19 at 14:18











          • That's an oversight on my part, and a bit trickier to handle. Will have to ponder a bit. It's a result of the default function producing something other than a Python datetime during execution (SQLA special cases passing an sql expression object as default during construction, or so).

            – Ilja Everilä
            Jan 19 at 14:43
















          0












          0








          0








          I think it's because my models and its attributes are already imported and evaluated before doing my test, so the mock is useless here.




          Close: you've passed a reference to function get_uuid() as default to id and the result of calling get_now() (a function expression object) as default to created_at. Changing what those names are bound to in the module after the fact has no effect anymore on the columns, which already hold references to the objects themselves.



          In case of get_uuid() you should mock the function it is calling:



          mock = mocker.MagicMock(return_value='123e4567-e89b-12d3-a456-426655440000')
          mocker.patch('uuid.uuid4', mock)


          and in case of get_now() you should consider not calling it during model construction:



          class BaseModel(db.Model):
          ...
          created_at = db.Column(..., default=get_now, ...)


          so that you can again mock the actual function it is calling:



          mock = mocker.MagicMock(return_value=datetime.datetime(2019, 1, 1))
          mocker.patch('sqlalchemy.func.now', mock)


          On the other hand perhaps you should not be asserting anything about id and the timestamps in the test, since it seems that the important thing is to verify that the name was set correctly.






          share|improve this answer
















          I think it's because my models and its attributes are already imported and evaluated before doing my test, so the mock is useless here.




          Close: you've passed a reference to function get_uuid() as default to id and the result of calling get_now() (a function expression object) as default to created_at. Changing what those names are bound to in the module after the fact has no effect anymore on the columns, which already hold references to the objects themselves.



          In case of get_uuid() you should mock the function it is calling:



          mock = mocker.MagicMock(return_value='123e4567-e89b-12d3-a456-426655440000')
          mocker.patch('uuid.uuid4', mock)


          and in case of get_now() you should consider not calling it during model construction:



          class BaseModel(db.Model):
          ...
          created_at = db.Column(..., default=get_now, ...)


          so that you can again mock the actual function it is calling:



          mock = mocker.MagicMock(return_value=datetime.datetime(2019, 1, 1))
          mocker.patch('sqlalchemy.func.now', mock)


          On the other hand perhaps you should not be asserting anything about id and the timestamps in the test, since it seems that the important thing is to verify that the name was set correctly.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Jan 19 at 10:49

























          answered Jan 19 at 10:44









          Ilja EveriläIlja Everilä

          23.8k33762




          23.8k33762













          • Thank you for your answzer and your explanation Ilja ! Your solution works very well for the uuid mock, but SQLAlchemy does not seem to like when I do not call the get_now function during the model construction : sqlalchemy.exc.StatementError: (builtins.TypeError) SQLite DateTime type only accepts Python datetime and date objects as input. [SQL: 'INSERT INTO sources (id, created_at, updated_at, name) VALUES (?, ?, ?, ?)'] [parameters: [{'name': 'My source'}]]

            – ncrocfer
            Jan 19 at 14:18











          • That's an oversight on my part, and a bit trickier to handle. Will have to ponder a bit. It's a result of the default function producing something other than a Python datetime during execution (SQLA special cases passing an sql expression object as default during construction, or so).

            – Ilja Everilä
            Jan 19 at 14:43





















          • Thank you for your answzer and your explanation Ilja ! Your solution works very well for the uuid mock, but SQLAlchemy does not seem to like when I do not call the get_now function during the model construction : sqlalchemy.exc.StatementError: (builtins.TypeError) SQLite DateTime type only accepts Python datetime and date objects as input. [SQL: 'INSERT INTO sources (id, created_at, updated_at, name) VALUES (?, ?, ?, ?)'] [parameters: [{'name': 'My source'}]]

            – ncrocfer
            Jan 19 at 14:18











          • That's an oversight on my part, and a bit trickier to handle. Will have to ponder a bit. It's a result of the default function producing something other than a Python datetime during execution (SQLA special cases passing an sql expression object as default during construction, or so).

            – Ilja Everilä
            Jan 19 at 14:43



















          Thank you for your answzer and your explanation Ilja ! Your solution works very well for the uuid mock, but SQLAlchemy does not seem to like when I do not call the get_now function during the model construction : sqlalchemy.exc.StatementError: (builtins.TypeError) SQLite DateTime type only accepts Python datetime and date objects as input. [SQL: 'INSERT INTO sources (id, created_at, updated_at, name) VALUES (?, ?, ?, ?)'] [parameters: [{'name': 'My source'}]]

          – ncrocfer
          Jan 19 at 14:18





          Thank you for your answzer and your explanation Ilja ! Your solution works very well for the uuid mock, but SQLAlchemy does not seem to like when I do not call the get_now function during the model construction : sqlalchemy.exc.StatementError: (builtins.TypeError) SQLite DateTime type only accepts Python datetime and date objects as input. [SQL: 'INSERT INTO sources (id, created_at, updated_at, name) VALUES (?, ?, ?, ?)'] [parameters: [{'name': 'My source'}]]

          – ncrocfer
          Jan 19 at 14:18













          That's an oversight on my part, and a bit trickier to handle. Will have to ponder a bit. It's a result of the default function producing something other than a Python datetime during execution (SQLA special cases passing an sql expression object as default during construction, or so).

          – Ilja Everilä
          Jan 19 at 14:43







          That's an oversight on my part, and a bit trickier to handle. Will have to ponder a bit. It's a result of the default function producing something other than a Python datetime during execution (SQLA special cases passing an sql expression object as default during construction, or so).

          – Ilja Everilä
          Jan 19 at 14:43




















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54259056%2fmock-default-attribute-of-sqlalchemy%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Liquibase includeAll doesn't find base path

          How to use setInterval in EJS file?

          Petrus Granier-Deferre