【问题标题】:Django Unit tests can't ovveride variable from tessting when postDjango单元测试不能在发布时覆盖测试中的变量
【发布时间】:2023-03-16 07:04:01
【问题描述】:

我需要测试更新视图。 我的更新视图创建文件夹。对于测试,我想更改文件夹的路径。我尝试使用 override_settings 装饰器和来自 https://docs.djangoproject.com/en/2.2/topics/testing/tools/#django.test.modify_settings 的 with self.settings(): statment 更改设置

但是当我在测试中运行 post 时,文件夹是根据我的项目设置而不是测试设置创建的。 在我的设置中创建文件夹的路径保存在变量CERTIFICATION_WORKS_ROOT 中。 在我看来,我将其导入为from django.conf import settings,然后是output_path = settings.CERTIFICATION_WORKS_ROOT。为了调试,我在视图print(f'output_path in view - {output_path}') 和我的测试print(f'test settings - {settings.CERTIFICATION_WORKS_ROOT}') 中添加了。 在我的设置中,变量定义为:

CERTIFICATION_WORKS_ROOT = os.path.join('/home/oleh/Documents/certDB_output/')

这是我的更新视图类:

...

from django.conf import settings
output_path = settings.CERTIFICATION_WORKS_ROOT
print(f'output_path in view - {output_path}')

...

class UpdateCase(LoginRequiredMixin, PassRequestToFormViewMixin, UpdateView):
    model = Case
    login_url = reverse_lazy('login')
    template_name = 'case/update_case.html'
    form_class = CaseForm

    def summary_list(self):
        work_list = CertificationByDocumentAnalysis.\
            objects.filter(case=self.object).values(
                'id',
                'work_number',
                'startdate',
                'work_type',
                'producer__name',
                'producer__address',
                'producer__code',
                'case__id',
                'case__agreement'
            )
        conformity_assessment_list = ConformityAssessment.\
            objects.filter(case=self.object).values(
                'id',
                'work_number',
                'startdate',
                'work_type',
                'producer__name',
                'producer__address',
                'producer__code',
                'case__id',
                'case__agreement'
            )
        declaration_of_conformity_list = DeclarationOfConformity.\
            objects.filter(case=self.object).values(
                'id',
                'work_number',
                'startdate',
                'work_type',
                'producer__name',
                'producer__address',
                'producer__code',
                'case__id',
                'case__agreement'
            )
        hygienic_examination_list = HygienicExamination.\
            objects.filter(case=self.object).values(
                'id',
                'work_number',
                'startdate',
                'work_type',
                'producer__name',
                'producer__address',
                'producer__code',
                'case__id',
                'case__agreement'
            )
        summary_list = conformity_assessment_list.union(work_list)
        summary_list = hygienic_examination_list.union(summary_list)
        summary_list = declaration_of_conformity_list.union(summary_list)
        summary_list = summary_list.order_by('work_number')
        return summary_list

    def get_context_data(self, *args, **kwargs):
        context = super(UpdateCase, self).get_context_data(*args, **kwargs)
        try:
            calculation = Calculation.objects.\
                get(case__id=self.object.id)
        except Calculation.DoesNotExist:
            calculation = None
        case = self.object
        context['path'] = get_path(case)
        context['calculation'] = calculation
        context['case_id'] = case.id
        context['case_type'] = case.get_case_type_display
        context['next'] = 'update_case'
        context['summary_list'] = self.summary_list()

        if calculation != None:
            calculation_dailies = CalculationDaily.objects.filter(
                calculation=calculation)
            calculation_cars = CalculationCar.objects.filter(
                calculation=calculation)
            calculation_transits = CalculationExpense.objects.filter(
                calculation=calculation, expenses_type='transit')
            calculation_accommodations = CalculationExpense.objects.filter(
                calculation=calculation, expenses_type='accommodation')
            calculation_others = CalculationExpense.objects.filter(
                calculation=calculation, expenses_type='other')
            context['calculation_dailies'] = calculation_dailies
            context['calculation_cars'] = calculation_cars
            context['calculation_transits'] = calculation_transits
            context['calculation_accommodations'] = calculation_accommodations
            context['calculation_others'] = calculation_others

        try:
            business_trip = BusinessTrip.objects.get(case=case)
        except BusinessTrip.DoesNotExist:
            business_trip = None
        context['business_trip'] = business_trip
        if business_trip != None:
            business_trip_cars = BusinessTripCar.objects.filter(
                business_trip_employee__business_trip__id=business_trip.id)
            context['business_trip_cars'] = business_trip_cars
            business_trip_employees = BusinessTripEmployee.objects.filter(
                business_trip__id=business_trip.id)
            context['business_trip_employees'] = business_trip_employees
            business_trip_expenses = BusinessTripExpense.objects.filter(
                business_trip_employee__business_trip__id=business_trip.id)
            context['business_trip_expenses'] = business_trip_expenses
            business_trip_dailies = BusinessTripDaily.objects.filter(
                business_trip_employee__business_trip__id=business_trip.id)
            context['business_trip_dailies'] = business_trip_dailies
        return context
   
    def form_valid(self, form):
        if self.request.user != Case.objects.get(id=self.object.id).author and self.request.user.id != 1:
            return self.handle_no_permission()
        new_case = self.object
        old_case = Case.objects.get(id=self.object.id)
        rename_case_folder(new_case, old_case)
        self.object.author = Case.objects.get(id=self.object.id).author
        self.object = form.save()
        return HttpResponseRedirect(self.get_success_url())

    def get_success_url(self):
        '''payments = Payment.objects\
            .filter(case__id=self.object.id)'''
        messages.success(self.request, 'Справу успішно збережено!')
        return reverse('update_case', kwargs={'pk': self.kwargs.get('pk')})

    def post(self, request, *args, **kwargs):
        if request.POST.get('cancel_button'):
            messages.info(request, 'Редагування справи відмінено!')
            return HttpResponseRedirect(reverse('cases'))
        else:
            return super(UpdateCase, self).post(request, *args, **kwargs)

