diff --git a/uroboros/command.py b/uroboros/command.py
index 59d72b5..ce21155 100644
--- a/uroboros/command.py
+++ b/uroboros/command.py
@@ -1,15 +1,16 @@
 import abc
 import argparse
 import logging
+import sys
 from typing import TYPE_CHECKING
 
 from uroboros import errors
 from uroboros import utils
+from uroboros.constants import ExitStatus
 
 if TYPE_CHECKING:
     from typing import List, Dict, Optional, Union, Set
     from uroboros.option import Option
-    from uroboros.constants import ExitStatus
 
 
 class Command(metaclass=abc.ABCMeta):
@@ -37,17 +38,21 @@ class Command(metaclass=abc.ABCMeta):
         # This is enabled after initialization.
         self._parser = None  # type: Optional[argparse.ArgumentParser]
 
-    def execute(self, args: 'Optional[argparse.Namespace]' = None) -> int:
+    def execute(self, argv: 'List[str]' = None) -> int:
         """
         Execute `uroboros.command.Command.run` internally.
         And return exit code (integer).
-        :param args:    Parsed arguments. If None is given, try to parse
+        :param argv:    Arguments to parse. If None is given, try to parse
                         `sys.argv` by the parser of this command.
         :return:        Exit status (integer only)
         """
-        if args is None:
+        try:
+            self._check_initialized()
+        except errors.CommandNotRegisteredError:
             self.initialize()
-            args = self._parser.parse_args()
+        if argv is None:
+            argv = sys.argv
+        args = self._parser.parse_args(argv)
         # Get all nested validator
         exceptions = self.validate(args)
         layer = self._layer
@@ -86,18 +91,21 @@ class Command(metaclass=abc.ABCMeta):
         :return: None
         """
         if parser is None:
-            self._parser = argparse.ArgumentParser(
-                prog=self.name,
-                description=self.description,
-                parents=[o.get_parser() for o in self.options]
-            )
-            self._parser.set_defaults(func=self.run)
+            self._parser = self.create_default_parser()
         else:
             self._parser = parser
+        # Add validator
+        validator_name = utils.get_args_validator_name(self._layer)
+        self._parser.set_defaults(**{validator_name: self.validate})
+        # Add function to execute
+        self._parser.set_defaults(func=self.run)
         self.build_option(self._parser)
+        self.initialize_sub_parsers(self._parser)
+
+    def initialize_sub_parsers(self, parser: 'argparse.ArgumentParser'):
         if len(self.sub_commands) == 0:
             return
-        parser = self._parser.add_subparsers(
+        parser = parser.add_subparsers(
             dest=utils.get_args_command_name(self._layer),
             title="Sub commands",
         )
@@ -108,13 +116,17 @@ class Command(metaclass=abc.ABCMeta):
                 help=cmd.description,
                 parents=[o.get_parser() for o in cmd.options],
             )
-            # Add validator
-            validator_name = utils.get_args_validator_name(self._layer)
-            sub_parser.set_defaults(**{validator_name: cmd.validate})
-            # Add function to execute
-            sub_parser.set_defaults(func=cmd.run)
             cmd.initialize(sub_parser)
 
+    def create_default_parser(self):
+        parser = argparse.ArgumentParser(
+            prog=self.name,
+            description=self.description,
+            parents=[o.get_parser() for o in self.options]
+        )
+        parser.set_defaults(func=self.run)
+        return parser
+
     def add_command(self, command: 'Command') -> 'Command':
         """
         Add sub command to this command.
diff --git a/uroboros/utils.py b/uroboros/utils.py
index 3616354..6aa0249 100644
--- a/uroboros/utils.py
+++ b/uroboros/utils.py
@@ -1,24 +1,6 @@
-from typing import TYPE_CHECKING
-
-from uroboros import errors
-
-if TYPE_CHECKING:
-    from typing import Dict
-    from uroboros.command import Command
-    CommandDict = Dict[Command, dict]
-    CommandsDict = Dict[Command, CommandDict]
-
-
 def get_args_command_name(layer: int):
     return "__layer{layer}_command".format(layer=layer)
 
 
 def get_args_validator_name(layer: int):
     return "__layer{layer}_validator".format(layer=layer)
-
-
-def get_matched_command(name, command_dict: 'CommandsDict') -> 'CommandDict':
-    for cmd, sub_cmds in command_dict.items():
-        if cmd.name == name:
-            return {cmd: sub_cmds}
-    raise errors.NoCommandError(name)

