Testing a Django model with custom save method calling external API











up vote
0
down vote

favorite
1












i'm pretty new to unittests and want to make some "simple" beginner tests in my Django application, I have a model with a "custom" save method that calls a external api if new. Can't figure out how to that that model without calling the external API, how do a implement a mock solution for this?



class Task(models.Model):
status = models.CharField(max_length=25, choices=STATUS_CHOICES, default='new')
....

def _create(self):
....
return requests.post(API_URL + url, json=payload).json()

def save(self, *args, **kwargs):
is_new = self.created is None
super(Task, self).save(*args, **kwargs)

if is_new:
self._create()




class TaskTestCase(TestCase):

def setUp(self):

self.task = Task.objects.create(status='new')

def test_get_new_task(self):
task = Task.objects.all()[0]
self.assertEqual(task.status, 'new')









share|improve this question






















  • You can look into this: realpython.com/testing-third-party-apis-with-mocks
    – ruddra
    Nov 7 at 10:28















up vote
0
down vote

favorite
1












i'm pretty new to unittests and want to make some "simple" beginner tests in my Django application, I have a model with a "custom" save method that calls a external api if new. Can't figure out how to that that model without calling the external API, how do a implement a mock solution for this?



class Task(models.Model):
status = models.CharField(max_length=25, choices=STATUS_CHOICES, default='new')
....

def _create(self):
....
return requests.post(API_URL + url, json=payload).json()

def save(self, *args, **kwargs):
is_new = self.created is None
super(Task, self).save(*args, **kwargs)

if is_new:
self._create()




class TaskTestCase(TestCase):

def setUp(self):

self.task = Task.objects.create(status='new')

def test_get_new_task(self):
task = Task.objects.all()[0]
self.assertEqual(task.status, 'new')









share|improve this question






















  • You can look into this: realpython.com/testing-third-party-apis-with-mocks
    – ruddra
    Nov 7 at 10:28













up vote
0
down vote

favorite
1









up vote
0
down vote

favorite
1






1





i'm pretty new to unittests and want to make some "simple" beginner tests in my Django application, I have a model with a "custom" save method that calls a external api if new. Can't figure out how to that that model without calling the external API, how do a implement a mock solution for this?



class Task(models.Model):
status = models.CharField(max_length=25, choices=STATUS_CHOICES, default='new')
....

def _create(self):
....
return requests.post(API_URL + url, json=payload).json()

def save(self, *args, **kwargs):
is_new = self.created is None
super(Task, self).save(*args, **kwargs)

if is_new:
self._create()




class TaskTestCase(TestCase):

def setUp(self):

self.task = Task.objects.create(status='new')

def test_get_new_task(self):
task = Task.objects.all()[0]
self.assertEqual(task.status, 'new')









share|improve this question













i'm pretty new to unittests and want to make some "simple" beginner tests in my Django application, I have a model with a "custom" save method that calls a external api if new. Can't figure out how to that that model without calling the external API, how do a implement a mock solution for this?



class Task(models.Model):
status = models.CharField(max_length=25, choices=STATUS_CHOICES, default='new')
....

def _create(self):
....
return requests.post(API_URL + url, json=payload).json()

def save(self, *args, **kwargs):
is_new = self.created is None
super(Task, self).save(*args, **kwargs)

if is_new:
self._create()




class TaskTestCase(TestCase):

def setUp(self):

self.task = Task.objects.create(status='new')

def test_get_new_task(self):
task = Task.objects.all()[0]
self.assertEqual(task.status, 'new')






django django-unittest






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 7 at 10:25









pkdkk

1,68973055




1,68973055












  • You can look into this: realpython.com/testing-third-party-apis-with-mocks
    – ruddra
    Nov 7 at 10:28


















  • You can look into this: realpython.com/testing-third-party-apis-with-mocks
    – ruddra
    Nov 7 at 10:28
















You can look into this: realpython.com/testing-third-party-apis-with-mocks
– ruddra
Nov 7 at 10:28




You can look into this: realpython.com/testing-third-party-apis-with-mocks
– ruddra
Nov 7 at 10:28












1 Answer
1






active

oldest

votes

















up vote
3
down vote













This is a typical use case for python mock library. In your case I will probably looks like this