...

def rename_case_folder(new_case, old_case, *args, **kwargs):
    new_producer = kwargs.pop('new_producer', None)
    new_path = get_path(new_case)
    old_path = get_path(old_case)
    if new_producer is not None and new_producer not in old_path:
        old_path = old_path[:-(len(new_producer) + 2)]
    if not os.path.exists(old_path):
        os.makedirs(old_path)
    if old_path != new_path:
        shutil.move(old_path, new_path)
        if new_case.customer != old_case.customer:
            message = f"'{old_path}' moved to '{new_path}' successfully."
            logger = logging.getLogger(__name__)
            logger.info(message)
        else:
            message = f"'{old_path}' renamed to '{new_path}' successfully."
            logger = logging.getLogger(__name__)
            logger.info(message)
    return None

...

def get_path(case):
    if case.producers:
        producers = ' ' + case.producers
    else:
        producers = ''
    path = os.path.join(output_path,
                        get_clean_parametr(case.body.simple_name),
                        get_clean_parametr(case.customer.simple_name),
                        str(case.startdate.year),
                        (case.agreement.replace('/', '_')
                         + ' від '
                         + case.startdate.strftime('%d-%m-%Y')
                         + producers
                         ))
    return path

这是我的测试模块:

from datetime import date
import os

from django.test import TestCase, Client, override_settings
from django.urls import reverse
from django.contrib.auth.models import User
from django.core.files.images import ImageFile

from case.models import *

TEST_WORKS_ROOT = os.path.join('/home/oleh/Documents/test_output/')


@override_settings(CERTIFICATION_WORKS_ROOT=TEST_WORKS_ROOT)
class TestCases(TestCase):

    fixtures = ['cases_test_data.json']

    def setUp(self):
        body1 = Body.objects.get(name='body_one')
        body1.logo.save('uploads/logo-UC.svg',
            ImageFile(open('/home/oleh/Downloads/logo-UC.svg', 'rb'), 'logo-UC'))
        self.client = Client()
        # remember url to case list page
        self.url = reverse('update_case', kwargs={'pk': 1})
        #self.url = reverse('cases')

    def test_form(self):
        user = User.objects.get(id=1)
        self.client.force_login(user)
        response = self.client.get(self.url)
        #print(response.url)

        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'ЗМІНИТИ СПРАВУ')
        self.assertContains(response, 'Додати роботу')
        self.assertContains(response, 'Друк')
        self.assertContains(response, 'Тип договору')
        self.assertContains(response, 'Замовник')
        self.assertContains(response, 'name="save_button"')
        self.assertContains(response, 'name="cancel_button"')
        self.assertContains(response, f'action="{self.url}"')

    def test_success_update(self):
        from django.conf import settings
        print(f'test settings - {settings.CERTIFICATION_WORKS_ROOT}')
        user = User.objects.get(id=1)
        body = Body.objects.get(id=1)
        customer2 = Enterprise.objects.get(id=2)
        self.client.force_login(user)
        with self.settings(CERTIFICATION_WORKS_ROOT=TEST_WORKS_ROOT):
            from django.conf import settings
            print(f'test settings - {settings.CERTIFICATION_WORKS_ROOT}')
            response = self.client.post(
                self.url,
                {
                    "body": body.id,
                    "author": user.id,
                    "agreement": "7/21/1",
                    "agreement_number": 1,
                    "case_type": "СО",
                    "currency": "USD",
                    "startdate": "2022-1-2",
                    "enddate": "2022-1-7",
                    "price": 150.00,
                    "customer": customer2.id
                },
                follow=True
            )

        self.assertEqual(response.status_code, 200)
        case = Case.objects.get(id=1)
        self.assertEqual(case.body.id, 1)
        self.assertEqual(case.author.id, 1)
        self.assertEqual(case.agreement, "7/21/1")
        self.assertEqual(case.agreement_number, 1)
        self.assertEqual(case.case_type, 'СО')
        self.assertEqual(case.currency, 'USD')
        self.assertEqual(case.startdate, date(2022, 1, 2))
        self.assertEqual(case.enddate, date(2022, 1, 7))
        self.assertEqual(case.price, 150.00)
        self.assertEqual(case.customer.id, 2)

        self.assertContains(response, 'Справу успішно збережено!')

        self.assertEqual(response.redirect_chain[0][0],
            f'/update-case/{case.id}/')
        from django.conf import settings
        print(f'test settings - {settings.CERTIFICATION_WORKS_ROOT}')

