Testing a PUT request in Django Rest Framework - django

When I run this test
def test_update_car(self):
new_car = Car.objects.create(make='Chevy', model='Equinox', year=2012, seats=4, color='green', VIN='12345671234567abc', current_mileage=19000, service_interval='3 months', next_service='april')
url = reverse('car-detail', kwargs={'pk': new_car.pk})
data = {
'make': 'test',
'model': 'test',
'year': 2014,
'seats': 5,
'color': 'blue',
'VIN': '12345671234567abc',
'current_mileage': 20000,
'service_interval': '6 months',
'next_service': 'July',
}
response = self.client.put(url, data=data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(new_car.make, 'test')
I get an assertion error
AssertionError: 'Chevy' != 'test'
How should I structure this differently so that the PUT request actually changes the make and model of the new_car?

If your view indeed responds to a PUT request, the problem is located at the test itself. You need to refresh the data from the database with .refresh_from_db(…) [Django-doc]:
def test_update_car(self):
new_car = Car.objects.create(make='Chevy', model='Equinox', year=2012, seats=4, color='green', VIN='12345671234567abc', current_mileage=19000, service_interval='3 months', next_service='april')
url = reverse('car-detail', kwargs={'pk': new_car.pk})
data = {
'make': 'test',
'model': 'test',
'year': 2014,
'seats': 5,
'color': 'blue',
'VIN': '12345671234567abc',
'current_mileage': 20000,
'service_interval': '6 months',
'next_service': 'July',
}
response = self.client.put(url, data=data)
new_car.refresh_from_db()
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(new_car.make, 'test')

Related

Fastapi Testclient not able to send POST request using form-data

Currently I am doing Unit Testing in Fastapi using from fastapi.testclient import TestClient
def test_login_api_returns_token(session,client):
form_data = {
"username": "mike#gmail.com",
"password": "mike"
}
response = client.post(
"/api/login",
data=form_data,
headers={"content-type": "multipart/form-data"}
# headers={"content-type": "application/x-www-form-urlencoded"}
)
result = response.json()
assert response.status_code == 200
I am supposed to get token as response which I am getting when I run the fastapi application but not able to proceed with Unit Testing with the same.
Example of postman request for the same
How do I make sure form-data is being sent from TestClient?
api/login.py
#router.post("/login")
async def user_login(form_data: OAuth2PasswordRequestForm = Depends(), session: Session = Depends(get_session)):
user = authenticated_user(form_data.username, form_data.password, session)
user = user[0]
if not user:
raise token_exception()
token_expires = timedelta(minutes=120)
token = create_access_token(username=user.username, user_id=user.id, expires_delta=token_expires)
token_exp = jwt.decode(token, SECRET, algorithms=[ALGORITHM])
return {
"status_code": status.HTTP_200_OK,
"data":{
"username": user.username,
"token": token,
"expiry": token_exp['exp'],
"user_id": user.id
}
}
Try set the header to Content-Type form-data like
def test_login_api_returns_token(session,client):
form_data = {
"username": "mike#gmail.com",
"password": "mike"
}
response = client.post(
"/api/login",
json=form_data,
headers={ 'Content-Type': 'application/x-www-form-urlencoded'}
)
result = response.json()
assert response.status_code == 200

Unittesting FastAPI CRUD doesn't work as expected

I have a problem with unit tests in a project that use FastAPI.
I used TestClient, pytest, monkeypatch.
When I test a GET function I don't know why but instead to have the result of the mocked function the result come from the "real" service, getting the results from the DB.
The function to test:
from fastapi import APIRouter, Body
...
...
router = APIRouter()
#router.post('/', response_description="feature data added into the database")
async def add_feature_data(feature: FeatureSchema = Body(...)):
feature = jsonable_encoder(feature)
new_feature = await add_feature(feature)
return ResponseModel(new_feature, "feature added successfully.")
my file conftest.py
import pytest
from fastapi.testclient import TestClient
from app.main import klingon
#pytest.fixture(scope='module')
def test_app():
client = TestClient(klingon)
yield client
the test file:
import json
import pytest
from app.server.routes import feature_route
def test_get_features(test_app, monkeypatch):
test_data = [
{
'change': '1',
'urs': 'something',
'state': 'active',
'created_at': '2020-11-09T11:51:14Z',
'created_by': '9'
},
{
'change': '2',
'urs': 'something 2',
'state': 'active',
'created_at': '2020-10-16T11:51:14Z',
'created_by': '3'
}
]
async def mock_get_features():
return test_data
monkeypatch.setattr(feature_route, 'get_features', mock_get_features)
response = test_app.get('/feature/')
assert response.status_code == 200
assert response.json() == test_data
Thanks

Django API Client Get Request with Payload

I am looking to see if this is possible. If I send a get request with body in postman I get back results matching what I need.
Can we send a get request with a body using the APIClient?
Here is my code;
def setUp(self):
self.client = APIClient()
HistoryLog.objects.create(username='User1', log_time='2020-05-05', logon_time='08:00', logout_time='09:00')
HistoryLog.objects.create(username='User2', log_time='2020-05-05', logon_time='08:30', logout_time='10:00')
HistoryLog.objects.create(username='User3', log_time='2020-05-08', logon_time='08:40', logout_time='11:00')
HistoryLog.objects.create(username='User4', log_time='2020-05-10', logon_time='08:50', logout_time='12:00')
def test_get_data(self):
payload = {'date': '2020-05-05', 'start_time': '06:00', 'end_time': '12:00'}
res = self.client.get(HISTORY_URL, payload)
self.assertEqual(res.status_code, status.HTTP_200_OK) -- this passes.
self.assertEqual(len(res.data), 2) -- this always come back that res.data is zero
A bit hacky, but seems to work:
def test_get_data(self):
payload = {'date': '2020-05-05', 'start_time': '06:00', 'end_time': '12:00'}
data, content_type = self.client._encode_data(payload, 'json')
res = self.client.generic('get', HISTORY_URL, data, content_type)
...

django test for upload image

i want to test user registration but i can't test image here is my test:
test.py
response = self.client.post('/api/v1/signup/',
content_type='application/json',
data=json.dumps({"username": "casino", "email": "casinoluxwin#gmail.com",
"password1": "android12", "password2": "android12", "photo": {
'real_filename': "u'banner3.jpg'",
'path': "u'C:/Users/Dima/Desktop'"}
}))
self.assertEqual(response.status_code, 200)
i get code 400(bad request), but without photo my test pass
service/users.py
#validate_input({
'username': {'min_length': 3, 'max_length': 50},
'email': {'validation_type': "email", 'max_length': 50},
'password1': {'min_length': 8, 'max_length': 50},
'password2': {'min_length': 8, 'equal_to': 'password1',
'messages': {'equal_to': _(u"Пароли не совпадают")}},
'photo': {'required': True}
})
#transaction.atomic
def signup(self, data):
user_data = {
'username': data['username'],
'email': data['email'],
'password': data['password1'],
'coins_amount': 0
}
user = self._create_user(user_data)
if data.get("photo"):
self._attach_photo(user, data["photo"])
obj, created = VerificationCode.objects.get_or_create(user=user, code_type="registration")
obj.create_expiration_date()
obj.create_code()
obj.save()
return user.id
So i want to test user photo anything else works fine. Thanks for any help
Problem probably resides in either Users._attach_photo() or your user model. Not enough information here to decipher it entirely. There are a couple of things to try.
I'd write a normal unittest that does not use the client. It'll give you a more helpful traceback than just the HTTP status code from the running server. Something like:
def test_user_add_method(self):
x = Users.signup(json.dumps({"username": "casino", "email": "casinoluxwin#gmail.com",
"password1": "android12", "password2": "android12", "photo": {
'real_filename': "u'banner3.jpg'",
'path': "u'C:/Users/Dima/Desktop'"})
Users.get(pk=x) #will fail if user was not created.
Second, try commenting out your validator. 400 bad request could easily be kicked off by that. It is possible that your validator isn't playing nice with the image and you'll have to mess around with that.

Django unit testing; POST checkboxes with multiple values

I am trying to run a unit test on a form with multiple checkboxes, but cannot figure out how to send the POST data. The most similar question I can find is here. But how do I embed that url-encoded piece into the POST with the other form data?
If I do something like this, my test errors out and says classes = request.POST.getlist('class_choices')
AttributeError: 'dict' object has no attribute 'getlist':
request = HttpRequest()
request.method = 'POST'
request.POST['fname'] = 'A'
request.POST['lname'] = 'Student'
request.POST['email'] = 'me#name.com'
request.POST['class_choices'] = urllib.urlencode({
'class_choices': ['1', '2'],
}, True)
request.POST['passwd'] = 'password'
request.POST['conpasswd'] = 'password'
response = success(request)
self.assertIn('My Browser', response.content.decode())
But if I do this, I get an error on fname = request.POST['fname']
TypeError: string indices must be integers, not str
request.POST = urllib.urlencode({
'class_choices': ['1', '2'],
'fname': 'A',
'lname': 'Student',
'email': 'me#name.com',
'passwd': 'password',
'conpasswd': 'password'
}, True)
response = success(request)
So after trying out a couple other things, I'm basically going to say there is "technically" no answer to my question...I could not find any way to create a checkbox-type POST argument using HttpRequest. However, doing it the 'right' way with the Django test client as Hieu Nguyen suggested does allow for that capability. You just have to encapsulate the multiple options in parantheses, as:
response = c.post('/success/',{
'fname': 'A',
'lname': 'Student',
'email': 'me#name.com',
'passwd': 'password',
'conpasswd': 'password',
'class_choices': ('1','2'),
})