How to conditionally patch a unit test in Python?










1














I'm test a method which makes external API calls which are mocked using a patch decorator. However, I'd like to be able to set an environment variable, MAKE_EXTERNAL_REQUESTS, such that if it is truthy, the patch is not applied.



Essentially, I'm looking to something similar to the skipIf and skipUnless decorators (cf. https://docs.python.org/3.7/library/unittest.html#skipping-tests-and-expected-failures), but like patchIf and patchUnless. As far as I can tell, though, no such functionality is built into patch. Should I write my own decorator?










share|improve this question


























    1














    I'm test a method which makes external API calls which are mocked using a patch decorator. However, I'd like to be able to set an environment variable, MAKE_EXTERNAL_REQUESTS, such that if it is truthy, the patch is not applied.



    Essentially, I'm looking to something similar to the skipIf and skipUnless decorators (cf. https://docs.python.org/3.7/library/unittest.html#skipping-tests-and-expected-failures), but like patchIf and patchUnless. As far as I can tell, though, no such functionality is built into patch. Should I write my own decorator?










    share|improve this question
























      1












      1








      1







      I'm test a method which makes external API calls which are mocked using a patch decorator. However, I'd like to be able to set an environment variable, MAKE_EXTERNAL_REQUESTS, such that if it is truthy, the patch is not applied.



      Essentially, I'm looking to something similar to the skipIf and skipUnless decorators (cf. https://docs.python.org/3.7/library/unittest.html#skipping-tests-and-expected-failures), but like patchIf and patchUnless. As far as I can tell, though, no such functionality is built into patch. Should I write my own decorator?










      share|improve this question













      I'm test a method which makes external API calls which are mocked using a patch decorator. However, I'd like to be able to set an environment variable, MAKE_EXTERNAL_REQUESTS, such that if it is truthy, the patch is not applied.



      Essentially, I'm looking to something similar to the skipIf and skipUnless decorators (cf. https://docs.python.org/3.7/library/unittest.html#skipping-tests-and-expected-failures), but like patchIf and patchUnless. As far as I can tell, though, no such functionality is built into patch. Should I write my own decorator?







      python unit-testing python-unittest python-unittest.mock






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Nov 12 '18 at 20:54









      Kurt Peek

      9,3671968140




      9,3671968140






















          2 Answers
          2






          active

          oldest

          votes


















          1














          You could write your own decorator that checks the environment variable and then calls the regular patch() if needed. You could also use the wraps argument in the regular patch() decorator.




          wraps: Item for the mock object to wrap. If wraps is not None then calling the Mock will pass the call through to the wrapped object (returning the real result). Attribute access on the mock will return a Mock object that wraps the corresponding attribute of the wrapped object (so attempting to access an attribute that doesn’t exist will raise an AttributeError).




          I think that means you could check the environment variable before setting the return variable in your test. If you don't set a return value, then calls will be passed through to the wrapped object.






          share|improve this answer




























            1














            You could just manually monkey patch out the dependency you're trying to mock/fake. This is what the patch decorator is doing under the hood (it also unpatches for you at the end of the function).



            def test_something():
            original_func = my_module.my_func
            if 'MAKE_EXTERNAL_REQUESTS' not in os.environ:
            # Monkey patch manually
            my_module.my_func = lambda x: x

            # do some logic that depends on my_module.my_func
            ...

            # Unpatch
            my_module.my_func = original_func


            Note that when you assign to the given module attribute you're trying to patch over, that patch is live for the entire session of your interpreter. So if you just patch and don't unpatch, that patch will be live for other tests in your test suite.



            If you're doing it one time this might be nice as a quick/dirty solution, if it's many times you might want a decorator to do it.



            HTH.






            share|improve this answer






















              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%2f53269945%2fhow-to-conditionally-patch-a-unit-test-in-python%23new-answer', 'question_page');

              );

              Post as a guest















              Required, but never shown

























              2 Answers
              2






              active

              oldest

              votes








              2 Answers
              2






              active

              oldest

              votes









              active

              oldest

              votes






              active

              oldest

              votes









              1














              You could write your own decorator that checks the environment variable and then calls the regular patch() if needed. You could also use the wraps argument in the regular patch() decorator.




              wraps: Item for the mock object to wrap. If wraps is not None then calling the Mock will pass the call through to the wrapped object (returning the real result). Attribute access on the mock will return a Mock object that wraps the corresponding attribute of the wrapped object (so attempting to access an attribute that doesn’t exist will raise an AttributeError).




              I think that means you could check the environment variable before setting the return variable in your test. If you don't set a return value, then calls will be passed through to the wrapped object.






              share|improve this answer

























                1














                You could write your own decorator that checks the environment variable and then calls the regular patch() if needed. You could also use the wraps argument in the regular patch() decorator.




                wraps: Item for the mock object to wrap. If wraps is not None then calling the Mock will pass the call through to the wrapped object (returning the real result). Attribute access on the mock will return a Mock object that wraps the corresponding attribute of the wrapped object (so attempting to access an attribute that doesn’t exist will raise an AttributeError).




                I think that means you could check the environment variable before setting the return variable in your test. If you don't set a return value, then calls will be passed through to the wrapped object.






                share|improve this answer























                  1












                  1








                  1






                  You could write your own decorator that checks the environment variable and then calls the regular patch() if needed. You could also use the wraps argument in the regular patch() decorator.




                  wraps: Item for the mock object to wrap. If wraps is not None then calling the Mock will pass the call through to the wrapped object (returning the real result). Attribute access on the mock will return a Mock object that wraps the corresponding attribute of the wrapped object (so attempting to access an attribute that doesn’t exist will raise an AttributeError).




                  I think that means you could check the environment variable before setting the return variable in your test. If you don't set a return value, then calls will be passed through to the wrapped object.






                  share|improve this answer












                  You could write your own decorator that checks the environment variable and then calls the regular patch() if needed. You could also use the wraps argument in the regular patch() decorator.




                  wraps: Item for the mock object to wrap. If wraps is not None then calling the Mock will pass the call through to the wrapped object (returning the real result). Attribute access on the mock will return a Mock object that wraps the corresponding attribute of the wrapped object (so attempting to access an attribute that doesn’t exist will raise an AttributeError).




                  I think that means you could check the environment variable before setting the return variable in your test. If you don't set a return value, then calls will be passed through to the wrapped object.







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Nov 14 '18 at 18:18









                  Don Kirkby

                  27.3k10127203




                  27.3k10127203























                      1














                      You could just manually monkey patch out the dependency you're trying to mock/fake. This is what the patch decorator is doing under the hood (it also unpatches for you at the end of the function).



                      def test_something():
                      original_func = my_module.my_func
                      if 'MAKE_EXTERNAL_REQUESTS' not in os.environ:
                      # Monkey patch manually
                      my_module.my_func = lambda x: x

                      # do some logic that depends on my_module.my_func
                      ...

                      # Unpatch
                      my_module.my_func = original_func


                      Note that when you assign to the given module attribute you're trying to patch over, that patch is live for the entire session of your interpreter. So if you just patch and don't unpatch, that patch will be live for other tests in your test suite.



                      If you're doing it one time this might be nice as a quick/dirty solution, if it's many times you might want a decorator to do it.



                      HTH.






                      share|improve this answer



























                        1














                        You could just manually monkey patch out the dependency you're trying to mock/fake. This is what the patch decorator is doing under the hood (it also unpatches for you at the end of the function).



                        def test_something():
                        original_func = my_module.my_func
                        if 'MAKE_EXTERNAL_REQUESTS' not in os.environ:
                        # Monkey patch manually
                        my_module.my_func = lambda x: x

                        # do some logic that depends on my_module.my_func
                        ...

                        # Unpatch
                        my_module.my_func = original_func


                        Note that when you assign to the given module attribute you're trying to patch over, that patch is live for the entire session of your interpreter. So if you just patch and don't unpatch, that patch will be live for other tests in your test suite.



                        If you're doing it one time this might be nice as a quick/dirty solution, if it's many times you might want a decorator to do it.



                        HTH.






                        share|improve this answer

























                          1












                          1








                          1






                          You could just manually monkey patch out the dependency you're trying to mock/fake. This is what the patch decorator is doing under the hood (it also unpatches for you at the end of the function).



                          def test_something():
                          original_func = my_module.my_func
                          if 'MAKE_EXTERNAL_REQUESTS' not in os.environ:
                          # Monkey patch manually
                          my_module.my_func = lambda x: x

                          # do some logic that depends on my_module.my_func
                          ...

                          # Unpatch
                          my_module.my_func = original_func


                          Note that when you assign to the given module attribute you're trying to patch over, that patch is live for the entire session of your interpreter. So if you just patch and don't unpatch, that patch will be live for other tests in your test suite.



                          If you're doing it one time this might be nice as a quick/dirty solution, if it's many times you might want a decorator to do it.



                          HTH.






                          share|improve this answer














                          You could just manually monkey patch out the dependency you're trying to mock/fake. This is what the patch decorator is doing under the hood (it also unpatches for you at the end of the function).



                          def test_something():
                          original_func = my_module.my_func
                          if 'MAKE_EXTERNAL_REQUESTS' not in os.environ:
                          # Monkey patch manually
                          my_module.my_func = lambda x: x

                          # do some logic that depends on my_module.my_func
                          ...

                          # Unpatch
                          my_module.my_func = original_func


                          Note that when you assign to the given module attribute you're trying to patch over, that patch is live for the entire session of your interpreter. So if you just patch and don't unpatch, that patch will be live for other tests in your test suite.



                          If you're doing it one time this might be nice as a quick/dirty solution, if it's many times you might want a decorator to do it.



                          HTH.







                          share|improve this answer














                          share|improve this answer



                          share|improve this answer








                          edited Nov 14 '18 at 18:35

























                          answered Nov 14 '18 at 18:28









                          Matt Messersmith

                          5,98921730




                          5,98921730



























                              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.





                              Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                              Please pay close attention to the following guidance:


                              • 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%2f53269945%2fhow-to-conditionally-patch-a-unit-test-in-python%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







                              這個網誌中的熱門文章

                              How to read a connectionString WITH PROVIDER in .NET Core?

                              Node.js Script on GitHub Pages or Amazon S3

                              Museum of Modern and Contemporary Art of Trento and Rovereto