diff --git a/tests/unit/suites/libraries/cms/component/router/rules/JComponentRouterRulesMenuTest.php b/tests/unit/suites/libraries/cms/component/router/rules/JComponentRouterRulesMenuTest.php
index 77d250a91fb24..19c654eaf5a81 100644
--- a/tests/unit/suites/libraries/cms/component/router/rules/JComponentRouterRulesMenuTest.php
+++ b/tests/unit/suites/libraries/cms/component/router/rules/JComponentRouterRulesMenuTest.php
@@ -40,6 +40,10 @@ protected function setUp()
 	{
 		parent::setUp();
 
+		// Getting categories relies on the user access which relies on the session.
+		$this->saveFactoryState();
+		JFactory::$session = $this->getMockSession();
+
 		$app = $this->getMockCmsApp();
 		JFactory::$application = $app;
 		$router = new JComponentRouterViewInspector($app, $app->getMenu());
@@ -64,6 +68,21 @@ protected function setUp()
 		$this->object = new JComponentRouterRulesMenuInspector($router);
 	}
 
+	/**
+	 * Overrides the parent tearDown method.
+	 *
+	 * @return  void
+	 *
+	 * @see     \PHPUnit\Framework\TestCase::tearDown()
+	 * @since   __DEPLOY_VERSION__
+	 */
+	protected function tearDown()
+	{
+		$this->restoreFactoryState();
+
+		parent::tearDown();
+	}
+
 	/**
 	 * Gets the data set to be loaded into the database during setup
 	 *
diff --git a/tests/unit/suites/libraries/cms/component/router/rules/JComponentRouterRulesStandardTest.php b/tests/unit/suites/libraries/cms/component/router/rules/JComponentRouterRulesStandardTest.php
new file mode 100644
index 0000000000000..1655657487521
--- /dev/null
+++ b/tests/unit/suites/libraries/cms/component/router/rules/JComponentRouterRulesStandardTest.php
@@ -0,0 +1,296 @@
+<?php
+/**
+ * @package     Joomla.UnitTest
+ * @subpackage  Component
+ *
+ * @copyright   Copyright (C) 2005 - 2017 Open Source Matters, Inc. All rights reserved.
+ * @license     GNU General Public License version 2 or later; see LICENSE
+ */
+
+require_once __DIR__ . '/../stubs/ComContentRouter.php';
+require_once __DIR__ . '/stubs/MockJComponentRouterRulesMenuMenuObject.php';
+
+/**
+ * Test class for JComponentRouterRulesStandard.
+ *
+ * @package     Joomla.UnitTest
+ * @subpackage  Component
+ * @since       __DEPLOY_VERSION__
+ */
+class JComponentRouterRulesStandardTest extends TestCaseDatabase {
+
+	/**
+	 * Object under test
+	 *
+	 * @var    JComponentRouterRulesStandard
+	 * @since  __DEPLOY_VERSION__
+	 */
+	protected $object;
+
+	/**
+	 * Gets the data set to be loaded into the database during setup
+	 *
+	 * @return  PHPUnit_Extensions_Database_DataSet_CsvDataSet
+	 *
+	 * @since   __DEPLOY_VERSION__
+	 */
+	protected function getDataSet()
+	{
+		$dataSet = new PHPUnit_Extensions_Database_DataSet_CsvDataSet(',', "'", '\\');
+
+		$dataSet->addTable('jos_categories', JPATH_TEST_DATABASE . '/jos_categories.csv');
+		$dataSet->addTable('jos_content', JPATH_TEST_DATABASE . '/jos_content.csv');
+		$dataSet->addTable('jos_extensions', JPATH_TEST_DATABASE . '/jos_extensions.csv');
+		$dataSet->addTable('jos_menu', JPATH_TEST_DATABASE . '/jos_menu.csv');
+
+		return $dataSet;
+	}
+
+	/**
+	 * Sets up the fixture, for example, opens a network connection.
+	 * This method is called before a test is executed.
+	 *
+	 * @return  void
+	 *
+	 * @since   __DEPLOY_VERSION__
+	 */
+	protected function setUp()
+	{
+		parent::setUp();
+		$noIds = true;
+
+		// The menu object relies on a session so mock it.
+		$this->saveFactoryState();
+		JFactory::$session = $this->getMockSession();
+
+		$app = $this->getMockCmsApp();
+		JFactory::$application = $app;
+
+		$router = new ContentRouterStandardRuleOnly($app, new JMenuSite(array('app' => $app, 'language' => self::getMockLanguage())), $noIds);
+		$categories = new JComponentRouterViewconfiguration('categories');
+		$categories->setKey('id');
+		$router->registerView($categories);
+		$category = new JComponentRouterViewconfiguration('category');
+		$category->setKey('id')->setParent($categories)->setNestable()->addLayout('blog');
+		$router->registerView($category);
+		$article = new JComponentRouterViewconfiguration('article');
+		$article->setKey('id')->setParent($category, 'catid');
+		$router->registerView($article);
+		$archive = new JComponentRouterViewconfiguration('archive');
+		$router->registerView($archive);
+		$featured = new JComponentRouterViewconfiguration('featured');
+		$router->registerView($featured);
+		$form = new JComponentRouterViewconfiguration('form');
+		$router->registerView($form);
+
+		$this->object = new JComponentRouterRulesStandard($router);
+	}
+
+	/**
+	 * Overrides the parent tearDown method.
+	 *
+	 * @return  void
+	 *
+	 * @see     \PHPUnit\Framework\TestCase::tearDown()
+	 * @since   __DEPLOY_VERSION__
+	 */
+	protected function tearDown()
+	{
+		$this->restoreFactoryState();
+
+		parent::tearDown();
+	}
+
+	/**
+	 * Tests the __construct() method
+	 *
+	 * @return  void
+	 *
+	 * @covers  JComponentRouterRulesStandard::__construct
+	 * @since   __DEPLOY_VERSION__
+	 */
+	public function testConstruct()
+	{
+		$this->assertInstanceOf('JComponentRouterRulesStandard', $this->object);
+		$this->assertInstanceOf('JComponentRouterView', TestReflection::getValue($this->object, 'router'));
+	}
+
+	/**
+	 * Provides the data to test the build method.
+	 *
+	 * @return  array
+	 *
+	 * @since   __DEPLOY_VERSION__
+	 */
+	public function dataTestBuild()
+	{
+		return array(
+			array(
+				array(
+					'option' => 'com_content',
+					'view' => 'article',
+					'catid' => 19,
+					'id' => 8,
+					'Itemid' => 260
+				),
+				array(
+					'option' => 'com_content',
+					'Itemid' => 260
+				),
+				array(
+					0 => 'beginners'
+				),
+				'Error building a URL for an article with a parent category menu item'
+			),
+			array(
+				array(
+					'option' => 'com_content',
+					'view' => 'article',
+					'catid' => 73,
+					'id' => 11,
+					'Itemid' => 272
+				),
+				array(
+					'option' => 'com_content',
+					'Itemid' => 272
+				),
+				array(
+					0 => 'park-site',
+					1 => 'photo-gallery',
+					2 => 'scenery',
+					3 => 'cradle-mountain'
+				),
+				'Error building a URL for an article with multiple levels to it\'s category menu item'
+			),
+			array(
+				array(
+					'option' => 'com_content',
+					'view' => 'category',
+					'id' => 19,
+					'Itemid' => 260
+				),
+				array(
+					'option' => 'com_content',
+					'Itemid' => 260
+				),
+				array(
+				),
+				'Error building a URL for category that has a menu item'
+			),
+			array(
+				array(
+					'option' => 'com_content',
+					'view' => 'form',
+					'Itemid' => 263
+				),
+				// TODO: I think this might be a bug? I think view should be unset whatever the status of the layout
+				array(
+					'option' => 'com_content',
+					'view' => 'form',
+					'Itemid' => 263
+				),
+				array(
+				),
+				'Error building a URL for a menu item that doesn\'t have a key'
+			),
+			array(
+				array(
+					'option' => 'com_content',
+					'id' => 19,
+					'Itemid' => 260
+				),
+				array(
+					'option' => 'com_content',
+					'id' => 19,
+					'Itemid' => 260
+				),
+				array(
+				),
+				'URL without a view specified cannot build'
+			),
+		);
+	}
+
+	/**
+	 * Tests the build() method
+	 *
+	 * @return  void
+	 *
+	 * @covers        JComponentRouterRulesStandard::build
+	 * @dataProvider  dataTestBuild
+	 * @since         __DEPLOY_VERSION__
+	 */
+	public function testBuild($query, $expectedQuery, $expectedSegments, $error)
+	{
+		$actualSegments = array();
+		$this->object->build($query, $actualSegments);
+		$this->assertEquals($expectedSegments, $actualSegments, $error);
+		$this->assertEquals($expectedQuery, $query, $error);
+	}
+
+	/**
+	 * Provides the data to test the build method.
+	 *
+	 * @return  array
+	 *
+	 * @since   __DEPLOY_VERSION__
+	 */
+	public function dataTestParse()
+	{
+		return array(
+			array(
+				array(
+					0 => 'beginners'
+				),
+				array(
+					'option' => 'com_content',
+					'view' => 'article',
+					'catid' => 19,
+					'id' => 8
+				),
+				260,
+				'Error parsing a URL for an article with a parent category menu item'
+			),
+			array(
+				array(
+					0 => 'park-site',
+					1 => 'photo-gallery',
+					2 => 'scenery',
+					3 => 'cradle-mountain'
+				),
+				array(
+					'option' => 'com_content',
+					'view' => 'article',
+					'catid' => 73,
+					'id' => 11
+				),
+				272,
+				'Error parsing a URL for an article with a parent category menu item'
+			),
+		);
+	}
+
+	/**
+	 * Tests the build() method
+	 *
+	 * @return  void
+	 *
+	 * @covers        JComponentRouterRulesStandard::parse
+	 * @dataProvider  dataTestParse
+	 * @since         __DEPLOY_VERSION__
+	 */
+	public function testParse($segments, $expectedVars, $activeMenu, $error)
+	{
+		$vars = array();
+
+		// Set the router ID effectively mimicking JComponentRouterRulesMenu
+		/** @var ContentRouterStandardRuleOnly $router */
+		$router = TestReflection::getValue($this->object, 'router');
+		$router->menu->setActive($activeMenu);
+		TestReflection::setValue($this->object, 'router', $router);
+
+		$this->object->parse($segments, $vars);
+		$this->assertEquals($expectedVars, $vars, $error . ' invalid vars');
+		$this->assertEquals(array(), $segments, $error . ' invalid segments');
+	}
+}
diff --git a/tests/unit/suites/libraries/cms/component/router/stubs/ComContentRouter.php b/tests/unit/suites/libraries/cms/component/router/stubs/ComContentRouter.php
new file mode 100644
index 0000000000000..06464a5e2ce04
--- /dev/null
+++ b/tests/unit/suites/libraries/cms/component/router/stubs/ComContentRouter.php
@@ -0,0 +1,216 @@
+<?php
+/**
+ * @package     Joomla.UnitTest
+ * @subpackage  Component
+ *
+ * @copyright   Copyright (C) 2005 - 2017 Open Source Matters, Inc. All rights reserved.
+ * @license     GNU General Public License version 2 or later; see LICENSE.txt
+ */
+
+defined('_JEXEC') or die;
+
+/**
+ * Routing class of com_content, with the following changes:
+ * 1. The no id's object is a constructor parameter
+ * 2. The router only has the JComponentRouterRulesStandard rule attached
+ *
+ * @since  __DEPLOY_VERSION__
+ */
+class ContentRouterStandardRuleOnly extends JComponentRouterView
+{
+	protected $noIDs = false;
+	protected $name = 'content';
+
+	/**
+	 * Content Component router constructor
+	 *
+	 * @param   JApplicationCms  $app    The application object
+	 * @param   JMenu            $menu   The menu object to work with
+	 * @param   boolean          $noIds  Should ID's be present in the URL or not?
+	 *
+	 * @since  __DEPLOY_VERSION__
+	 */
+	public function __construct($app = null, $menu = null, $noIds)
+	{
+		$this->noIDs = $noIds;
+		$categories = new JComponentRouterViewconfiguration('categories');
+		$categories->setKey('id');
+		$this->registerView($categories);
+		$category = new JComponentRouterViewconfiguration('category');
+		$category->setKey('id')->setParent($categories, 'catid')->setNestable()->addLayout('blog');
+		$this->registerView($category);
+		$article = new JComponentRouterViewconfiguration('article');
+		$article->setKey('id')->setParent($category, 'catid');
+		$this->registerView($article);
+		$this->registerView(new JComponentRouterViewconfiguration('archive'));
+		$this->registerView(new JComponentRouterViewconfiguration('featured'));
+		$this->registerView(new JComponentRouterViewconfiguration('form'));
+
+		parent::__construct($app, $menu);
+
+		$this->attachRule(new JComponentRouterRulesStandard($this));
+	}
+
+	/**
+	 * Method to get the segment(s) for a category
+	 *
+	 * @param   string  $id     ID of the category to retrieve the segments for
+	 * @param   array   $query  The request that is built right now
+	 *
+	 * @return  array|string  The segments of this item
+	 *
+	 * @since  __DEPLOY_VERSION__
+	 */
+	public function getCategorySegment($id, $query)
+	{
+		$category = JCategories::getInstance($this->getName())->get($id);
+
+		if ($category)
+		{
+			$path = array_reverse($category->getPath(), true);
+			$path[0] = '1:root';
+
+			if ($this->noIDs)
+			{
+				foreach ($path as &$segment)
+				{
+					list($id, $segment) = explode(':', $segment, 2);
+				}
+			}
+
+			return $path;
+		}
+
+		return array();
+	}
+
+	/**
+	 * Method to get the segment(s) for a category
+	 *
+	 * @param   string  $id     ID of the category to retrieve the segments for
+	 * @param   array   $query  The request that is built right now
+	 *
+	 * @return  array|string  The segments of this item
+	 *
+	 * @since  __DEPLOY_VERSION__
+	 */
+	public function getCategoriesSegment($id, $query)
+	{
+		return $this->getCategorySegment($id, $query);
+	}
+
+	/**
+	 * Method to get the segment(s) for an article
+	 *
+	 * @param   string  $id     ID of the article to retrieve the segments for
+	 * @param   array   $query  The request that is built right now
+	 *
+	 * @return  array|string  The segments of this item
+	 *
+	 * @since  __DEPLOY_VERSION__
+	 */
+	public function getArticleSegment($id, $query)
+	{
+		if (!strpos($id, ':'))
+		{
+			$db = JFactory::getDbo();
+			$dbquery = $db->getQuery(true);
+			$dbquery->select($dbquery->qn('alias'))
+				->from($dbquery->qn('#__content'))
+				->where('id = ' . $dbquery->q($id));
+			$db->setQuery($dbquery);
+
+			$id .= ':' . $db->loadResult();
+		}
+
+		if ($this->noIDs)
+		{
+			list($void, $segment) = explode(':', $id, 2);
+
+			return array($void => $segment);
+		}
+
+		return array((int) $id => $id);
+	}
+
+	/**
+	 * Method to get the id for a category
+	 *
+	 * @param   string  $segment  Segment to retrieve the ID for
+	 * @param   array   $query    The request that is parsed right now
+	 *
+	 * @return  mixed   The id of this item or false
+	 *
+	 * @since  __DEPLOY_VERSION__
+	 */
+	public function getCategoryId($segment, $query)
+	{
+		if (isset($query['id']))
+		{
+			$category = JCategories::getInstance($this->getName())->get($query['id']);
+
+			foreach ($category->getChildren() as $child)
+			{
+				if ($this->noIDs)
+				{
+					if ($child->alias == $segment)
+					{
+						return $child->id;
+					}
+				}
+				else
+				{
+					if ($child->id == (int) $segment)
+					{
+						return $child->id;
+					}
+				}
+			}
+		}
+
+		return false;
+	}
+
+	/**
+	 * Method to get the segment(s) for a category
+	 *
+	 * @param   string  $segment  Segment to retrieve the ID for
+	 * @param   array   $query    The request that is parsed right now
+	 *
+	 * @return  mixed   The id of this item or false
+	 *
+	 * @since  __DEPLOY_VERSION__
+	 */
+	public function getCategoriesId($segment, $query)
+	{
+		return $this->getCategoryId($segment, $query);
+	}
+
+	/**
+	 * Method to get the segment(s) for an article
+	 *
+	 * @param   string  $segment  Segment of the article to retrieve the ID for
+	 * @param   array   $query    The request that is parsed right now
+	 *
+	 * @return  mixed   The id of this item or false
+	 *
+	 * @since  __DEPLOY_VERSION__
+	 */
+	public function getArticleId($segment, $query)
+	{
+		if ($this->noIDs)
+		{
+			$db = JFactory::getDbo();
+			$dbquery = $db->getQuery(true);
+			$dbquery->select($dbquery->qn('id'))
+				->from($dbquery->qn('#__content'))
+				->where('alias = ' . $dbquery->q($segment))
+				->where('catid = ' . $dbquery->q($query['id']));
+			$db->setQuery($dbquery);
+
+			return (int) $db->loadResult();
+		}
+
+		return (int) $segment;
+	}
+}