Mock()函数在django2中给出类型错误

gj3fmq9x  于 2022-11-18  发布在  Go
关注(0)|答案(2)|浏览(150)

我在跟踪this tutorial
当我运行test_views.py时,出现了一个错误,根据作者的说法,这个错误不应该出现:TypeError: quote_from_bytes() expected bytes .
我的views和我的test_views和书中的一样,但是我用的是django 2.0.6而不是django 1.11,所以我的url.py改变了,所以可能有问题。

编辑:

再看一下,问题似乎出在mock()函数中。
当我使用patch('lists.views.List')时,视图中的Print(list_)给出<MagicMock name='List()' id='79765800'>而不是List object (1)

/编辑

我的lists/urls.py

urlpatterns = [
    path('new', views.new_list, name='new_list'),
    path('<slug:list_id>/',
        views.view_list, name='view_list'),
    path('users/<email>/',         # I'm not sure about this one but it works in other tests
        views.my_lists, name='my_lists'),
]
#instead of:
#urlpatterns = [
#    url(r'^new$', views.new_list, name='new_list'),
#    url(r'^(\d+)/$', views.view_list, name='view_list'),
#    url(r'^users/(.+)/$', views.my_lists, name='my_lists'),
#]

我的lists/views.py

[...]
def new_list(request):
    form = ItemForm(data=request.POST)
    if form.is_valid():
        list_ = List()
        list_.owner = request.user
        list_.save()
        form.save(for_list=list_)
        Print(list_)
        return redirect(list_)
    else:
        return render(request, 'home.html', {"form": form})

我的lists/tests/test_views.py

@patch('lists.views.List')
@patch('lists.views.ItemForm')
def test_list_owner_is_saved_if_user_is_authenticated(self, 
    mockItemFormClass, mockListClass
):
    user = User.objects.create(email='a@b.com')
    self.client.force_login(user)
    self.client.post('/lists/new', data={'text': 'new item'})
    mock_list = mockListClass.return_value
    self.assertEqual(mock_list.owner, user)

我的完整回溯:
类型错误:quote_from_bytes()需要字节

能是什么?
谢谢你

bfhwhh0e

bfhwhh0e1#

最后我在网上找到了解决办法。
Django 2在某些地方不再支持字节串,所以当视图重定向mock Class List时,它会作为一个mock对象重定向,并且iri_to_uri django函数会抛出一个错误。在Django 1. 11中,iri_to_uri强制将iri改为字节return quote(force_bytes(iri), safe="/#%[]=:;$&()+,!?*@'~"),而现在是return quote(iri, safe="/#%[]=:;$&()+,!?*@'~")。所以解决方案是在lists.views.py中使用return redirect(str(list_.get_absolute_url()))而不是return redirect(list_)

def new_list(request):
    form = ItemForm(data=request.POST)
    if form.is_valid():
        list_ = List()
        list_.owner = request.user
        list_.save()
        form.save(for_list=list_)
        #return redirect(list_)
        return redirect(str(list_.get_absolute_url()))
    else:
        return render(request, 'home.html', {"form": form})

我希望这对其他人有帮助

fdbelqdn

fdbelqdn2#

我已经在测试代码中解决了这个问题,而没有更改所需的生产代码,如下所示:

@patch('lists.views.NewListForm')
class NewListViewUnitTest(unittest.TestCase):
    def setUp(self):
        self.request = HttpRequest()
        self.request.POST['text'] = 'new list item'
        self.request.user = Mock()

def test_passes_POST_data_to_NewListForm(self, mockNewListForm):
    mock_form = mockNewListForm.return_value
    returned_object = mock_form.save.return_value
    returned_object.get_absolute_url.return_value = 'fakeurl'

    new_list2(self.request)

    mockNewListForm.assert_called_once_with(data=self.request.POST)

def test_saves_form_with_owner_if_form_valid(self, mockNewListForm):
    mock_form = mockNewListForm.return_value
    mock_form.is_valid.return_value = True
    returned_object = mock_form.save.return_value
    returned_object.get_absolute_url.return_value = 'fakeurl'

    new_list2(self.request)

    mock_form.save.assert_called_once_with(owner=self.request.user)

@patch('lists.views.redirect')
def test_redirects_to_form_returned_object_if_form_valid(
    self, mock_redirect, mockNewListForm
):
    mock_form = mockNewListForm.return_value
    mock_form.is_valid.return_value = True

    response = new_list2(self.request)

    self.assertEqual(response, mock_redirect.return_value)
    mock_redirect.assert_called_once_with(mock_form.save.return_value)

请注意,赋值some_method.return_value会设置some_method的响应,而不调用some_method(),因此我们还可以测试该方法是否只被调用了一次。
我喜欢这个解决方案的原因是它产生了所需的生产代码:

def new_list2(request):
    form = NewListForm(data=request.POST)
    list_ = form.save(owner=request.user)
    return redirect(list_)

..,而不是在生产代码中使用类似return redirect(str(list_.get_absolute_url()))的变通方法,这是不可取的,因为它:
1.不是所需的生产代码
1.是不太雅的生产代码
1.只返回模拟对象的名称作为字符串(即<MagicMock name='NewListForm().save().get_absolute_url()' id='4363470544'>),这不是我们想要的:我们希望调用get_absolute_url()方法,方法(不是str())应返回一个字符串形式的URL。

相关问题