Test with mock pass when run individually but not when together - django

I want test if a function is called in a view.
My view is something like that:
#api_view(["POST"])
def my_view(request):
data = request.data
my_function(data)
return Response("ok")
And my test:
#pytest.fixture
def mock_my_function(mocker):
return mocker.patch("path.for.my.function")
def test_my_test(
mock_my_function, db, client
):
data = {"some": "thing"}
resp = client.post(
"/my/url/",
data=data,
format="json",
)
assert resp.data == "ok"
mock_my_function.assert_called()
Running this test individually, it's ok. Works! But, when I run all tests, this test fails. Pytest show me this error:
E AssertionError: Expected 'mock_my_function' to have been called.

Related

Django Testing Using AsyncClient and Authentication

I have a basic Asynchronous Class Based View:
class AsyncAuthenticationView(View):
async def post(self, request, *args, **kwargs):
authenticated: bool = await sync_to_async(lambda: request.user.is_authenticated)()
if not authenticated:
return HttpResponse('Unauthorized', status=401)
return HttpResponse('Success', status=200)
And two simple tests:
#pytest.fixture
def authenticated_async_client(user) -> AsyncClient:
client = AsyncClient()
client.force_login(user)
return client
class TestAsyncAuthenticationView:
#pytest.mark.asyncio
async def test_with_async_client(self, authenticated_async_client: AsyncClient):
"""This test fails, with response code 401 instead of 200"""
response = await authenticated_async_client.post(
reverse('auth-test'),
'abc',
content_type="text/html",
)
assert 200 == response.status_code # fails with 401
#pytest.mark.asyncio
async def test_with_async_request_factory(self, async_rf: AsyncRequestFactory, user):
"""This test succeeds correctly with response code 200"""
r = async_rf.post('/fake-url', 'abc', content_type='text/html')
r.user = user
r.session = {}
response = await AsyncAuthenticationView().post(r)
assert 200 == response.status_code
The first test which uses the AsyncClient always fails to authenticate returning status code 401 while the second test is passing just fine with status code 200.
Based on the docs the AsyncClient has all the same methods, so I am not not why the test is failing. Perhaps there is a different authentication method that needs to be used?
The AsyncRequestFactory that is being used in the second test is from the pytest-django package.
Any help would be much appreciated. Thank you.

How can I test the data saved in a session?

I new to Django and I'm trying to implement unittest. Is there a way to test the data saved in a session ?
Here is a simple view, where dummy data is saved in the session.
def save_data_in_session(request):
request.session["hello"] = "world"
# If I print(request.session.items()), output is dict_items([('hello', 'world')]) as expected.
context = {}
return render(request, "home.html", context)
Here is my unittest
class TestDummyViews(TestCase):
def test_save_data_in_session(self):
session = self.client.session
url = reverse("home_page:save_data_in_session")
response = self.client.get(url)
session.save()
# If I print(session.items()) output is dict_items([])...
# Following test passes
self.assertEqual(response.status_code, 200)
# Following test fails. KeyError: 'hello'
self.assertEqual(
session["hello"],
"world",
)

Django mock not getting called at all

I have test defined looking like below
#patch('user.serializers.validate_sns_token', return_value=True)
def test_can_create_user(self, validate):
"""
Test that user can be created without logging in
:return:
"""
user_payload = self.generate_user_payload()
resp = self.client.post(
CREATE_USER_URL,
user_payload,
)
self.assertTrue(validate.called)
self.assertEqual(
resp.status_code,
status.HTTP_200_OK,
)
Inside the user.serializers.py file I have below function that I would like to mock
def validate_sns_token(**kwargs):
try:
...
return True
except ValueError as e:
...
return False
It's an external call to google api so I would like to always return True from it.
Inside the serializer I am calling the create function like below
def create(self, validated_data):
...
is_token_valid = validate_sns_token(
sns_id=sns_id,
sns_type=sns_type,
token=token,
)
if is_token_valid is False:
raise serializers.ValidationError('토큰이 유효하지 않습니다. 다시 로그인해주세요.', code='AUTH')
...
return user
Unfortunately, the validate.called is always False and the original validate_sns_token is still getting called. How do I write a test so that the original validate_sns_token is not called at all but its return value is set to True?

Django testing an update post

