Scala: How to Unit Test a function I have which makes an API call using mock/stub?
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
add a comment |
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
add a comment |
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
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
scala unit-testing mocking
asked Nov 14 '18 at 23:09
alexalex
6428
6428
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
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
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
|
show 3 more comments
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")
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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
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
|
show 3 more comments
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
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
|
show 3 more comments
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
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
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
|
show 3 more comments
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
|
show 3 more comments
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")
add a comment |
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")
add a comment |
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")
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")
answered Nov 15 '18 at 3:52
Chaitanya WaikarChaitanya Waikar
1,367217
1,367217
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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