Add unit test
diff --git a/tests/base.py b/tests/base.py
new file mode 100644
index 0000000..124cccb
--- /dev/null
+++ b/tests/base.py
@@ -0,0 +1,73 @@
+import uroboros
+
+
+class RootCommand(uroboros.Command):
+    name = 'root'
+    description = 'command of root'
+
+    error = Exception(name)
+    value = False
+
+    def build_option(self, parser):
+        parser.add_argument(
+            '--{}'.format(self.name), action='store_true',
+            default=self.value, help='root argument'
+        )
+        return parser
+
+    def run(self, args):
+        print(args.root)
+        return 0
+
+    def validate(self, args):
+        if args.root != self.value:
+            return [self.error]
+        return []
+
+
+class SecondCommand(uroboros.Command):
+    name = 'second'
+    description = 'command of second'
+
+    error = Exception(name)
+    value = 0
+
+    def build_option(self, parser):
+        parser.add_argument(
+            '--{}'.format(self.name), type=int,
+            default=self.value, help='second argument'
+        )
+        return parser
+
+    def run(self, args):
+        print(args.second)
+        return 0
+
+    def validate(self, args):
+        if args.second != self.value:
+            return [self.error]
+        return []
+
+
+class ThirdCommand(uroboros.Command):
+    name = 'third'
+    description = 'command of third'
+
+    error = Exception(name)
+    value = 'third'
+
+    def build_option(self, parser):
+        parser.add_argument(
+            '--{}'.format(self.name), type=str,
+            default=self.value, help='third argument'
+        )
+        return parser
+
+    def run(self, args):
+        print(args.third)
+        return 0
+
+    def validate(self, args):
+        if args.third != self.value:
+            return [self.error]
+        return []
diff --git a/tests/test_command.py b/tests/test_command.py
new file mode 100644
index 0000000..5c00ec6
--- /dev/null
+++ b/tests/test_command.py
@@ -0,0 +1,219 @@
+import argparse
+import logging
+from unittest import mock
+
+import pytest
+
+from uroboros.errors import CommandDuplicateError
+from .base import RootCommand, SecondCommand, ThirdCommand
+
+
+@pytest.fixture
+def commands():
+    return RootCommand(), SecondCommand(), ThirdCommand()
+
+
+def get_sub_commands(cmd_set):
+    if len(cmd_set) == 0:
+        return []
+    cmds = []
+    for c, sub_set in cmd_set.items():
+        cmds.append(c)
+        c.sub_commands = get_sub_commands(sub_set)
+    return cmds
+
+
+class TestCommand(object):
+
+    @pytest.mark.parametrize(
+        'command', [
+            RootCommand(),
+            SecondCommand(),
+            ThirdCommand()
+        ]
+    )
+    def test_build_option(self, command):
+        parser = argparse.ArgumentParser()
+        parser = command.build_option(parser)
+        expected = command.value
+        actual = parser.get_default(command.name)
+        assert expected == actual
+
+    @pytest.mark.parametrize(
+        'command', [
+            RootCommand(),
+            SecondCommand(),
+            ThirdCommand()
+        ]
+    )
+    def test_validate(self, command):
+        valid_args = mock.MagicMock(
+            **{command.name: command.value}
+        )
+        expected = []
+        actual = command.validate(valid_args)
+        assert expected == actual
+        invalid_args = mock.MagicMock(
+            **{command.name: None}
+        )
+        expected = [command.error]
+        actual = command.validate(invalid_args)
+        assert expected == actual
+
+    @pytest.mark.parametrize(
+        'command_set', [
+            {RootCommand(): {SecondCommand(): {ThirdCommand(): {}}}},
+            {RootCommand(): {SecondCommand(): {}, ThirdCommand(): {}}},
+            {RootCommand(): {}, SecondCommand(): {}, ThirdCommand(): {}}
+        ]
+    )
+    def test_increment_nest(self, command_set):
+        result_set = {}
+
+        def count_nest(cmd_set, count):
+            count += 1
+            for c, sub_set in cmd_set.items():
+                result_set[c] = count
+                count_nest(sub_set, count)
+
+        for root, sub_commands in command_set.items():
+            root.sub_commands = get_sub_commands(sub_commands)
+            root.increment_nest(-1)
+        layer_num = -1
+        count_nest(command_set, layer_num)
+        for cmd, expected in result_set.items():
+            actual = cmd._layer
+            assert expected == actual
+
+    @pytest.mark.parametrize(
+        'command_set', [
+            {RootCommand(): {SecondCommand(): {ThirdCommand(): {}}}},
+            {RootCommand(): {SecondCommand(): {}, ThirdCommand(): {}}},
+            {RootCommand(): {}, SecondCommand(): {}, ThirdCommand(): {}}
+        ]
+    )
+    def test_register_parent(self, command_set):
+        result_set = {}
+
+        def collect_ids(parent_ids, cmd_set):
+            for c, sub_set in cmd_set.items():
+                current_set = parent_ids | {id(c)}
+                result_set[c] = current_set
+                collect_ids(current_set, sub_set)
+
+        for root, sub_commands in command_set.items():
+            root.sub_commands = get_sub_commands(sub_commands)
+            root.register_parent(set())
+        collect_ids(set(), command_set)
+
+        for cmd, expected in result_set.items():
+            actual = cmd._parent_ids
+            assert expected == actual
+
+    @pytest.mark.parametrize(
+        'command_set', [
+            {RootCommand(): {SecondCommand(): {ThirdCommand(): {}}}},
+            {RootCommand(): {SecondCommand(): {}, ThirdCommand(): {}}},
+            {RootCommand(): {}, SecondCommand(): {}, ThirdCommand(): {}}
+        ]
+    )
+    def test_add_command(self, command_set):
+        result_set = {}
+
+        def add_command(cmd, cmd_set):
+            result_set[cmd] = set()
+            for c, sub_set in cmd_set.items():
+                result_set[cmd].add(id(c))
+                cmd.add_command(c)
+                add_command(c, sub_set)
+
+        for root, sub_commands in command_set.items():
+            add_command(root, sub_commands)
+
+        for command, expected in result_set.items():
+            actual = command._sub_command_ids
+            assert actual == expected
+
+    @pytest.mark.parametrize(
+        'command_set', [
+            {RootCommand(): {SecondCommand(): {ThirdCommand(): {}}}},
+            {RootCommand(): {SecondCommand(): {}, ThirdCommand(): {}}},
+        ]
+    )
+    def test_add_duplicate_command(self, command_set):
+        result_set = {}
+
+        def add_command(parent_cmds, cmd, cmd_set):
+            result_set[cmd] = parent_cmds + [cmd]
+            for c, sub_set in cmd_set.items():
+                cmd.add_command(c)
+                add_command(result_set[cmd], c, sub_set)
+
+        for root, sub_commands in command_set.items():
+            add_command([], root, sub_commands)
+
+        for command, parents in result_set.items():
+            for parent in parents:
+                with pytest.raises(CommandDuplicateError):
+                    command.add_command(parent)
+
+    @pytest.mark.parametrize(
+        'command_set,argv,expected', [
+            ({RootCommand(): {SecondCommand(): {ThirdCommand(): {}}}},
+             "root second third", ThirdCommand.value),
+            ({RootCommand(): {SecondCommand(): {ThirdCommand(): {}}}},
+             "root second", SecondCommand.value),
+            ({RootCommand(): {SecondCommand(): {ThirdCommand(): {}}}},
+             "root", RootCommand.value),
+            ({RootCommand(): {SecondCommand(): {}, ThirdCommand(): {}}},
+             "root second", SecondCommand.value),
+            ({RootCommand(): {SecondCommand(): {}, ThirdCommand(): {}}},
+             "root third", ThirdCommand.value),
+            ({RootCommand(): {SecondCommand(): {}, ThirdCommand(): {}}},
+             "root", RootCommand.value),
+        ]
+    )
+    def test_execute(self, command_set, argv, expected, capsys):
+
+        def add_command(cmd, cmd_set):
+            for c, sub_set in cmd_set.items():
+                cmd.add_command(c)
+                add_command(c, sub_set)
+
+        for root, sub_commands in command_set.items():
+            add_command(root, sub_commands)
+            root.execute(argv.split(' ')[1:])
+            stdout = capsys.readouterr()
+            actual = stdout.out.splitlines()[0]
+            assert actual == str(expected)
+
+    @pytest.mark.parametrize(
+        'command_set,argv,expected', [
+            ({RootCommand(): {SecondCommand(): {ThirdCommand(): {}}}},
+             "root second third --third invalid", "third"),
+            ({RootCommand(): {SecondCommand(): {ThirdCommand(): {}}}},
+             "root second --second 10", "second"),
+            ({RootCommand(): {SecondCommand(): {ThirdCommand(): {}}}},
+             "root --root", "root"),
+            ({RootCommand(): {SecondCommand(): {}, ThirdCommand(): {}}},
+             "root second --second 10", "second"),
+            ({RootCommand(): {SecondCommand(): {}, ThirdCommand(): {}}},
+             "root third --third invalid", "third"),
+            ({RootCommand(): {SecondCommand(): {}, ThirdCommand(): {}}},
+             "root --root", "root"),
+        ]
+    )
+    def test_violates_validation_argv(
+            self, command_set, argv, expected, caplog):
+
+        def add_command(cmd, cmd_set):
+            for c, sub_set in cmd_set.items():
+                cmd.add_command(c)
+                add_command(c, sub_set)
+
+        for root, sub_commands in command_set.items():
+            add_command(root, sub_commands)
+            root.execute(argv.split(' ')[1:])
+            out = caplog.record_tuples[0]
+            actual = out[1:]
+            assert actual == (logging.ERROR, expected)

