Mock() 函数在 django2 中给出 TypeError
Mock() function gives TypeError in django2
我正在关注 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)
我的完整回溯:
可以是什么?
谢谢
终于在网上找到了解决方法
Django 2 doesn't support anymore bytestrings 在某些地方,因此当视图重定向模拟 Class List 时,它作为模拟对象执行,并且 iri_to_uri
django 函数会抛出错误。在 django 1.11 中 iri_to_uri
将 iri 强制为一个字节 return quote(force_bytes(iri), safe="/#%[]=:;$&()+,!?*@'~")
而现在是 return quote(iri, safe="/#%[]=:;$&()+,!?*@'~")
。所以解决方案是 return redirect(str(list_.get_absolute_url()))
而不是 lists.views.py
中的 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})
我希望这对其他人有帮助
我已经在测试代码中解决了这个问题,没有更改所需的生产代码,如下所示:
@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()))
这样的解决方法,这是不可取的,因为它:
- 不是想要的生产代码
- 不太优雅的生产代码
- 只是 return 将模拟对象的名称作为字符串(即
<MagicMock name='NewListForm().save().get_absolute_url()' id='4363470544'>
),这不是我们想要的:我们想调用 get_absolute_url()
方法和 那个方法(不是str()
)应该return一个url作为一个字符串。
我正在关注 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)
我的完整回溯:
可以是什么?
谢谢
终于在网上找到了解决方法
Django 2 doesn't support anymore bytestrings 在某些地方,因此当视图重定向模拟 Class List 时,它作为模拟对象执行,并且 iri_to_uri
django 函数会抛出错误。在 django 1.11 中 iri_to_uri
将 iri 强制为一个字节 return quote(force_bytes(iri), safe="/#%[]=:;$&()+,!?*@'~")
而现在是 return quote(iri, safe="/#%[]=:;$&()+,!?*@'~")
。所以解决方案是 return redirect(str(list_.get_absolute_url()))
而不是 lists.views.py
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})
我希望这对其他人有帮助
我已经在测试代码中解决了这个问题,没有更改所需的生产代码,如下所示:
@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()))
这样的解决方法,这是不可取的,因为它:
- 不是想要的生产代码
- 不太优雅的生产代码
- 只是 return 将模拟对象的名称作为字符串(即
<MagicMock name='NewListForm().save().get_absolute_url()' id='4363470544'>
),这不是我们想要的:我们想调用get_absolute_url()
方法和 那个方法(不是str()
)应该return一个url作为一个字符串。