这是终端输出:

...
...
INFO 2022-01-23 09:45:20,665 signals: user None added Migration 0001_initial for sessions
output_path in view - /home/oleh/Documents/certDB_output/
System check identified no issues (0 silenced).
INFO 2022-01-23 09:45:22,858 signals: user None added body1
INFO 2022-01-23 09:45:22,869 signals: user None added user1
INFO 2022-01-23 09:45:22,877 signals: user None added enterprise_one, , код ЄДРПОУ: 
INFO 2022-01-23 09:45:22,882 signals: user None added enterprise_two, , код ЄДРПОУ: 
INFO 2022-01-23 09:45:22,913 signals: user None added body1 Договір №:7/21/1, від: 2022-01-01, Заявник: enterprise1, вартість: 100
INFO 2022-01-23 09:45:22,932 signals: user None added Справа №: 7/21/1; Сума оплати: 100; Дата оплати: 2022-01-04; Замовник: enterprise1
INFO 2022-01-23 09:45:22,961 signals: user None updated body1
INFO 2022-01-23 09:45:23,069 signals: user None added malcwkkfqvgmbwd768v4gn8f63f49tb6
INFO 2022-01-23 09:45:23,083 signals: user None added 86y7qzb4058ed9wajuqbz5t756sz1dmk
INFO 2022-01-23 09:45:23,092 signals: user None deleted malcwkkfqvgmbwd768v4gn8f63f49tb6
INFO 2022-01-23 09:45:23,103 signals: user None updated user1
INFO 2022-01-23 09:45:23,108 signals: user None updated 86y7qzb4058ed9wajuqbz5t756sz1dmk
.INFO 2022-01-23 09:45:23,381 signals: user None updated body1
test settings - /home/oleh/Documents/test_output/
INFO 2022-01-23 09:45:23,396 signals: user None added wucx5j3gd0esfm10twhf1vrtgxy8gjl7
INFO 2022-01-23 09:45:23,406 signals: user None added x0l3976s8x2hn2z8zwurilmjlzeokfk7
INFO 2022-01-23 09:45:23,412 signals: user None deleted wucx5j3gd0esfm10twhf1vrtgxy8gjl7
INFO 2022-01-23 09:45:23,417 signals: user None updated user1
INFO 2022-01-23 09:45:23,422 signals: user None updated x0l3976s8x2hn2z8zwurilmjlzeokfk7
test settings - /home/oleh/Documents/test_output/
INFO 2022-01-23 09:45:23,477 views: '/home/oleh/Documents/certDB_output/body1/enterprise1/2022/7_21_1 від 01-01-2022' moved to '/home/oleh/Documents/certDB_output/body1/enterprise2/2022/7_21_1 від 02-01-2022' successfully.
INFO 2022-01-23 09:45:23,499 signals: user None updated body1 Договір №:7/21/1, від: 2022-01-02, Заявник: enterprise2, вартість: 150.0
test settings - /home/oleh/Documents/test_output/
.
----------------------------------------------------------------------
Ran 2 tests in 0.910s

OK
Destroying test database for alias 'default'...

文件夹是在certDB_output 而非test_output 文件夹中创建的

【问题讨论】:

  • 你应该从你的视图函数中发布代码,因为这就是意外行为所在。
  • 我已经发布了我的视图函数
  • 您的视图函数中没有print(f'output_path in view - {output_path}')。代码的重要部分是,您可以在其中导入from django.conf import settings,然后访问您的变量。但是在您发布的视图中,您无法访问您的设置...
  • 我的视图非常大(我想在将来将其重构为部分),所以我用部分更新了我的帖子,其中显示了从设置导入变量、updatecase 视图类、创建文件夹的函数rename_case_folder 和为前一个函数 get_path 准备路径的函数

标签: django unit-testing


【解决方案1】:

由于您的代码当前是编写的,因此在导入时设置只读一次。

删除output_path = settings.CERTIFICATION_WORKS_ROOT 并在您的get_path 方法中使用settings.CERTIFICATION_WORKS_ROOT。

或者,在您的测试中修补 case.views.output_path。

【讨论】:

  • 是的,它有效。感谢您的帮助
【解决方案2】:

Django 建议不要为你的设置设置别名,但这就是你所做的。见docs

最后,避免将您的设置作为模块级常量的别名,因为 override_settings() 不会对此类值起作用,因为它们仅在模块第一次导入时进行评估。

因此,在 get_path 中,您应该将 outout_path 替换为 settings.CERTIFICATION_WORKS_ROOT,它会起作用

【讨论】:

  • 谢谢你,阿里。您的回答是正确的,您的 cmets 帮助找到了解决方案。
猜你喜欢
  • 1970-01-01
  • 2011-04-02
  • 1970-01-01
  • 2015-04-25
  • 1970-01-01
  • 2015-07-16
  • 2018-12-04
  • 2017-12-14
  • 1970-01-01
相关资源
最近更新 更多