I receive this message "feed.models.Post.DoesNotExist: Post matching query does not exist." I believe it to be in the UpdatePost class I dont understand as there is a post created with an id of one. Why is this? Edit : I've added delete to fully test CRUD functionality
from django.test import TestCase, SimpleTestCase
from django.contrib.auth.models import User
from django.urls import reverse
from feed.models import Post
class Setup_Class(TestCase):
def setUp(self):
self.user = User.objects.create_user(username='jtur', email='jtur#accenture.com', password='onion')
user = User.objects.first()
Post.objects.create(title='test', content='more testing', author=user)
class PostTests(Setup_Class):
def test_content(self):
post = Post.objects.get(id=1)
expected_post_title = f'{post.title}'
expected_post_content = f'{post.content}'
self.assertEquals(expected_post_title, 'test')
self.assertEquals(expected_post_content, 'more testing')
def test_post_list_view(self):
response = self.client.get(reverse('feed-home'))
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'more testing')
self.assertTemplateUsed(response, 'feed/home.html')
class UpdatePost(Setup_Class):
def test_post_update(self):
post = Post.objects.first()
post.title = "This has been changed"
expected_post_title = f'{post.title}'
self.assertEquals(expected_post_title, 'This has been changed')
def test_post_updated_view(self):
response = self.client.get(reverse('feed-home'))
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'This has been changed')
self.assertTemplateUsed(response, 'feed/home.html')
class DeletePost(Setup_Class):
def test_post_delete(self):
post = Post.objects.first()
post.delete()
val = False
if post is None:
val = True
else:
val = False
self.assertTrue(val)
def test_post_list_view(self):
response = self.client.get(reverse('feed-home'))
self.assertEqual(response.status_code, 200)
self.assertNotContains(response, 'test')
self.assertTemplateUsed(response, 'feed/home.html')
There's no reason to assume the post created in your setUp method will have id=1. In fact, it probably won't after the first run of your tests. Even though the database is emptied after each run, the sequences are usually not reset.
You should get the first post with Post.objects.first() instead.
(Note however that your test_content and test_post_update methods are pretty pointless; they only call native Django functionality, which you don't need to test. Your tests should be concerned with testing your app's functionality, such as views that update or display the posts.)

Django REST test ignores factory's url

I have a weird error writing a APITestCase for a Django REST view.
This is my code:
class CreateUserTest(APITestCase):
def setup(self):
self.superuser = User.objects.create_superuser('vishnu#vishnu.com', '1989-10-06', 'vishnupassword')
self.client.login(username='vishnu', password='vishnupassword')
self.data = a bunch of trivial data
def test_can_create_user(self):
print "create user"
self.setup()
self.token = Token.objects.get(user_id=self.superuser.id)
self.api_key = settings.API_KEY
self.factory = APIRequestFactory()
self.request = self.factory.post('/api/v1/uaaaaaasers/?api_key=%s' % self.api_key,
self.data,
HTTP_AUTHORIZATION='Token {}'.format(self.token))
force_authenticate(self.request, user=self.superuser)
self.view = UserList.as_view()
self.response = self.view(self.request)
self.response.render()
#print self.response.content
self.assertEqual(self.response.status_code, status.HTTP_201_CREATED)
As you see I run a factory.post to an intentionally wrong url /api/v1/uaaaaaasers/
But the test doesnt complain:
Creating test database for alias 'default'...
create user .
----------------------------------------------------------------------
Ran 1 test in 0.199s
OK Destroying test database for alias 'default'...
Shouldnt it crash with that wrong url? How do I know the test is going fine?
You are testing it all wrong...
The response that you have tested is from the direct view call...
self.view = UserList.as_view()
self.response = self.view(self.request)
self.response.render()
#print self.response.content
self.assertEqual(self.response.status_code, status.HTTP_201_CREATED)
your above case will always call the view...
In actual testcases we hit the urls with the client and test that response
self.response = self.client.post('/api/v1/uaaaaaasers/?api_key=%s' % self.api_key,
self.data,
HTTP_AUTHORIZATION='Token {}'.format(self.token))
self.assertEqual(self.response.status_code, status.HTTP_201_CREATED)
If you want to test posting a request to an invalid url, use the test client instead of the request factory.
class CreateUserTest(APITestCase):
def test_can_create_user(self):
...
response = self.client.post(
'/api/v1/uaaaaaasers/?api_key=%s' % self.api_key,
self.data,
...
)
...