  class TestSimpleRouter(TestCase):
      def setUp(self):
          self.router = SimpleRouter()
  
      def test_link_and_action_decorator(self):
          routes = self.router.get_routes(BasicViewSet)
          decorator_routes = routes[2:]
          # Make sure all these endpoints exist and none have been clobbered
          for i, endpoint in enumerate(['action1', 'action2', 'action3', 'link1', 'link2']):
              route = decorator_routes[i]
              # check url listing
              assert route.url == '^{{prefix}}/{{lookup}}/{0}{{trailing_slash}}$'.format(endpoint)
              # check method to function mapping
              if endpoint == 'action3':
                  methods_map = ['post', 'delete']
              elif endpoint.startswith('action'):
                  methods_map = ['post']
              else:
                  methods_map = ['get']
              for method in methods_map:
                  assert route.mapping[method] == endpoint
  
  
  class TestRootView(URLPatternsTestCase, TestCase):
      urlpatterns = [
          url(r'^non-namespaced/', include(namespaced_router.urls)),
 
</s> remove class TestSimpleRouter(TestCase):
 </s> add  </s> remove class BrowsableAPIRendererTests(TestCase):
 </s> add class BrowsableAPIRendererTests(URLPatternsTestCase):
    class ExampleViewSet(ViewSet):
        def list(self, request):
            return Response()

        @action(detail=False, name="Extra list action")
        def list_action(self, request):
            raise NotImplementedError

    router = SimpleRouter()
    router.register('examples', ExampleViewSet, base_name='example')
    urlpatterns = [url(r'^api/', include(router.urls))] </s> remove                 pass
 </s> add                 raise NotImplementedError
    def test_action_routes(self):
        # Get action routes (first two are list/detail)
        routes = self.router.get_routes(BasicViewSet)[2:]

        assert routes[0].url == '^{prefix}/{lookup}/action1{trailing_slash}$'
        assert routes[0].mapping == {
            'post': 'action1',
        }

        assert routes[1].url == '^{prefix}/{lookup}/action2{trailing_slash}$'
        assert routes[1].mapping == {
            'post': 'action2',
            'delete': 'action2',
        }

        assert routes[2].url == '^{prefix}/{lookup}/action3{trailing_slash}$'
        assert routes[2].mapping == {
            'post': 'action3',
            'delete': 'action3_delete',
        }

    def test_multiple_action_handlers(self):
        # Standard action
        response = self.client.post(reverse('basic-action3', args=[1]))
        assert response.data == {'post': '1'}

        # Additional handler registered with MethodMapper
        response = self.client.delete(reverse('basic-action3', args=[1]))
        assert response.data == {'delete': '1'}
  class TestSimpleRouter(TestCase):
      def setUp(self):
          self.router = SimpleRouter()
  
    def test_action_routes(self):
        # Get action routes (first two are list/detail)
        routes = self.router.get_routes(BasicViewSet)[2:]

        assert routes[0].url == '^{prefix}/{lookup}/action1{trailing_slash}$'
        assert routes[0].mapping == {
            'post': 'action1',
        }

        assert routes[1].url == '^{prefix}/{lookup}/action2{trailing_slash}$'
        assert routes[1].mapping == {
            'post': 'action2',
            'delete': 'action2',
        }

        assert routes[2].url == '^{prefix}/{lookup}/action3{trailing_slash}$'
        assert routes[2].mapping == {
            'post': 'action3',
            'delete': 'action3_delete',
        }

    def test_multiple_action_handlers(self):
        # Standard action
        response = self.client.post(reverse('basic-action3', args=[1]))
        assert response.data == {'post': '1'}

        # Additional handler registered with MethodMapper
        response = self.client.delete(reverse('basic-action3', args=[1]))
        assert response.data == {'delete': '1'}
    def test_action_routes(self):
        # Get action routes (first two are list/detail)
        routes = self.router.get_routes(BasicViewSet)[2:]

        assert routes[0].url == '^{prefix}/{lookup}/action1{trailing_slash}$'
        assert routes[0].mapping == {
            'post': 'action1',
        }

        assert routes[1].url == '^{prefix}/{lookup}/action2{trailing_slash}$'
        assert routes[1].mapping == {
            'post': 'action2',
            'delete': 'action2',
        }

        assert routes[2].url == '^{prefix}/{lookup}/action3{trailing_slash}$'
        assert routes[2].mapping == {
            'post': 'action3',
            'delete': 'action3_delete',
        }

    def test_multiple_action_handlers(self):
        # Standard action
        response = self.client.post(reverse('basic-action3', args=[1]))
        assert response.data == {'post': '1'}

        # Additional handler registered with MethodMapper
        response = self.client.delete(reverse('basic-action3', args=[1]))
        assert response.data == {'delete': '1'}
    def test_action_routes(self):
        # Get action routes (first two are list/detail)
        routes = self.router.get_routes(BasicViewSet)[2:]

        assert routes[0].url == '^{prefix}/{lookup}/action1{trailing_slash}$'
        assert routes[0].mapping == {
            'post': 'action1',
        }

        assert routes[1].url == '^{prefix}/{lookup}/action2{trailing_slash}$'
        assert routes[1].mapping == {
            'post': 'action2',
            'delete': 'action2',
        }

        assert routes[2].url == '^{prefix}/{lookup}/action3{trailing_slash}$'
        assert routes[2].mapping == {
            'post': 'action3',
            'delete': 'action3_delete',
        }

    def test_multiple_action_handlers(self):
        # Standard action
        response = self.client.post(reverse('basic-action3', args=[1]))
        assert response.data == {'post': '1'}

        # Additional handler registered with MethodMapper
        response = self.client.delete(reverse('basic-action3', args=[1]))
        assert response.data == {'delete': '1'}
    def test_action_routes(self):
        # Get action routes (first two are list/detail)
        routes = self.router.get_routes(BasicViewSet)[2:]

        assert routes[0].url == '^{prefix}/{lookup}/action1{trailing_slash}$'
        assert routes[0].mapping == {
            'post': 'action1',
        }

        assert routes[1].url == '^{prefix}/{lookup}/action2{trailing_slash}$'
        assert routes[1].mapping == {
            'post': 'action2',
            'delete': 'action2',
        }

        assert routes[2].url == '^{prefix}/{lookup}/action3{trailing_slash}$'
        assert routes[2].mapping == {
            'post': 'action3',
            'delete': 'action3_delete',
        }

    def test_multiple_action_handlers(self):
        # Standard action
        response = self.client.post(reverse('basic-action3', args=[1]))
        assert response.data == {'post': '1'}

        # Additional handler registered with MethodMapper
        response = self.client.delete(reverse('basic-action3', args=[1]))
        assert response.data == {'delete': '1'}
    def test_action_routes(self):
        # Get action routes (first two are list/detail)
        routes = self.router.get_routes(BasicViewSet)[2:]

        assert routes[0].url == '^{prefix}/{lookup}/action1{trailing_slash}$'
        assert routes[0].mapping == {
            'post': 'action1',
        }

        assert routes[1].url == '^{prefix}/{lookup}/action2{trailing_slash}$'
        assert routes[1].mapping == {
            'post': 'action2',
            'delete': 'action2',
        }

        assert routes[2].url == '^{prefix}/{lookup}/action3{trailing_slash}$'
        assert routes[2].mapping == {
            'post': 'action3',
            'delete': 'action3_delete',
        }

    def test_multiple_action_handlers(self):
        # Standard action
        response = self.client.post(reverse('basic-action3', args=[1]))
        assert response.data == {'post': '1'}

        # Additional handler registered with MethodMapper
        response = self.client.delete(reverse('basic-action3', args=[1]))
        assert response.data == {'delete': '1'}
    def test_action_routes(self):
        # Get action routes (first two are list/detail)
        routes = self.router.get_routes(BasicViewSet)[2:]

        assert routes[0].url == '^{prefix}/{lookup}/action1{trailing_slash}$'
        assert routes[0].mapping == {
            'post': 'action1',
        }

        assert routes[1].url == '^{prefix}/{lookup}/action2{trailing_slash}$'
        assert routes[1].mapping == {
            'post': 'action2',
            'delete': 'action2',
        }

        assert routes[2].url == '^{prefix}/{lookup}/action3{trailing_slash}$'
        assert routes[2].mapping == {
            'post': 'action3',
            'delete': 'action3_delete',
        }

    def test_multiple_action_handlers(self):
        # Standard action
        response = self.client.post(reverse('basic-action3', args=[1]))
        assert response.data == {'post': '1'}

        # Additional handler registered with MethodMapper
        response = self.client.delete(reverse('basic-action3', args=[1]))
        assert response.data == {'delete': '1'}
    def test_action_routes(self):
        # Get action routes (first two are list/detail)
        routes = self.router.get_routes(BasicViewSet)[2:]

        assert routes[0].url == '^{prefix}/{lookup}/action1{trailing_slash}$'
        assert routes[0].mapping == {
            'post': 'action1',
        }

        assert routes[1].url == '^{prefix}/{lookup}/action2{trailing_slash}$'
        assert routes[1].mapping == {
            'post': 'action2',
            'delete': 'action2',
        }

        assert routes[2].url == '^{prefix}/{lookup}/action3{trailing_slash}$'
        assert routes[2].mapping == {
            'post': 'action3',
            'delete': 'action3_delete',
        }

    def test_multiple_action_handlers(self):
        # Standard action
        response = self.client.post(reverse('basic-action3', args=[1]))
        assert response.data == {'post': '1'}

        # Additional handler registered with MethodMapper
        response = self.client.delete(reverse('basic-action3', args=[1]))
        assert response.data == {'delete': '1'}
    def test_action_routes(self):
        # Get action routes (first two are list/detail)
        routes = self.router.get_routes(BasicViewSet)[2:]

        assert routes[0].url == '^{prefix}/{lookup}/action1{trailing_slash}$'
        assert routes[0].mapping == {
            'post': 'action1',
        }

        assert routes[1].url == '^{prefix}/{lookup}/action2{trailing_slash}$'
        assert routes[1].mapping == {
            'post': 'action2',
            'delete': 'action2',
        }

        assert routes[2].url == '^{prefix}/{lookup}/action3{trailing_slash}$'
        assert routes[2].mapping == {
            'post': 'action3',
            'delete': 'action3_delete',
        }

    def test_multiple_action_handlers(self):
        # Standard action
        response = self.client.post(reverse('basic-action3', args=[1]))
        assert response.data == {'post': '1'}

        # Additional handler registered with MethodMapper
        response = self.client.delete(reverse('basic-action3', args=[1]))
        assert response.data == {'delete': '1'}
    def test_action_routes(self):
        # Get action routes (first two are list/detail)
        routes = self.router.get_routes(BasicViewSet)[2:]

        assert routes[0].url == '^{prefix}/{lookup}/action1{trailing_slash}$'
        assert routes[0].mapping == {
            'post': 'action1',
        }

        assert routes[1].url == '^{prefix}/{lookup}/action2{trailing_slash}$'
        assert routes[1].mapping == {
            'post': 'action2',
            'delete': 'action2',
        }

        assert routes[2].url == '^{prefix}/{lookup}/action3{trailing_slash}$'
        assert routes[2].mapping == {
            'post': 'action3',
            'delete': 'action3_delete',
        }

    def test_multiple_action_handlers(self):
        # Standard action
        response = self.client.post(reverse('basic-action3', args=[1]))
        assert response.data == {'post': '1'}

        # Additional handler registered with MethodMapper
        response = self.client.delete(reverse('basic-action3', args=[1]))
        assert response.data == {'delete': '1'}
    def test_action_routes(self):
        # Get action routes (first two are list/detail)
        routes = self.router.get_routes(BasicViewSet)[2:]

        assert routes[0].url == '^{prefix}/{lookup}/action1{trailing_slash}$'
        assert routes[0].mapping == {
            'post': 'action1',
        }

        assert routes[1].url == '^{prefix}/{lookup}/action2{trailing_slash}$'
        assert routes[1].mapping == {
            'post': 'action2',
            'delete': 'action2',
        }

        assert routes[2].url == '^{prefix}/{lookup}/action3{trailing_slash}$'
        assert routes[2].mapping == {
            'post': 'action3',
            'delete': 'action3_delete',
        }

    def test_multiple_action_handlers(self):
        # Standard action
        response = self.client.post(reverse('basic-action3', args=[1]))
        assert response.data == {'post': '1'}

        # Additional handler registered with MethodMapper
        response = self.client.delete(reverse('basic-action3', args=[1]))
        assert response.data == {'delete': '1'}
    def test_action_routes(self):
        # Get action routes (first two are list/detail)
        routes = self.router.get_routes(BasicViewSet)[2:]

        assert routes[0].url == '^{prefix}/{lookup}/action1{trailing_slash}$'
        assert routes[0].mapping == {
            'post': 'action1',
        }

        assert routes[1].url == '^{prefix}/{lookup}/action2{trailing_slash}$'
        assert routes[1].mapping == {
            'post': 'action2',
            'delete': 'action2',
        }

        assert routes[2].url == '^{prefix}/{lookup}/action3{trailing_slash}$'
        assert routes[2].mapping == {
            'post': 'action3',
            'delete': 'action3_delete',
        }

    def test_multiple_action_handlers(self):
        # Standard action
        response = self.client.post(reverse('basic-action3', args=[1]))
        assert response.data == {'post': '1'}

        # Additional handler registered with MethodMapper
        response = self.client.delete(reverse('basic-action3', args=[1]))
        assert response.data == {'delete': '1'}
    def test_action_routes(self):
        # Get action routes (first two are list/detail)
        routes = self.router.get_routes(BasicViewSet)[2:]

        assert routes[0].url == '^{prefix}/{lookup}/action1{trailing_slash}$'
        assert routes[0].mapping == {
            'post': 'action1',
        }

        assert routes[1].url == '^{prefix}/{lookup}/action2{trailing_slash}$'
        assert routes[1].mapping == {
            'post': 'action2',
            'delete': 'action2',
        }

        assert routes[2].url == '^{prefix}/{lookup}/action3{trailing_slash}$'
        assert routes[2].mapping == {
            'post': 'action3',
            'delete': 'action3_delete',
        }

    def test_multiple_action_handlers(self):
        # Standard action
        response = self.client.post(reverse('basic-action3', args=[1]))
        assert response.data == {'post': '1'}

        # Additional handler registered with MethodMapper
        response = self.client.delete(reverse('basic-action3', args=[1]))
        assert response.data == {'delete': '1'}
    def test_action_routes(self):
        # Get action routes (first two are list/detail)
        routes = self.router.get_routes(BasicViewSet)[2:]

        assert routes[0].url == '^{prefix}/{lookup}/action1{trailing_slash}$'
        assert routes[0].mapping == {
            'post': 'action1',
        }

        assert routes[1].url == '^{prefix}/{lookup}/action2{trailing_slash}$'
        assert routes[1].mapping == {
            'post': 'action2',
            'delete': 'action2',
        }

        assert routes[2].url == '^{prefix}/{lookup}/action3{trailing_slash}$'
        assert routes[2].mapping == {
            'post': 'action3',
            'delete': 'action3_delete',
        }

    def test_multiple_action_handlers(self):
        # Standard action
        response = self.client.post(reverse('basic-action3', args=[1]))
        assert response.data == {'post': '1'}

        # Additional handler registered with MethodMapper
        response = self.client.delete(reverse('basic-action3', args=[1]))
        assert response.data == {'delete': '1'}
    def test_action_routes(self):
        # Get action routes (first two are list/detail)
        routes = self.router.get_routes(BasicViewSet)[2:]

        assert routes[0].url == '^{prefix}/{lookup}/action1{trailing_slash}$'
        assert routes[0].mapping == {
            'post': 'action1',
        }

        assert routes[1].url == '^{prefix}/{lookup}/action2{trailing_slash}$'
        assert routes[1].mapping == {
            'post': 'action2',
            'delete': 'action2',
        }

        assert routes[2].url == '^{prefix}/{lookup}/action3{trailing_slash}$'
        assert routes[2].mapping == {
            'post': 'action3',
            'delete': 'action3_delete',
        }

    def test_multiple_action_handlers(self):
        # Standard action
        response = self.client.post(reverse('basic-action3', args=[1]))
        assert response.data == {'post': '1'}

        # Additional handler registered with MethodMapper
        response = self.client.delete(reverse('basic-action3', args=[1]))
        assert response.data == {'delete': '1'}
    def test_action_routes(self):
        # Get action routes (first two are list/detail)
        routes = self.router.get_routes(BasicViewSet)[2:]

        assert routes[0].url == '^{prefix}/{lookup}/action1{trailing_slash}$'
        assert routes[0].mapping == {
            'post': 'action1',
        }

        assert routes[1].url == '^{prefix}/{lookup}/action2{trailing_slash}$'
        assert routes[1].mapping == {
            'post': 'action2',
            'delete': 'action2',
        }

        assert routes[2].url == '^{prefix}/{lookup}/action3{trailing_slash}$'
        assert routes[2].mapping == {
            'post': 'action3',
            'delete': 'action3_delete',
        }

    def test_multiple_action_handlers(self):
        # Standard action
        response = self.client.post(reverse('basic-action3', args=[1]))
        assert response.data == {'post': '1'}

        # Additional handler registered with MethodMapper
        response = self.client.delete(reverse('basic-action3', args=[1]))
        assert response.data == {'delete': '1'}
    def test_action_routes(self):
        # Get action routes (first two are list/detail)
        routes = self.router.get_routes(BasicViewSet)[2:]

        assert routes[0].url == '^{prefix}/{lookup}/action1{trailing_slash}$'
        assert routes[0].mapping == {
            'post': 'action1',
        }

        assert routes[1].url == '^{prefix}/{lookup}/action2{trailing_slash}$'
        assert routes[1].mapping == {
            'post': 'action2',
            'delete': 'action2',
        }

        assert routes[2].url == '^{prefix}/{lookup}/action3{trailing_slash}$'
        assert routes[2].mapping == {
            'post': 'action3',
            'delete': 'action3_delete',
        }

    def test_multiple_action_handlers(self):
        # Standard action
        response = self.client.post(reverse('basic-action3', args=[1]))
        assert response.data == {'post': '1'}

        # Additional handler registered with MethodMapper
        response = self.client.delete(reverse('basic-action3', args=[1]))
        assert response.data == {'delete': '1'}
    def test_action_routes(self):
        # Get action routes (first two are list/detail)
        routes = self.router.get_routes(BasicViewSet)[2:]

        assert routes[0].url == '^{prefix}/{lookup}/action1{trailing_slash}$'
        assert routes[0].mapping == {
            'post': 'action1',
        }

        assert routes[1].url == '^{prefix}/{lookup}/action2{trailing_slash}$'
        assert routes[1].mapping == {
            'post': 'action2',
            'delete': 'action2',
        }

        assert routes[2].url == '^{prefix}/{lookup}/action3{trailing_slash}$'
        assert routes[2].mapping == {
            'post': 'action3',
            'delete': 'action3_delete',
        }

    def test_multiple_action_handlers(self):
        # Standard action
        response = self.client.post(reverse('basic-action3', args=[1]))
        assert response.data == {'post': '1'}

        # Additional handler registered with MethodMapper
        response = self.client.delete(reverse('basic-action3', args=[1]))
        assert response.data == {'delete': '1'}
  
  
  class TestRootView(URLPatternsTestCase, TestCase):
      urlpatterns = [
          url(r'^non-namespaced/', include(namespaced_router.urls)),
keep keep keep keep replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace keep keep keep keep keep
 Improvements to ViewSet extra actions (#5605)

* View suffix already set by initializer

* Add 'name' and 'description' attributes to ViewSet

ViewSets may now provide their `name` and `description` attributes
directly, instead of relying on view introspection to derive them.
These attributes may also be provided with the view's initkwargs.

The ViewSet `name` and `suffix` initkwargs are mutually exclusive.

The `action` decorator now provides the `name` and `description` to
the view's initkwargs. By default, these values are derived from the
method name and its docstring. The `name` may be overridden by providing
it as an argument to the decorator.

The `get_view_name` and `get_view_description` hooks now provide the
view instance to the handler, instead of the view class. The default
implementations of these handlers now respect the `name`/`description`.

* Add 'extra actions' to ViewSet & browsable APIs

* Update simple router tests

Removed old test logic around link/action decorators from `v2.3`. Also
simplified the test by making the results explicit instead of computed.

* Add method mapping to ViewSet actions

* Document extra action method mapping 
tests/test_routers.py