diff --git a/.gitignore b/.gitignore
index 2b039a2a9d1b641bd89e98e34edbf3b6f26fb126..7a68906b8d675e2eede3f6536f35e120ef4d6323 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,6 @@
 /composer.lock
 /public
 /docs
+
+# IDE stuff
+.idea
diff --git a/composer.json b/composer.json
index 19401fb7642c5e5889385a1345597df58eaea4ca..337c98d85543b0aa04019864b2d2f645065091de 100644
--- a/composer.json
+++ b/composer.json
@@ -17,10 +17,19 @@
         "php": ">=7.2",
         "guzzlehttp/guzzle": "^6.0|^7.0"
     },
+    "require-dev": {
+        "phpunit/phpunit": "8.5.x-dev"
+    },
     "support": {
         "source": "https://gitlab.res-telae.cat/restelae/php-gbif"
     },
     "autoload": {
         "psr-4": {"ResTelae\\Gbif\\": "src"}
+    },
+    "autoload-dev": {
+        "psr-4": {"ResTelae\\Gbif\\Tests\\": "tests/"}
+    },
+    "scripts": {
+        "test": "vendor/bin/phpunit --colors tests/"
     }
 }
diff --git a/tests/Unit/SpeciesTest.php b/tests/Unit/SpeciesTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..07eab4963cea78584592b02dab46c4ad970ba7ea
--- /dev/null
+++ b/tests/Unit/SpeciesTest.php
@@ -0,0 +1,112 @@
+<?php declare(strict_types=1);
+
+namespace ResTelae\Gbif\Tests\Unit;
+
+use PHPUnit\Framework\TestCase;
+use ResTelae\Gbif\Species;
+
+final class SpeciesTest extends TestCase
+{
+    /** @var Species */
+    private $species;
+
+    protected function setUp(): void
+    {
+        parent::setUp();
+
+        $this->species = new Species();
+    }
+
+    public function testShouldThrowExceptionIfInvalidDataChoice()
+    {
+        $this->expectExceptionMessage('Illegal choice for `data`');
+        $this->species->nameUsage([], 'invalid');
+
+        $this->fail("No exception raised");
+    }
+
+    public function testShouldThrowExceptionIfNoDataKeyProvided()
+    {
+        $this->expectExceptionMessage('You must specify a key if `data` does not equal `all`');
+        $this->species->nameUsage([], 'name');
+
+        $this->fail("No exception raised");
+    }
+
+    public function testShouldThrowExceptionOnRootDataIfNoUuidOrShortNameProvided()
+    {
+        $this->expectExceptionMessage('`uuid` and `short_name` cannot be both NULL if `data` equals "root"');
+        $this->species->nameUsage([], 'root', '123');
+
+        $this->fail("No exception raised");
+    }
+
+    public function testShouldGetValidResponseWithValidArguments()
+    {
+        $response = $this->species->nameUsage([]);
+
+        $this->assertEquals(0, $response['offset']);
+        $this->assertEquals(100, $response['limit']);
+        $this->assertFalse($response['endOfRecords']);
+        $this->assertIsArray($response['results']);
+    }
+
+    public function testShouldGetValidResponseFromNameUsageByKey()
+    {
+        $response = $this->species->nameUsageByKey(5231190);
+
+        $this->assertEquals(5231190, $response['key']);
+        $this->assertEquals('Animalia', $response['kingdom']);
+        $this->assertEquals('Passer domesticus', $response['species']);
+    }
+
+    public function testShouldGetValidResponseFromNameBackbone()
+    {
+        $response = $this->species->nameBackbone(
+            [
+                'verbose' => true,
+                'kingdom' => 'Plantae',
+                'name'    => 'Oenante',
+            ]
+        );
+
+        $this->assertEquals(3034893, $response['usageKey']);
+        $this->assertEquals('Oenanthe', $response['canonicalName']);
+        $this->assertEquals('GENUS', $response['rank']);
+    }
+
+    public function testShouldGetValidResponseFromNameLookup()
+    {
+        $response = $this->species->nameLookup(
+            [
+                'q'      => 'Puma',
+                'rank'   => 'GENUS',
+                'offset' => 0,
+                'limit'  => 20,
+            ]
+        );
+
+        $this->assertEquals(0, $response['offset']);
+        $this->assertEquals(20, $response['limit']);
+        $this->assertIsArray($response['results']);
+        $this->assertEquals('Puma', $response['results'][0]['genus']);
+        $this->assertEquals('Puma', $response['results'][0]['canonicalName']);
+    }
+
+    public function testShouldGetValidResponseFromNameSuggest()
+    {
+        $response = $this->species->nameSuggest(
+            [
+                'q'      => 'Pum',
+                'rank'   => 'GENUS',
+                'offset' => 0,
+                'limit'  => 20,
+            ]
+        );
+
+        $this->assertIsArray($response);
+        $this->assertCount(20, $response);
+        $this->assertEquals('Pumililema', $response[0]['genus']);
+        $this->assertEquals('Pumililema', $response[0]['canonicalName']);
+    }
+}