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.)
Related
Django 2.2
I am writing tests for API using APIRequestFactory. The code that hits
/some_endpoint and /some_endpoint/<item_id> already works, and so does the test that tests /some_endpoint. However the test to test /some_endpoint/<item_id> does not work because I can not find a working way to pass that <item_id> value to the view code. Please not it's not /some_endpoint/<some_keyword>=<item_id> , it's "flat" in my case i.e. there's no keyword. The problem is <item_id> does not make it into the view code (it's always None in the classview in get_queryset method)
I tried to pass it as **kwargs, it does not arrive either ( see here). But that probably would not work anyway without keyword.
I tried to switch to use of Client instead of APIRequestFactory, same result. But I would rather get it working with APIRequestFactory unless it does not work this way in general. Below is the code.
test.py
def test_getByLongId(self) :
factory = APIRequestFactory()
item = Item.active.get(id=1)
print(item.longid)
#it prints correct longid here
request = factory.get("/item/%s" % item.longid)
view = ItemList.as_view()
force_authenticate(request, user=self.user)
response = view(request)
urls.py
urlpatterns = [
...
...
url(item/(?P<item_id>[a-zA-Z0-9-]+)/$', views.ItemList.as_view(), name='item-detail'),
...
...
]
views.py
class ItemList(generics.ListAPIView):
permission_classes = (IsBotOrReadOnly,)
"""
API endpoint that allows users to be viewed or edited.
"""
serializer_class = ItemSerializer
schema = AutoSchema(
manual_fields=[
coreapi.Field("longid"),
]
)
def get_queryset(self):
"""
Optionally restricts the returned SampleSequencing to a given barcode.
"""
longid = self.kwargs.get('item_id', None)
print(longid)
#prints correct longid when executed by the webserver code and prints None when executed by the test
queryset = Item.active.filter(longid=longid)
return queryset
You have to pass item_id into the view():
def test_by_long_id(self) :
factory = APIRequestFactory()
item = Item.active.get(id=1)
print(item.longid)
#it prints correct longid here
request = factory.get("/item/%s" % item.longid)
view = ItemList.as_view()
force_authenticate(request, user=self.user)
response = view(request, item_id=item.longid)
or use APIClient:
from rest_framework.test import APIClient
# ...
#
def test_item_client(self):
item = Item.active.get(id=1)
client = APIClient()
url = '/item/%s/' % item.id
response = client.get(url)
I have this test:
class AttributeTest(APITestCase):
def setUp(self):
user1 = User.objects.create(pk=1, username='pepa', email='ads#asasd.cz', is_active=True, is_staff=True)
user1.set_password('mypass')
user1.save()
self.c1 = Campaign.objects.create(pk=1, owner=user1, project_name='c1')
def test(self):
campaign_url = 'http://testserver/api/campaigns/{}/'.format(self.c1.pk)
self.client.login(username='pepa', password='mypass')
data = {
"label": "something_here",
"parent_campaign": campaign_url,
}
# campaign clearly exists (created in setUp) and GET retrieve it:
assert self.client.get(campaign_url).json()['project_name'] == 'c1'
# I can even try it myself using pdb
# but this doesn't work - response return 400 Bad Request
# complaining about the very same hyperlink I can GET above
response = self.client.post('/api/keys', data, format="json")
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
but when run, it fails with {'parent_campaign': ['Invalid hyperlink - No URL match.']}.
When I try using curl or browsable API (outside the test environment), everything works as expected.
My serializer corresponding to the /api/keys:
class AttributeSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='api:key-detail')
parent_campaign = serializers.HyperlinkedRelatedField(
view_name='api:campaign-detail',
lookup_field='cid',
queryset=Campaign.objects.all())
def _get_user_campaigns(self):
user = self.context['view'].request.user
return Campaign.objects.filter(owner=user)
def get_fields(self, *args, **kwargs):
fields = super(AttributeSerializer, self).get_fields(*args, **kwargs)
fields['parent_campaign'].queryset = self._get_user_campaigns()
return fields
class Meta:
model = Key
fields = ("id", 'url', "label", 'parent_campaign')
Using serializer directly:
(Pdb) from api.attribute.serializers import AttributeSerializer
(Pdb) ser = AttributeSerializer(data=data)
(Pdb) ser.is_valid()
True
(Pdb) ser.save()
<Key: Something1 | MAROO | CID: lrvyw93>
Try reversing your url name and passing c1.pk as a url parameter, not just formatting it into your url:
from rest_framework.reverse import reverse
campaign_url_name = 'api:campaign-detail' # Use URL name instead of raw URL path
response = self.client.get(reverse(campaign_url_name, kwargs={'pk': self.c1.pk}))
I don't know why, but the results of tests had to be somehow cached. I restarted the PC and it worked with exactly the same commit. Solved.
The problem has been solved thanks to Thaian, by adding a login to the beginning of the 'test_create' function, as you need to be logged in on this site to use the createview
I am currently writing a test for a createview and I am unable to post data to it.
The object being tested has the following model
class Role(models.Model):
name = models.CharField(max_length=255)
linked_tenant = models.ForeignKey(Tenant, blank=True, null=True)
And is used in the following (generic) view
class RolCreate(TenantRootedMixin, CreateView):
model = RolTemplate
form_class = RoleForm
def get_form_kwargs(self):
kwargs = super(RolCreate, self).get_form_kwargs()
kwargs['linked_tenant'] = self.request.tenant
return kwargs
def form_valid(self, form):
form.instance.linked_tenant = self.kwargs.get('tenant')
return super(RolCreate, self).form_valid(form)
def get_success_url(self, **kwargs):
return reverse('rol_list', args=[self.request.tenant.slug])
And this is the test that I am using.
class RolCreate_tests(TestCase):
def setUp(self):
self.tenant = get_tenant()
self.role = get_role(self.tenant)
self.client = Client(HTTP_HOST='tc.tc:8000')
self.user = get_user(self.tenant)
def test_create(self):
response = self.client.post(reverse('rolcreate'), {'name' : 'new_object'})
self.assertEqual(response.status_code, 302)
test_against = Role.objects.get(name='new_object')
self.assertEqual(test_against, self.tenant)
The assertion that throws the error is the 'get' request at the end.
DoesNotExist: Role matching query does not exist.
So the object is not created, yet the test does validate the 302 view, meaning a post is being made. I do not understand why this test is failing to do what it should. Could someone here help me?
=====
After Thaians suggestions I got the following values:
(Pdb) print(self.client.post)
<bound method Client.post of <django.test.client.Client object at 0x10f20da50>>
Pdb) response
<HttpResponseRedirect status_code=302, "text/html; charset=utf-8", url="/accounts/login/?next=/my/role/create/">
(Pdb) print(response)
Vary: Cookie
Content-Length: 0
Content-Type: text/html; charset=utf-8
Location: /accounts/login/?next=/my/role/create/
Did you print response and check what return maybe?
Good idea is to run tests with PDB.
def test_create(self):
response = self.client.post(reverse('rolcreate'), {'name': 'new_object'})
import pdb; pdb.set_trace()
self.assertEqual(response.status_code, 302)
test_against = Role.objects.get(name='new_object')
self.assertEqual(test_against, self.tenant)
add import pdb;pdb.set_trace() in your test and then check self.client.post().
So please paste what response contain.
I'm trying to write tests for an Admin action in the change_list view. I referred to this question but couldn't get the test to work. Here's my code and issue:
class StatusChangeTestCase(TestCase):
"""
Test case for batch changing 'status' to 'Show' or 'Hide'
"""
def setUp(self):
self.categories = factories.CategoryFactory.create_batch(5)
def test_status_hide(self):
"""
Test changing all Category instances to 'Hide'
"""
# Set Queryset to be hidden
to_be_hidden = models.Category.objects.values_list('pk', flat=True)
# Set POST data to be passed to changelist url
data = {
'action': 'change_to_hide',
'_selected_action': to_be_hidden
}
# Set change_url
change_url = self.reverse('admin:product_category_changelist')
# POST data to change_url
response = self.post(change_url, data, follow=True)
self.assertEqual(
models.Category.objects.filter(status='show').count(), 0
)
def tearDown(self):
models.Category.objects.all().delete()
I tried using print to see what the response was and this is what I got:
<HttpResponseRedirect status_code=302, "text/html; charset=utf-8", url="/admin/login/?next=/admin/product/category/">
It seems like it needs my login credentials - I tried to create a user in setUp() and log in as per Django docs on testing but it didn't seem to work.
Any help would be appreciated!
I found the solution - I wasn't instantiating Django's Client() class when I created a superuser, so whenever I logged in - it didn't persist in my subsequent requests. The correct code should look like this.
def test_status_hide(self):
"""
Test changing all Category instances to 'Hide'
"""
# Create user
user = User.objects.create_superuser(
username='new_user', email='test#example.com', password='password',
)
# Log in
self.client = Client()
self.client.login(username='new_user', password='password')
# Set Queryset to be hidden
to_be_hidden = models.Category.objects.values_list('pk', flat=True)
# Set POST data to be passed to changelist url
data = {
'action': 'change_to_hide',
'_selected_action': to_be_hidden
}
# Set change_url
change_url = self.reverse('admin:product_category_changelist')
# POST data to change_url
response = self.client.post(change_url, data, follow=True)
self.assertEqual(
models.Category.objects.filter(status='show').count(), 0
)
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,
...
)
...