pip install -e .
parse_log_pytest
pytest
pytest
3.7
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs @ file:///croot/attrs_1668696182826/work
certifi @ file:///croot/certifi_1671487769961/work/certifi
flit_core @ file:///opt/conda/conda-bld/flit-core_1644941570762/work/source/flit_core
importlib-metadata @ file:///tmp/build/80754af9/importlib-metadata_1648562407465/work
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
packaging @ file:///croot/packaging_1671697413597/work
pluggy @ file:///tmp/build/80754af9/pluggy_1648042572264/work
py @ file:///opt/conda/conda-bld/py_1644396412707/work
pytest==7.1.2
tomli @ file:///opt/conda/conda-bld/tomli_1657175507142/work
typing_extensions @ file:///croot/typing_extensions_1669924550328/work
-e git+https://github.com/pddg/uroboros.git@81ff57b387f2b2571eb5a4b569867c4cd2cd59a3#egg=uroboros
zipp @ file:///croot/zipp_1672387121353/work

name: uroboros
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - attrs=22.1.0=py37h06a4308_0
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - flit-core=3.6.0=pyhd3eb1b0_0
  - importlib-metadata=4.11.3=py37h06a4308_0
  - importlib_metadata=4.11.3=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - packaging=22.0=py37h06a4308_0
  - pip=22.3.1=py37h06a4308_0
  - pluggy=1.0.0=py37h06a4308_1
  - py=1.11.0=pyhd3eb1b0_0
  - pytest=7.1.2=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tomli=2.0.1=py37h06a4308_0
  - typing_extensions=4.4.0=py37h06a4308_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zipp=3.11.0=py37h06a4308_0
  - zlib=1.2.13=h5eee18b_1
