I do coverage report and there are many place that not cover, do you have any idea how to I write test for th?
this views.py was not cover
def addreview(request, u_id, shop_id):
url = request.META.get('HTTP_REFERER')
shop = shop_detail.objects.get(id=shop_id)
user = Profile.objects.get(customer=u_id)
if request.method == 'POST':
form = ReviewForm(request.POST)
if form.is_valid():
data = Review()
data.review_text = form.cleaned_data['review_text']
data.review_rating = form.cleaned_data['review_rating']
data.shop = shop
data.user = user
data.save()
return redirect(url)
def rating(request):
if not request.user.is_authenticated:
return HttpResponseRedirect(reverse('customer_login'))
if request.method == 'POST':
form = RateUsForm(request.POST)
if form.is_valid():
rate = RateUs()
rate.rate_text = form.cleaned_data['rate_text']
rate.rating = form.cleaned_data['rating']
rate.user = request.user
rate.save()
return HttpResponseRedirect(reverse("index"))
return render(request, 'shop/rate.html')
models.py
class Review(models.Model):
user = models.ForeignKey(Profile,on_delete=models.CASCADE)
shop = models.ForeignKey(shop_detail,on_delete=models.CASCADE)
review_text = models.TextField(max_length=300)
review_rating = models.IntegerField()
def __str__(self):
return f"{self.review_rating}"
class RateUs(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
rate_text = models.TextField()
rating = models.IntegerField()
def __str__(self):
return f"{self.rating}"
forms.py
class ReviewForm(forms.ModelForm):
class Meta:
model= Review
fields= ["review_text", "review_rating"]
class RateUsForm(forms.ModelForm):
class Meta:
model= RateUs
fields= ["rate_text","rating"]
This is what I write for rating
def test_valid_rating(self):
rateus1 = RateUs.objects.first()
data={
"user": rateus1.user,
"rate_text": rateus1.rate_text,
"rating": rateus1.rating
}
response = self.client.post(reverse('rating'), data=data)
self.assertEqual(response.status_code, 302)
When I try to write test for addreview i got customer.models.Profile.DoesNotExist: Profile matching query does not exist.
Here is what you need to implement to get a way to test your function:
# Imports needed
import pytest
from unittest.mock import MagicMock, patch
def test_addreview(self):
# Here probably you will need to create a User before.
user = User.objects.create(username='testuser', id=15445)
shop = shop_detail.objects.create(id=19889)
Profile.objects.create(customer=user.pk)
mock_request = MagicMock(
POST={'review_text': 'test', 'review_rating': 5},
META={'HTTP_REFERER': 'http://testserver/'},
method='POST',
)
self.assert(Review.objects.count(), 0)
with patch('django.shortcuts.redirect') as redirect_mock:
addreview(mock_request, user.pk, shop.pk)
redirect_mock.assert_called_with('http://testserver/')
self.assert(Review.objects.count(), 1)
def test_addreview_not_redirected(self):
# Here probably you will need to create a User before.
user = User.objects.create(username='testuser', id=15445)
shop = shop_detail.objects.create(id=19889)
Profile.objects.create(customer=user.pk)
mock_request = MagicMock(
POST={'review_text': 'test', 'review_rating': 5},
META={'HTTP_REFERER': 'http://testserver/'},
method='GET',
)
self.assert(Review.objects.count(), 0)
with patch('django.shortcuts.redirect') as redirect_mock:
addreview(mock_request, user.pk, shop.pk)
redirect_mock.assert_not_called()
self.assert(Review.objects.count(), 0)
def test_addreview_raise_exception_profile_not_found(self):
# Here probably you will need to create a User before.
user = User.objects.create(username='testuser', id=15445)
shop = shop_detail.objects.create(id=19889)
mock_request = MagicMock(
POST={'review_text': 'test', 'review_rating': 5},
META={'HTTP_REFERER': 'http://testserver/'},
method='GET',
)
self.assert(Review.objects.count(), 0)
self.assert(Profile.objects.count(), 0)
with pytest.raises(Profile.DoesNotExist):
addreview(mock_request, user.pk, shop.pk)
self.assert(Review.objects.count(), 0)
self.assert(Profile.objects.count(), 0)
Probaly you will need to adapt a little bit each example as it is just a guide of how you have to implement it.
When working with this kind of test if you expect for example a Profile to exists you need to create it before to anticipate to the lines of code that will be executed once the function is called.
Mock are so important in this case we mock request and simulate all the properties that a request has.
We also mock redirect but in this case just to knwo if it was called or not.
At the end we generate a case where what we expect is the code to generate an exception when calling the funcion.
Normally to create model objects in test the best practice is to use https://factoryboy.readthedocs.io/en/stable/ take a look of that.
Related
i just learning how to pytest my code in django, and i need your help. So, i have tested my forms.py already,and now i want to test my views.py. I know that i need to test is it post on page,like by response on by ORM, but i cant understand how to do that, probably with my factories or no?
This is my views.py
class AddPost(CreateView):
model = Posts
form_class = PostsForm
template_name = 'posts/addpost.html'
success_url = '/'
def form_valid(self, form):
instance = form.save(commit=False)
if self.request.user.is_authenticated:
instance.owner = self.request.user
instance.save()
return HttpResponseRedirect(self.get_success_url())
class ShowPost(ListView):
model = Posts
template_name = 'posts/allposts.html'
paginate_by = 2
this is test_forms
#pytest.mark.django_db(True)
class TestPostCreationForm:
def test_form(self):
proto_post = PostsFactory.build()
form_payload = {
'phone_number': proto_post.phone_number,
'title': proto_post.title,
'type': proto_post.type,
'text': proto_post.text,
'price': proto_post.price,
'status': proto_post.status,
'image': proto_post.image,
}
form = PostsForm(form_payload)
assert form.is_valid()
instance = form.save()
assert instance.phone_number == proto_post.phone_number
assert instance.title == proto_post.title
assert instance.price == proto_post.price
and factories
from users.tests.factories import UserFactory
def get_mock_img(name='test.png', ext='png', size=(50, 50), color=(256, 0, 0)):
file_obj = BytesIO()
image = Image.new("RGB", size=size, color=color)
image.save(file_obj, ext)
file_obj.seek(0)
return File(file_obj, name=name)
class PostsFactory(factory.DjangoModelFactory):
owner = SubFactory(UserFactory)
phone_number = factory.Faker("phone_number", locale='uk_UA')
title = factory.fuzzy.FuzzyText(length=50)
text = factory.fuzzy.FuzzyText(length=250)
price = factory.fuzzy.FuzzyDecimal(10.5, 50.5)
status = factory.fuzzy.FuzzyChoice(choices=['active', 'deactivated'])
type = factory.fuzzy.FuzzyChoice(choices=['private', 'business'])
image = get_mock_img()
class Meta:
model = 'posts.Posts'
Use the Django test client. Although I don't use pytest, so I can't be certain that it's completely separable from the Django test runner which I use. I'd be surprised if that was not the case
Typical usage (POST to a simple form, which is expected to succeed and redirect)
data = {
"number_per_gridbox": "10",
"submit": "Submit" }
url_fill_gridbox = reverse( "jobs:qa_break_into_gridboxes",
kwargs={'jobline': self.jobline.pk ,'stockline': self.stockline.pk})
response = self.client.post( url_gridbox, data)
self.assertRedirects( response, url_fill_gridbox)
where you are testing error responses or GET, you can obtain the rendered HTML with
body = response.content.decode()
and you can look at entities in the context data that was used for rendering:
form_errors = response.context['form'].errors
As for setting up some objects for the view to process, you can either just create and save them with code (often in the setUp method), or you can use faker and factory-boy
first i update my model instance, after that i tried to insert a new data but showing
"IntegrityError('duplicate key value violates unique constraint "RFIDActivation_ActivationId_key"\nDETAIL: Key ("ActivationId")=(6de9ed9a) already exists.\n',)"
Models.py
class RFIDActivation(models.Model):
RFIDActivationId = models.AutoField(primary_key=True, db_column='RFIDActivationId')
Device = models.ForeignKey(Device, on_delete=models.CASCADE, db_column='DeviceId')
Employee = models.ForeignKey(Employee, on_delete=models.CASCADE, db_column='EmployeeId')
ActivationId = models.CharField(max_length=10, unique=True, default=uuid4().hex[:8])
ActivationStatus = models.CharField(max_length=1)default=None)
class Meta:
db_table = "RFIDActivation"
my serializer.py
class RFIDActivationSerializer(serializers.ModelSerializer):
class Meta:
model = RFIDActivation
fields = '__all__'
view.py
#api_view(["POST"])
#permission_classes([IsAuthenticated])
def rfid_activation_initial(request):
RFIDActivation.objects.filter(Employee=request.POST.get("Employee")).update(
ActivationStatus='2',
ActivationMessage='Abort'
)
rfid_activation = {
'Employee': request.POST.get("Employee"),
'Device': request.POST.get("Device"),
'ActivationStatus': "0",
'ActivationMessage': "RFID Activation Initiated"
}
rfid_serializer = RFIDActivationSerializer(data=rfid_activation)
if rfid_serializer.is_valid():
rfid_serializer.save()
but rfid_serializer.save() shows exceptionexception
Previous ActivationId is using to saving the new data. ActivationId is unique and auto generated. How can i solve this. Error is showing trying to insert after update query
view.py for working fine code only insertion.
#api_view(["POST"])
#permission_classes([IsAuthenticated])
def rfid_data_add(request):
rfid_activation = {
'Employee': request.POST.get("Employee"),
'Device': request.POST.get("Device"),
'ActivationStatus': "0",
'ActivationMessage': "RFID Activation Initiated"
}
rfid_serializer = RFIDActivationSerializer(data=rfid_activation)
if rfid_serializer.is_valid():
rfid_serializer.save()
In restframework, serializer.save() has different behavior based on creation and update. If you pass instance to serializer, it will update the object, otherwise, it will create a new one
#api_view(['POST', 'PUT'])
def rfid_data(request, pk):
"""
Update or delete a rfid data.
"""
rfid_activation = {
...
}
if request.method == 'POST':
rfid_serializer = RFIDActivationSerializer(data=rfid_activation)
if rfid_serializer.is_valid():
rfid_serializer.save()
return Response(rfid_serializer.data,status=status.HTTP_201_CREATED)
return return Response(rfid_serializer.data, status=status.HTTP_400_BAD_REQUEST)
# PUT is used for object update
elif request.method == 'PUT':
rfid = RFIDActivation.objects.get(pk=pk)
# pass instance to your serializer
# pass partial=True to allow partial updates
rfid_serializer = RFIDActivationSerializer(instance=rfid, data=rfid_activation, partial=True)
if rfid_serializer.is_valid():
rfid_serializer.save()
return Response(rfid_serializer.data)
return Response(rfid_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
UPDATE
default is calculated at import time, in your case, your default value for activation_id is calculated once and never change for subsequent instances.
You have to define a function, thus default callable will be updated each time on object creation, read more here
def activation_id_generator(self):
return uuid4().hex[:8]
# then in your class
class RFIDActivation(models.Model):
...
ActivationId = models.CharField(max_length=10, unique=True, default=activation_id_generator)
This question is directly related to this question, but that one is now outdated it seems.
I am trying to test a view without having to access the database. To do that I need to Mock a RelatedManager on the user.
I am using pytest and pytest-mock.
models.py
# truncated for brevity, taken from django-rest-knox
class AuthToken(models.Model):
user = models.ForeignKey(
User,
null=False,
blank=False,
related_name='auth_token_set',
on_delete=models.CASCADE
)
views.py
class ChangeEmail(APIView):
permission_classes = [permissions.IsAdmin]
serializer_class = serializers.ChangeEmail
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = request.user
user.email = request.validated_data['email']
user.save()
# Logout user from all devices
user.auth_token_set.all().delete() # <--- How do I mock this?
return Response(status=status.HTTP_200_OK)
test_views.py
def test_valid(mocker, user_factory):
user = user_factory.build()
user.id = 1
data = {
'email': 'foo#example.com'
}
factory = APIRequestFactory()
request = factory.post('/', data=data)
force_authenticate(request, user)
mocker.patch.object(user, "save")
related_manager = mocker.patch(
'django.db.models.fields.related.ReverseManyToOneDescriptor.__set__',
return_vaue=mocker.MagicMock()
)
related_manager.all = mocker.MagicMock()
related_manager.all.delete = mocker.MagicMock()
response = ChangeEmail.as_view()(request)
assert response.status_code == status.HTTP_200_OK
Drawing from the answer in the linked question I tried to patch the ReverseManyToOneDescriptor. However, it does not appear to actually get mocked because the test is still trying to connect to the database when it tries to delete the user's auth_token_set.
You'll need to mock the return value of the create_reverse_many_to_one_manager factory function. Example:
def test_valid(mocker):
mgr = mocker.MagicMock()
mocker.patch(
'django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager',
return_value=mgr
)
user = user_factory.build()
user.id = 1
...
mgr.assert_called()
Beware that the above example will mock the rev manager for all models. If you need a more fine-grained approach (e.g. patch User.auth_token's rev manager only, leave the rest unpatched), provide a custom factory impl, e.g.
def test_valid(mocker):
mgr = mocker.MagicMock()
factory_orig = related_descriptors.create_reverse_many_to_one_manager
def my_factory(superclass, rel):
if rel.model == User and rel.name == 'auth_token_set':
return mgr
else:
return factory_orig(superclass, rel)
mocker.patch(
'django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager',
my_factory
)
user = user_factory.build()
user.id = 1
...
mgr.assert_called()
I accomplish this doing this(Django 1.11.5)
#patch("django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager")
def test_reverse_mock_count(self, reverse_mock):
instance = mommy.make(DjangoModel)
manager_mock = MagicMock
count_mock = MagicMock()
manager_mock.count = count_mock()
reverse_mock.return_value = manager_mock
instance.related_manager.count()
self.assertTrue(count_mock.called)
hope this help!
If you use django's APITestCase, this becomes relatively simple.
class TestChangeEmail(APITestCase):
def test_valid(self):
user = UserFactory()
auth_token = AuthToken.objects.create(user=user)
response = self.client.post(
reverse('your endpoint'),
data={'email': 'foo#example.com'}
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertFalse(AuthToken.objects.filter(user=user).exists())
This avoids mocking altogether and gives a more accurate representation of your logic.
unittest.PropertyMock can be used to mock descriptors in a way that doesn't require mocking internal implementation details:
def test_valid(mocker, user_factory):
user = user_factory.build()
user.id = 1
data = {
'email': 'foo#example.com'
}
factory = APIRequestFactory()
request = factory.post('/', data=data)
force_authenticate(request, user)
mocker.patch.object(user, "save")
with mocker.patch('app.views.User.auth_token_set', new_callable=PropertyMock) as mock_auth_token_set:
mock_delete = mocker.MagicMock()
mock_auth_token_set.return_value.all.return_value.delete = mock_delete
response = ChangeEmail.as_view()(request)
assert response.status_code == status.HTTP_200_OK
assert mock_delete.call_count == 1
I am building a testing application and I need to run through a series of test steps. I have a few questions surrounding the concern of mine.
When you create a test, you will select wether it will be a full or partial test. A full test will run through all the test steps. A partial test will be able to go a individual step.
When a full test is created then all the test steps should be created by default.
How will I filter the test steps when a user selects a partial test?
In my test-detail template, I need to go through a series of questions which will be forms that send requests and recieve responses from an api.
I am wondering how i can use a form set or another way to pass on to the next form. For example, step 1 - do whatever it says, click next. Next will be the form submit which will send the request and the next form will populate the django template.
Any help is greatly appreciated.
View.py
def create_test(request, id=None):
form = PrinterTestForm(request.POST or None)
if form.is_valid():
instance = form.save(commit=False)
instance.printer = Printer.objects.get(id=id)
instance.save()
# test = Test.objects.get_object_or_404(Test, id=instance.id)
return redirect(instance.get_absolute_url())
else:
test_name = None
test_type = None
test = None
context = {
"form": form,
}
return render(request, "testing/create_test.html", context)
def test_detail(request, id):
test_instance = get_object_or_404(Test, id=id)
printer_status_check = printer_check(test_instance)
stepOne =
context = {
"test": test_instance,
'printer_status': printer_status_check
}
return render(request, "testing/test_detail.html", context)
def printer_check(request):
device_dns = request.printer.device_dns
status = get_status_test(device_dns)
return status
models.py
TESTCHOICES = (
('Full Test', 'full_test'),
('Partial Test', 'partial_test')
)
TEST_REGEX = '^[a-zA-Z0-9.+-_ ]*$'
class Test(models.Model):
test_name = models.CharField(max_length=120, validators=[RegexValidator(regex=TEST_REGEX, message="Only Alphanumeric characters allowed in Test Name", code="invalid_test_name")])
test_type = models.CharField(max_length=32, choices=TESTCHOICES, default='Full Test')
printer = models.ForeignKey(Printer, on_delete=models.CASCADE)
timestamp = models.DateTimeField(auto_now=True)
def __str__(self):
return self.test_name
def get_absolute_url(self):
return reverse('testing:test_detail', kwargs={'id': self.id })
class TestStep(models.Model):
test = models.ForeignKey(Test, on_delete=models.CASCADE)
test_type_step = models.CharField(max_length=120, choices=TESTCHOICES)
test_step_name = models.CharField(max_length=240)
I have models.py
class Visit(Model):
reference_visit = models.ForeignKey('self',
help_text="Visit needs a refrence to Prior Visits",
null=True, blank=True)
show_prior_responses = models.BooleanField(default=False,
help_text="Show PriorResponses")
# has many field but i am making it short.
def __unicode__(self):
result = """Visit id:%s pt:%s""" % (self.id, self.patient.id)
return result
forms.py
class VisitSetupForm(Form):
list_visit_ids = ModelChoiceField(
queryset=Visit.objects.none(),
empty_label='Select Revisit ID',required=False)
show_prior_visit = ModelChoiceField(
queryset=User.objects.all(),
empty_label="Select User for Revisit",required = False)
has many but question is on list_visit_ids.
views.py
def setup(request):
"""
Allow an Admin user the ability to setup a patient & visit all at once.
"""
if request.user.is_superuser:
form_class = AdminVisitSetupForm
all_topics = True
else:
form_class = VisitSetupForm
all_topics = False
f = form_class()
# Get a list of topics for each report.
report_topics = {}
for r in Interview.objects.all():
report_topics[r.id] = [t['ad'] for t in r.topics.values('ad')]
data = {
'superuser':request.user.is_superuser,
'report_topics':simplejson.dumps(report_topics)
}
try:
request.user.reviewer
data['reviewer'] = True
except:
pass
if request.method == "POST":
f = form_class(request.POST)
if f.is_valid():
# Create the patient, generate a password, and send them on their way.
cd = f.cleaned_data
patient = None
if cd['revisit']:
# Check for an existing user first.
try:
patient = Patient.objects.get(username=cd['username'])
except Patient.DoesNotExist, e:
data['form'] = f
data['msg'] = 'There is no user with this username.'
return render_to_response('visit/setup.html', data, context_instance=RequestContext(request))
admin_user = get_user(request)
organization = None
if admin_user:
organization = admin_user.organization
if patient and not request.user.is_superuser:
# Make sure the patient they've selected is one of their own.
if patient.organization != organization:
return HttpResponseForbidden('You are not allowed to see this page.')
if not patient:
password = generate_password()
user = User.objects.create_user(cd['username'], cd['contact_email'], password)
user.first_name = cd['first_name']
user.last_name = cd['last_name']
user.save()
patient = Patient(
user=user,
username=user.username,
contact_phone=cd['contact_phone'],
date_of_birth=cd['date_of_birth'],
email=user.email,
first_name=user.first_name,
gender=cd['gender'],
last_name=user.last_name,
maiden_name=cd['maiden_name'],
organization=organization,
patient_type=cd['patient_type'],
security_answer=cd['security_answer'],
security_question=cd['security_question'],
)
patient.save()
# Send them an email.
t = loader.get_template('www/new_account.txt')
c = Context({
'password':'%s-%s-%s' % (password[:3], password[3:5], password[5:]),
'patient':patient
})
msg = t.render(c)
try:
send_mail(
'A request by your physician to do an online medical history before your appointment.',
msg,
'support#careprep.com',
[user.email]
)
except Exception, e:
log.error('Could not send email for new account %s because: [%s]' % (user.username, e))
request.session['password'] = password
# Create the Visit, too.
interview = cd['interview']
list_visit_ids = cd['list_visit_ids']
print list_visit_ids
visit = Visit(
reference_visit = cd['list_visit_ids'],
show_prior_responses = cd['show_prior_responses'],
patient=patient
)
if request.user.is_superuser:
topics = cd['topics']
else:
topics = set(list(interview.topics.all()) + list(cd['topics']))
reviewer_mode = cd.get('reviewer_mode') or patient.patient_type == 'Reviewer'
url, visit = initialize_visit(
request,
patient=patient,
starting_section=interview.starting_section,
visit_title='%s %s' % (patient, interview.title),
topics=topics,
reviewer_mode=reviewer_mode,
chief_complaint=cd['chief_complaint'],
location=cd['interview_site'],
reference_visit = cd['list_visit_ids'],
show_prior_responses = cd['show_prior_responses'],
)
next_url = "/visit/confirmation/%s/%s/?next=%s" % (patient.user.id, interview.id, url)
else:
v = Visit.objects.get(pk=request.POST['list_visit_ids'])
print v
return HttpResponseRedirect(next_url)
# all the fields that are not given pls ignore.
The template is fine.
Now watch forms.py when i do list_visit_ids = ModelChoiceField(queryset=Visit.objects.all(), empty_label='Select Revisit ID',required=False) It works perfectly fine on my local machine.But on my server it has around 6000 visit objects so this page hangs or i should say keep on loading.
So initially i changed it to list_visit_ids = ModelChoiceField(queryset=Visit.objects.none(), empty_label='Select Revisit ID',required=False)
Now i know that by this the form becomes invalid and should go to the else part Now my question how do i make reference_visit=cd['list_visit_ids'] in else (form is invalid)
case save().How do i override the none() attribute.
Thanks in advance i will really appreciate.
If your goal is to save your html page load by removing the 6000 choices (which I've done too: 10000+ <option> fields wrapped by misc html will absolutely choke a page), you shouldn't be using a ChoiceField at all. By setting queryset=Visit.objects.none() you're allowing zero choices and nothing passed in will validate.
You either show 6000 select item drop downs, radio boxes, etc., or find a way to /not/ have a giant select drop down (such as a hidden input or charfield), not fake around a ModelChoiceField who's main purpose is to populate that select drop down and validate.
In short: don't use a ModelChoiceField if you're not going to be using the html choices generated by it. Use something else and do the validation / model pulling yourself via the clean_FOO methods.
class MyForm(forms.Form):
my_input = forms.CharField()
def clean_my_input(self):
input = self.cleaned_data.get('my_input')
try:
return MyModel.objects.get(pk=input) # add a filter here if you want
# (whatever filters you were using in the queryset argument)
except MyModel.DoesNotExist:
raise forms.ValidationError("Doesn't exist / is invalid")
return input