import mock
class TaskTestCase(TestCase):

@mock.patch("request.post")
def setUp(self, mocked_post):

self.task = Task.objects.create(status='new')

def test_get_new_task(self):
task = Task.objects.all()[0]
self.assertEqual(task.status, 'new')


You can use mock.patch as a decorator but also as a context manager if you want to mock a function call on a specific part of your code. See the documentation for more details.



More details



In this example we only mocked the method to avoid the called being really made. In some cases in can be a good idea to also checked that the mocked method was called, and with which arguments.



import mock
class TaskTestCase(TestCase):

@mock.patch("request.post")
def setUp(self, mocked_post):

self.task = Task.objects.create(status='new')
# The most basic way to check a call was performed
mocked_post..assert_called()

# Checking the number of call can be a good idea
mocked_post.assert_called_once()

# Same idea but it also checks the arguments of the method.
mocked_post.assert_called_once_with()


The last option is very useful to test that your are sending the right data to the external service. More details on the different options in the documentation.



Why mocking external calls is a good idea



Mocking all the external calls you can in your test suite is really a good idea. Here is a few reasons why:




  • It will improve performance and speed of your test suite. Network calls are generally time expensive, lowering the number of calls will speed up your test suite.

  • It allows you to test the data you are sending to other services. As seen with assert_called_once_with you can assert that you are sending once the proper data to other services

  • It makes your test suite more predictable. Use mocks you won't rely on any other services to test your application. From time to time external services may not respond properly (maintenance, too many requests, etc.). Using mock you will break the coupling between your test suite and other services

  • You can run tests offline (commuting, train, place, etc.).






share|improve this answer





















  • Thanks for the great answer,. One thing, when I try out you example, i get an error due to the requests response.. <MagicMock name='post().json().get()' id='4441346384'> is not JSON serializable .. do you have an idea how to make that work.. can i "use" some example data from the external response?
    – pkdkk
    Nov 8 at 13:44






  • 1




    @pkdkk this is propably due to the fact that the mock you have setup does not return any value. You can either use mocked_post.return_value = {} or use mocked_post.side_effect to use a function to return a value
    – Anto
    Nov 8 at 19:46











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',
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%2f53187580%2ftesting-a-django-model-with-custom-save-method-calling-external-api%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








up vote
3
down vote













This is a typical use case for python mock library. In your case I will probably looks like this



import mock
class TaskTestCase(TestCase):

@mock.patch("request.post")
def setUp(self, mocked_post):

self.task = Task.objects.create(status='new')

def test_get_new_task(self):
task = Task.objects.all()[0]
self.assertEqual(task.status, 'new')


You can use mock.patch as a decorator but also as a context manager if you want to mock a function call on a specific part of your code. See the documentation for more details.



More details



In this example we only mocked the method to avoid the called being really made. In some cases in can be a good idea to also checked that the mocked method was called, and with which arguments.



import mock
class TaskTestCase(TestCase):

@mock.patch("request.post")
def setUp(self, mocked_post):

self.task = Task.objects.create(status='new')
# The most basic way to check a call was performed
mocked_post..assert_called()

# Checking the number of call can be a good idea
mocked_post.assert_called_once()

# Same idea but it also checks the arguments of the method.
mocked_post.assert_called_once_with()


The last option is very useful to test that your are sending the right data to the external service. More details on the different options in the documentation.



Why mocking external calls is a good idea



Mocking all the external calls you can in your test suite is really a good idea. Here is a few reasons why:




  • It will improve performance and speed of your test suite. Network calls are generally time expensive, lowering the number of calls will speed up your test suite.

  • It allows you to test the data you are sending to other services. As seen with assert_called_once_with you can assert that you are sending once the proper data to other services

  • It makes your test suite more predictable. Use mocks you won't rely on any other services to test your application. From time to time external services may not respond properly (maintenance, too many requests, etc.). Using mock you will break the coupling between your test suite and other services

  • You can run tests offline (commuting, train, place, etc.).






