Scala: How to Unit Test a function I have which makes an API call using mock/stub?












0















I have a function which makes an API call to a an external API



Let us say the function does something simple like the below. FYI need to import scala.io.Source



  def myFunction(apiRequestUrl: String) : MyObject = {
val response: String = Source.fromURL(apiRequestUrl).mkString
val formatedResponse: MyObject = formatResponseFunction(response)
formatedResponse
}


I know that some error codes I could receive are 400, 404, etc... and I'd like to just handle any error code generically from this. How could this be done? The examples I've found seem to be testing a person's own built REST API, and not function calls to someone else's external API










share|improve this question



























    0















    I have a function which makes an API call to a an external API



    Let us say the function does something simple like the below. FYI need to import scala.io.Source



      def myFunction(apiRequestUrl: String) : MyObject = {
    val response: String = Source.fromURL(apiRequestUrl).mkString
    val formatedResponse: MyObject = formatResponseFunction(response)
    formatedResponse
    }


    I know that some error codes I could receive are 400, 404, etc... and I'd like to just handle any error code generically from this. How could this be done? The examples I've found seem to be testing a person's own built REST API, and not function calls to someone else's external API










    share|improve this question

























      0












      0








      0


      1






      I have a function which makes an API call to a an external API



      Let us say the function does something simple like the below. FYI need to import scala.io.Source



        def myFunction(apiRequestUrl: String) : MyObject = {
      val response: String = Source.fromURL(apiRequestUrl).mkString
      val formatedResponse: MyObject = formatResponseFunction(response)
      formatedResponse
      }


      I know that some error codes I could receive are 400, 404, etc... and I'd like to just handle any error code generically from this. How could this be done? The examples I've found seem to be testing a person's own built REST API, and not function calls to someone else's external API










      share|improve this question














      I have a function which makes an API call to a an external API



      Let us say the function does something simple like the below. FYI need to import scala.io.Source



        def myFunction(apiRequestUrl: String) : MyObject = {
      val response: String = Source.fromURL(apiRequestUrl).mkString
      val formatedResponse: MyObject = formatResponseFunction(response)
      formatedResponse
      }


      I know that some error codes I could receive are 400, 404, etc... and I'd like to just handle any error code generically from this. How could this be done? The examples I've found seem to be testing a person's own built REST API, and not function calls to someone else's external API







      scala unit-testing mocking






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Nov 14 '18 at 23:09









      alexalex

      6428




      6428
























          2 Answers
          2






          active

          oldest

          votes


















          1














          First of all, you have to inject the dependencies you want to replace with mocks, static calls within a function can't be mocked/stubbed



          In your Scenario you have another problem that is: yor dependency Source is an object, and you can't mock objects, only non-final classes and traits. Furthermore, is considered a bad practice to mock 3rd party APIs...



          A good way to solve all those problems would be to re-write the code like



          trait HttpAdapter {
          def fromUrl(apiRequestUrl: String): String = Source.fromURL(apiRequestUrl).mkString
          }

          object HttpAdapter extends HttpAdapter

          def myFunction(apiRequestUrl: String, httpAdapter: HttpAdapter = HttpAdapter) : MyObject = {
          val response: String = httpAdapter.fromUrl(apiRequestUrl)
          val formatedResponse: MyObject = formatResponseFunction(response)
          formatedResponse
          }

          "myFunction" should "work" in {
          //create mock
          val http = mock[HttpAdapter]

          //stub mock
          http.fromUrl("some url") shouldReturn "result"

          //inject mock
          myFunction("some url", http) shouldBe MyObject
          }


          Notice I've wrapped the third party API in a class I have full control over (HttpAdapter) and then I mock that one



          Then I inject httpAdapter as a parameter, but I provide a default value so the callers do not need to worry about it, while I can still override it with a mock or stub in test code



          Also note that I've use mockito-scala instead of regular mockito, so the stubbing syntax is different






          share|improve this answer


























          • Thank you for a great response. I'm still new so thanks for sharing some best practices as well. Would that trait example be known as an adapter pattern? I noticed that you wrote "object HttpAdapter extends " but did not say what it extends. Could you complete that?

            – alex
            Dec 31 '18 at 16:59






          • 1





            Glad it helped! - oh yes, copy paste error, I fixed the extends now - if it solves your problem mark it as the correct answer so it can benefit more people!

            – Bruno
            Jan 1 at 1:24













          • Yeah, this is awesome. Thank you.

            – alex
            Jan 2 at 21:35











          • After trying this out with regular Mockito framework I get an error stating "String cannot be returned by get$default$2() get$default$2() should return int".

            – alex
            Jan 8 at 23:42











          • Regular mockito does not play well with some scala features, that error seems to be related to a default argument returning a value class (just a wild guess here)... could you try with mockito-scala? if it doesn't work there, please create an issue in github and provide enough code so it can be easily copy and pasted to reproduce the error

            – Bruno
            Jan 9 at 9:04



















          1














          For mocking the external service calls, you could use Mockito which is a mocking framework. Mockito is very simple to use and you can provide stubs for your external calls. For example



          val m = mock[io.Source.type]


          Here you mock the Source and then you provide your desired behaviour on invocation of the fromUrl function.
          i.e



          when(m.fromUrl("external service url")) thenReturn("result")





          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%2f53310108%2fscala-how-to-unit-test-a-function-i-have-which-makes-an-api-call-using-mock-stu%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














            First of all, you have to inject the dependencies you want to replace with mocks, static calls within a function can't be mocked/stubbed



            In your Scenario you have another problem that is: yor dependency Source is an object, and you can't mock objects, only non-final classes and traits. Furthermore, is considered a bad practice to mock 3rd party APIs...



            A good way to solve all those problems would be to re-write the code like



            trait HttpAdapter {
            def fromUrl(apiRequestUrl: String): String = Source.fromURL(apiRequestUrl).mkString
            }

            object HttpAdapter extends HttpAdapter

            def myFunction(apiRequestUrl: String, httpAdapter: HttpAdapter = HttpAdapter) : MyObject = {
            val response: String = httpAdapter.fromUrl(apiRequestUrl)
            val formatedResponse: MyObject = formatResponseFunction(response)
            formatedResponse
            }

            "myFunction" should "work" in {
            //create mock
            val http = mock[HttpAdapter]

            //stub mock
            http.fromUrl("some url") shouldReturn "result"

            //inject mock
            myFunction("some url", http) shouldBe MyObject
            }


            Notice I've wrapped the third party API in a class I have full control over (HttpAdapter) and then I mock that one



            Then I inject httpAdapter as a parameter, but I provide a default value so the callers do not need to worry about it, while I can still override it with a mock or stub in test code



            Also note that I've use mockito-scala instead of regular mockito, so the stubbing syntax is different






            share|improve this answer


























            • Thank you for a great response. I'm still new so thanks for sharing some best practices as well. Would that trait example be known as an adapter pattern? I noticed that you wrote "object HttpAdapter extends " but did not say what it extends. Could you complete that?

              – alex
              Dec 31 '18 at 16:59






            • 1





              Glad it helped! - oh yes, copy paste error, I fixed the extends now - if it solves your problem mark it as the correct answer so it can benefit more people!

              – Bruno
              Jan 1 at 1:24













            • Yeah, this is awesome. Thank you.

              – alex
              Jan 2 at 21:35











            • After trying this out with regular Mockito framework I get an error stating "String cannot be returned by get$default$2() get$default$2() should return int".

              – alex
              Jan 8 at 23:42











            • Regular mockito does not play well with some scala features, that error seems to be related to a default argument returning a value class (just a wild guess here)... could you try with mockito-scala? if it doesn't work there, please create an issue in github and provide enough code so it can be easily copy and pasted to reproduce the error

              – Bruno
              Jan 9 at 9:04
















            1














            First of all, you have to inject the dependencies you want to replace with mocks, static calls within a function can't be mocked/stubbed



            In your Scenario you have another problem that is: yor dependency Source is an object, and you can't mock objects, only non-final classes and traits. Furthermore, is considered a bad practice to mock 3rd party APIs...



            A good way to solve all those problems would be to re-write the code like



            trait HttpAdapter {
            def fromUrl(apiRequestUrl: String): String = Source.fromURL(apiRequestUrl).mkString
            }

            object HttpAdapter extends HttpAdapter

            def myFunction(apiRequestUrl: String, httpAdapter: HttpAdapter = HttpAdapter) : MyObject = {
            val response: String = httpAdapter.fromUrl(apiRequestUrl)
            val formatedResponse: MyObject = formatResponseFunction(response)
            formatedResponse
            }

            "myFunction" should "work" in {
            //create mock
            val http = mock[HttpAdapter]

            //stub mock
            http.fromUrl("some url") shouldReturn "result"

            //inject mock
            myFunction("some url", http) shouldBe MyObject
            }


            Notice I've wrapped the third party API in a class I have full control over (HttpAdapter) and then I mock that one



            Then I inject httpAdapter as a parameter, but I provide a default value so the callers do not need to worry about it, while I can still override it with a mock or stub in test code



            Also note that I've use mockito-scala instead of regular mockito, so the stubbing syntax is different






            share|improve this answer


























            • Thank you for a great response. I'm still new so thanks for sharing some best practices as well. Would that trait example be known as an adapter pattern? I noticed that you wrote "object HttpAdapter extends " but did not say what it extends. Could you complete that?

              – alex
              Dec 31 '18 at 16:59






            • 1





              Glad it helped! - oh yes, copy paste error, I fixed the extends now - if it solves your problem mark it as the correct answer so it can benefit more people!

              – Bruno
              Jan 1 at 1:24













            • Yeah, this is awesome. Thank you.

              – alex
              Jan 2 at 21:35











            • After trying this out with regular Mockito framework I get an error stating "String cannot be returned by get$default$2() get$default$2() should return int".

              – alex
              Jan 8 at 23:42











            • Regular mockito does not play well with some scala features, that error seems to be related to a default argument returning a value class (just a wild guess here)... could you try with mockito-scala? if it doesn't work there, please create an issue in github and provide enough code so it can be easily copy and pasted to reproduce the error

              – Bruno
              Jan 9 at 9:04














            1












            1








            1







            First of all, you have to inject the dependencies you want to replace with mocks, static calls within a function can't be mocked/stubbed



            In your Scenario you have another problem that is: yor dependency Source is an object, and you can't mock objects, only non-final classes and traits. Furthermore, is considered a bad practice to mock 3rd party APIs...



            A good way to solve all those problems would be to re-write the code like



            trait HttpAdapter {
            def fromUrl(apiRequestUrl: String): String = Source.fromURL(apiRequestUrl).mkString
            }

            object HttpAdapter extends HttpAdapter

            def myFunction(apiRequestUrl: String, httpAdapter: HttpAdapter = HttpAdapter) : MyObject = {
            val response: String = httpAdapter.fromUrl(apiRequestUrl)
            val formatedResponse: MyObject = formatResponseFunction(response)
            formatedResponse
            }

            "myFunction" should "work" in {
            //create mock
            val http = mock[HttpAdapter]

            //stub mock
            http.fromUrl("some url") shouldReturn "result"

            //inject mock
            myFunction("some url", http) shouldBe MyObject
            }


            Notice I've wrapped the third party API in a class I have full control over (HttpAdapter) and then I mock that one



            Then I inject httpAdapter as a parameter, but I provide a default value so the callers do not need to worry about it, while I can still override it with a mock or stub in test code



            Also note that I've use mockito-scala instead of regular mockito, so the stubbing syntax is different






            share|improve this answer















            First of all, you have to inject the dependencies you want to replace with mocks, static calls within a function can't be mocked/stubbed



            In your Scenario you have another problem that is: yor dependency Source is an object, and you can't mock objects, only non-final classes and traits. Furthermore, is considered a bad practice to mock 3rd party APIs...



            A good way to solve all those problems would be to re-write the code like



            trait HttpAdapter {
            def fromUrl(apiRequestUrl: String): String = Source.fromURL(apiRequestUrl).mkString
            }

            object HttpAdapter extends HttpAdapter

            def myFunction(apiRequestUrl: String, httpAdapter: HttpAdapter = HttpAdapter) : MyObject = {
            val response: String = httpAdapter.fromUrl(apiRequestUrl)
            val formatedResponse: MyObject = formatResponseFunction(response)
            formatedResponse
            }

            "myFunction" should "work" in {
            //create mock
            val http = mock[HttpAdapter]

            //stub mock
            http.fromUrl("some url") shouldReturn "result"

            //inject mock
            myFunction("some url", http) shouldBe MyObject
            }


            Notice I've wrapped the third party API in a class I have full control over (HttpAdapter) and then I mock that one



            Then I inject httpAdapter as a parameter, but I provide a default value so the callers do not need to worry about it, while I can still override it with a mock or stub in test code



            Also note that I've use mockito-scala instead of regular mockito, so the stubbing syntax is different







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Jan 1 at 1:24

























            answered Dec 31 '18 at 16:49









            BrunoBruno

            42638




            42638













            • Thank you for a great response. I'm still new so thanks for sharing some best practices as well. Would that trait example be known as an adapter pattern? I noticed that you wrote "object HttpAdapter extends " but did not say what it extends. Could you complete that?

              – alex
              Dec 31 '18 at 16:59






            • 1





              Glad it helped! - oh yes, copy paste error, I fixed the extends now - if it solves your problem mark it as the correct answer so it can benefit more people!

              – Bruno
              Jan 1 at 1:24













            • Yeah, this is awesome. Thank you.

              – alex
              Jan 2 at 21:35











            • After trying this out with regular Mockito framework I get an error stating "String cannot be returned by get$default$2() get$default$2() should return int".

              – alex
              Jan 8 at 23:42











            • Regular mockito does not play well with some scala features, that error seems to be related to a default argument returning a value class (just a wild guess here)... could you try with mockito-scala? if it doesn't work there, please create an issue in github and provide enough code so it can be easily copy and pasted to reproduce the error

              – Bruno
              Jan 9 at 9:04



















            • Thank you for a great response. I'm still new so thanks for sharing some best practices as well. Would that trait example be known as an adapter pattern? I noticed that you wrote "object HttpAdapter extends " but did not say what it extends. Could you complete that?

              – alex
              Dec 31 '18 at 16:59






            • 1





              Glad it helped! - oh yes, copy paste error, I fixed the extends now - if it solves your problem mark it as the correct answer so it can benefit more people!

              – Bruno
              Jan 1 at 1:24













            • Yeah, this is awesome. Thank you.

              – alex
              Jan 2 at 21:35











            • After trying this out with regular Mockito framework I get an error stating "String cannot be returned by get$default$2() get$default$2() should return int".

              – alex
              Jan 8 at 23:42











            • Regular mockito does not play well with some scala features, that error seems to be related to a default argument returning a value class (just a wild guess here)... could you try with mockito-scala? if it doesn't work there, please create an issue in github and provide enough code so it can be easily copy and pasted to reproduce the error

              – Bruno
              Jan 9 at 9:04

















            Thank you for a great response. I'm still new so thanks for sharing some best practices as well. Would that trait example be known as an adapter pattern? I noticed that you wrote "object HttpAdapter extends " but did not say what it extends. Could you complete that?

            – alex
            Dec 31 '18 at 16:59





            Thank you for a great response. I'm still new so thanks for sharing some best practices as well. Would that trait example be known as an adapter pattern? I noticed that you wrote "object HttpAdapter extends " but did not say what it extends. Could you complete that?

            – alex
            Dec 31 '18 at 16:59




            1




            1





            Glad it helped! - oh yes, copy paste error, I fixed the extends now - if it solves your problem mark it as the correct answer so it can benefit more people!

            – Bruno
            Jan 1 at 1:24







            Glad it helped! - oh yes, copy paste error, I fixed the extends now - if it solves your problem mark it as the correct answer so it can benefit more people!

            – Bruno
            Jan 1 at 1:24















            Yeah, this is awesome. Thank you.

            – alex
            Jan 2 at 21:35





            Yeah, this is awesome. Thank you.

            – alex
            Jan 2 at 21:35













            After trying this out with regular Mockito framework I get an error stating "String cannot be returned by get$default$2() get$default$2() should return int".

            – alex
            Jan 8 at 23:42





            After trying this out with regular Mockito framework I get an error stating "String cannot be returned by get$default$2() get$default$2() should return int".

            – alex
            Jan 8 at 23:42













            Regular mockito does not play well with some scala features, that error seems to be related to a default argument returning a value class (just a wild guess here)... could you try with mockito-scala? if it doesn't work there, please create an issue in github and provide enough code so it can be easily copy and pasted to reproduce the error

            – Bruno
            Jan 9 at 9:04





            Regular mockito does not play well with some scala features, that error seems to be related to a default argument returning a value class (just a wild guess here)... could you try with mockito-scala? if it doesn't work there, please create an issue in github and provide enough code so it can be easily copy and pasted to reproduce the error

            – Bruno
            Jan 9 at 9:04













            1














            For mocking the external service calls, you could use Mockito which is a mocking framework. Mockito is very simple to use and you can provide stubs for your external calls. For example



            val m = mock[io.Source.type]


            Here you mock the Source and then you provide your desired behaviour on invocation of the fromUrl function.
            i.e



            when(m.fromUrl("external service url")) thenReturn("result")





            share|improve this answer




























              1














              For mocking the external service calls, you could use Mockito which is a mocking framework. Mockito is very simple to use and you can provide stubs for your external calls. For example



              val m = mock[io.Source.type]


              Here you mock the Source and then you provide your desired behaviour on invocation of the fromUrl function.
              i.e



              when(m.fromUrl("external service url")) thenReturn("result")





              share|improve this answer


























                1












                1








                1







                For mocking the external service calls, you could use Mockito which is a mocking framework. Mockito is very simple to use and you can provide stubs for your external calls. For example



                val m = mock[io.Source.type]


                Here you mock the Source and then you provide your desired behaviour on invocation of the fromUrl function.
                i.e



                when(m.fromUrl("external service url")) thenReturn("result")





                share|improve this answer













                For mocking the external service calls, you could use Mockito which is a mocking framework. Mockito is very simple to use and you can provide stubs for your external calls. For example



                val m = mock[io.Source.type]


                Here you mock the Source and then you provide your desired behaviour on invocation of the fromUrl function.
                i.e



                when(m.fromUrl("external service url")) thenReturn("result")






                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Nov 15 '18 at 3:52









                Chaitanya WaikarChaitanya Waikar

                1,367217




                1,367217






























                    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%2f53310108%2fscala-how-to-unit-test-a-function-i-have-which-makes-an-api-call-using-mock-stu%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

                    List item for chat from Array inside array React Native

                    Thiostrepton

                    Caerphilly