prefix: /opt/conda/envs/uroboros

tests/test_command.py::TestCommand::test_execute[command_set0-root tests/test_command.py::TestCommand::test_execute[command_set1-root tests/test_command.py::TestCommand::test_execute[command_set2-root-False] tests/test_command.py::TestCommand::test_execute[command_set3-root tests/test_command.py::TestCommand::test_execute[command_set4-root tests/test_command.py::TestCommand::test_execute[command_set5-root-False] tests/test_command.py::TestCommand::test_violates_validation_argv[command_set0-root tests/test_command.py::TestCommand::test_violates_validation_argv[command_set1-root tests/test_command.py::TestCommand::test_violates_validation_argv[command_set2-root tests/test_command.py::TestCommand::test_violates_validation_argv[command_set3-root tests/test_command.py::TestCommand::test_violates_validation_argv[command_set4-root tests/test_command.py::TestCommand::test_violates_validation_argv[command_set5-root
tests/test_command.py::TestCommand::test_build_option[command0] tests/test_command.py::TestCommand::test_build_option[command1] tests/test_command.py::TestCommand::test_build_option[command2] tests/test_command.py::TestCommand::test_validate[command0] tests/test_command.py::TestCommand::test_validate[command1] tests/test_command.py::TestCommand::test_validate[command2] tests/test_command.py::TestCommand::test_increment_nest[command_set0] tests/test_command.py::TestCommand::test_increment_nest[command_set1] tests/test_command.py::TestCommand::test_increment_nest[command_set2] tests/test_command.py::TestCommand::test_register_parent[command_set0] tests/test_command.py::TestCommand::test_register_parent[command_set1] tests/test_command.py::TestCommand::test_register_parent[command_set2] tests/test_command.py::TestCommand::test_add_command[command_set0] tests/test_command.py::TestCommand::test_add_command[command_set1] tests/test_command.py::TestCommand::test_add_command[command_set2] tests/test_command.py::TestCommand::test_add_duplicate_command[command_set0] tests/test_command.py::TestCommand::test_add_duplicate_command[command_set1]
uroboros/utils.py uroboros/command.py
uroboros/utils.py uroboros/command.py