share|improve this answer





















  • Thanks for the great answer,. One thing, when I try out you example, i get an error due to the requests response.. <MagicMock name='post().json().get()' id='4441346384'> is not JSON serializable .. do you have an idea how to make that work.. can i "use" some example data from the external response?
    – pkdkk
    Nov 8 at 13:44






  • 1




    @pkdkk this is propably due to the fact that the mock you have setup does not return any value. You can either use mocked_post.return_value = {} or use mocked_post.side_effect to use a function to return a value
    – Anto
    Nov 8 at 19:46















up vote
3
down vote













This is a typical use case for python mock library. In your case I will probably looks like this



import mock
class TaskTestCase(TestCase):

@mock.patch("request.post")
def setUp(self, mocked_post):

self.task = Task.objects.create(status='new')

def test_get_new_task(self):
task = Task.objects.all()[0]
self.assertEqual(task.status, 'new')


You can use mock.patch as a decorator but also as a context manager if you want to mock a function call on a specific part of your code. See the documentation for more details.



More details



In this example we only mocked the method to avoid the called being really made. In some cases in can be a good idea to also checked that the mocked method was called, and with which arguments.



import mock
class TaskTestCase(TestCase):

@mock.patch("request.post")
def setUp(self, mocked_post):

self.task = Task.objects.create(status='new')
# The most basic way to check a call was performed
mocked_post..assert_called()

# Checking the number of call can be a good idea
mocked_post.assert_called_once()

# Same idea but it also checks the arguments of the method.
mocked_post.assert_called_once_with()


The last option is very useful to test that your are sending the right data to the external service. More details on the different options in the documentation.



Why mocking external calls is a good idea



Mocking all the external calls you can in your test suite is really a good idea. Here is a few reasons why:




  • It will improve performance and speed of your test suite. Network calls are generally time expensive, lowering the number of calls will speed up your test suite.

  • It allows you to test the data you are sending to other services. As seen with assert_called_once_with you can assert that you are sending once the proper data to other services

  • It makes your test suite more predictable. Use mocks you won't rely on any other services to test your application. From time to time external services may not respond properly (maintenance, too many requests, etc.). Using mock you will break the coupling between your test suite and other services

  • You can run tests offline (commuting, train, place, etc.).






share|improve this answer





















  • Thanks for the great answer,. One thing, when I try out you example, i get an error due to the requests response.. <MagicMock name='post().json().get()' id='4441346384'> is not JSON serializable .. do you have an idea how to make that work.. can i "use" some example data from the external response?
    – pkdkk
    Nov 8 at 13:44






  • 1




    @pkdkk this is propably due to the fact that the mock you have setup does not return any value. You can either use mocked_post.return_value = {} or use mocked_post.side_effect to use a function to return a value
    – Anto
    Nov 8 at 19:46













up vote
3
down vote










up vote
3
down vote









This is a typical use case for python mock library. In your case I will probably looks like this



import mock
class TaskTestCase(TestCase):

@mock.patch("request.post")
def setUp(self, mocked_post):

self.task = Task.objects.create(status='new')

def test_get_new_task(self):
task = Task.objects.all()[0]
self.assertEqual(task.status, 'new')


You can use mock.patch as a decorator but also as a context manager if you want to mock a function call on a specific part of your code. See the documentation for more details.



More details



In this example we only mocked the method to avoid the called being really made. In some cases in can be a good idea to also checked that the mocked method was called, and with which arguments.



import mock
class TaskTestCase(TestCase):

@mock.patch("request.post")
def setUp(self, mocked_post):

self.task = Task.objects.create(status='new')
# The most basic way to check a call was performed
mocked_post..assert_called()

# Checking the number of call can be a good idea
mocked_post.assert_called_once()

# Same idea but it also checks the arguments of the method.
mocked_post.assert_called_once_with()


The last option is very useful to test that your are sending the right data to the external service. More details on the different options in the documentation.



Why mocking external calls is a good idea



Mocking all the external calls you can in your test suite is really a good idea. Here is a few reasons why:




  • It will improve performance and speed of your test suite. Network calls are generally time expensive, lowering the number of calls will speed up your test suite.

  • It allows you to test the data you are sending to other services. As seen with assert_called_once_with you can assert that you are sending once the proper data to other services

  • It makes your test suite more predictable. Use mocks you won't rely on any other services to test your application. From time to time external services may not respond properly (maintenance, too many requests, etc.). Using mock you will break the coupling between your test suite and other services

  • You can run tests offline (commuting, train, place, etc.).






share|improve this answer












This is a typical use case for python mock library. In your case I will probably looks like this



import mock
class TaskTestCase(TestCase):

@mock.patch("request.post")
def setUp(self, mocked_post):

self.task = Task.objects.create(status='new')

def test_get_new_task(self):
task = Task.objects.all()[0]
self.assertEqual(task.status, 'new')


You can use mock.patch as a decorator but also as a context manager if you want to mock a function call on a specific part of your code. See the documentation for more details.



More details



In this example we only mocked the method to avoid the called being really made. In some cases in can be a good idea to also checked that the mocked method was called, and with which arguments.



import mock
class TaskTestCase(TestCase):

@mock.patch("request.post")
def setUp(self, mocked_post):

self.task = Task.objects.create(status='new')
# The most basic way to check a call was performed
mocked_post..assert_called()

# Checking the number of call can be a good idea
mocked_post.assert_called_once()

# Same idea but it also checks the arguments of the method.
mocked_post.assert_called_once_with()


The last option is very useful to test that your are sending the right data to the external service. More details on the different options in the documentation.



Why mocking external calls is a good idea



Mocking all the external calls you can in your test suite is really a good idea. Here is a few reasons why:




  • It will improve performance and speed of your test suite. Network calls are generally time expensive, lowering the number of calls will speed up your test suite.

  • It allows you to test the data you are sending to other services. As seen with assert_called_once_with you can assert that you are sending once the proper data to other services

  • It makes your test suite more predictable. Use mocks you won't rely on any other services to test your application. From time to time external services may not respond properly (maintenance, too many requests, etc.). Using mock you will break the coupling between your test suite and other services

  • You can run tests offline (commuting, train, place, etc.).







share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 7 at 10:42









Anto

862




862












  • Thanks for the great answer,. One thing, when I try out you example, i get an error due to the requests response.. <MagicMock name='post().json().get()' id='4441346384'> is not JSON serializable .. do you have an idea how to make that work.. can i "use" some example data from the external response?
    – pkdkk
    Nov 8 at 13:44






  • 1




    @pkdkk this is propably due to the fact that the mock you have setup does not return any value. You can either use mocked_post.return_value = {} or use mocked_post.side_effect to use a function to return a value
    – Anto
    Nov 8 at 19:46


















  • Thanks for the great answer,. One thing, when I try out you example, i get an error due to the requests response.. <MagicMock name='post().json().get()' id='4441346384'> is not JSON serializable .. do you have an idea how to make that work.. can i "use" some example data from the external response?
    – pkdkk
    Nov 8 at 13:44






  • 1




    @pkdkk this is propably due to the fact that the mock you have setup does not return any value. You can either use mocked_post.return_value = {} or use mocked_post.side_effect to use a function to return a value
    – Anto
    Nov 8 at 19:46
















Thanks for the great answer,. One thing, when I try out you example, i get an error due to the requests response.. <MagicMock name='post().json().get()' id='4441346384'> is not JSON serializable .. do you have an idea how to make that work.. can i "use" some example data from the external response?
– pkdkk
Nov 8 at 13:44




Thanks for the great answer,. One thing, when I try out you example, i get an error due to the requests response.. <MagicMock name='post().json().get()' id='4441346384'> is not JSON serializable .. do you have an idea how to make that work.. can i "use" some example data from the external response?
– pkdkk
Nov 8 at 13:44




1




1




@pkdkk this is propably due to the fact that the mock you have setup does not return any value. You can either use mocked_post.return_value = {} or use mocked_post.side_effect to use a function to return a value
– Anto
Nov 8 at 19:46




@pkdkk this is propably due to the fact that the mock you have setup does not return any value. You can either use mocked_post.return_value = {} or use mocked_post.side_effect to use a function to return a value
– Anto
Nov 8 at 19:46


















 

draft saved


draft discarded



















































 


draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53187580%2ftesting-a-django-model-with-custom-save-method-calling-external-api%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







這個網誌中的熱門文章

Tangent Lines Diagram Along Smooth Curve

Yusuf al-Mu'taman ibn Hud

Zucchini