Repository: SpiralMoon/maplestory.openapi (language: python)

Issue:
Feature/csharp test



Discussion / hints:

Fix patch:
diff --git a/csharp/MapleStory.OpenAPI.sln b/csharp/MapleStory.OpenAPI.sln
index f46817a..44d320b 100644
--- a/csharp/MapleStory.OpenAPI.sln
+++ b/csharp/MapleStory.OpenAPI.sln
@@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MapleStory.OpenAPISample",
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MapleStory.OpenAPI", "MapleStory.OpenAPI\MapleStory.OpenAPI.csproj", "{E1C26621-0875-466C-AEAD-F550FEFA5509}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MapleStory.Test", "MapleStory.Test\MapleStory.Test.csproj", "{7C30BDB0-B51E-474F-A19C-2E84D3980996}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -21,6 +23,10 @@ Global
 		{E1C26621-0875-466C-AEAD-F550FEFA5509}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{E1C26621-0875-466C-AEAD-F550FEFA5509}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{E1C26621-0875-466C-AEAD-F550FEFA5509}.Release|Any CPU.Build.0 = Release|Any CPU
+		{7C30BDB0-B51E-474F-A19C-2E84D3980996}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{7C30BDB0-B51E-474F-A19C-2E84D3980996}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{7C30BDB0-B51E-474F-A19C-2E84D3980996}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{7C30BDB0-B51E-474F-A19C-2E84D3980996}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
diff --git a/csharp/MapleStory.OpenAPI/Src/Dto/Character/CharacterSetEffectDTO.cs b/csharp/MapleStory.OpenAPI/Src/Dto/Character/CharacterSetEffectDTO.cs
index eff5d49..2cfe495 100644
--- a/csharp/MapleStory.OpenAPI/Src/Dto/Character/CharacterSetEffectDTO.cs
+++ b/csharp/MapleStory.OpenAPI/Src/Dto/Character/CharacterSetEffectDTO.cs
@@ -29,6 +29,6 @@ public DateTimeOffset? Date
         /// 세트 효과 정보 목록
         /// </summary>
         [JsonProperty("set_effect")]
-        public List<CharacterSetEffectInfoDTO> SetEffect { get; set; }
+        public List<CharacterSetEffectSetDTO> SetEffect { get; set; }
     }
 }
diff --git a/csharp/MapleStory.OpenAPI/Src/Dto/Character/CharacterSetEffectSetDTO.cs b/csharp/MapleStory.OpenAPI/Src/Dto/Character/CharacterSetEffectSetDTO.cs
index 1ced8b5..891a158 100644
--- a/csharp/MapleStory.OpenAPI/Src/Dto/Character/CharacterSetEffectSetDTO.cs
+++ b/csharp/MapleStory.OpenAPI/Src/Dto/Character/CharacterSetEffectSetDTO.cs
@@ -5,7 +5,7 @@ namespace MapleStory.OpenAPI.Dto
     /// <summary>
     /// 캐릭터 세트 효과 정보
     /// </summary>
-    public class CharacterSetEffectInfoDTO
+    public class CharacterSetEffectSetDTO
     {
         /// <summary>
         /// 세트 효과 명
diff --git a/csharp/MapleStory.OpenAPI/Src/MapleStoryAPI.cs b/csharp/MapleStory.OpenAPI/Src/MapleStoryAPI.cs
index 052c5a2..4fedc48 100644
--- a/csharp/MapleStory.OpenAPI/Src/MapleStoryAPI.cs
+++ b/csharp/MapleStory.OpenAPI/Src/MapleStoryAPI.cs
@@ -402,9 +402,9 @@ public async Task<CharacterSymbolEquipmentDTO> GetCharacterSymbolEquipment(strin
         /// <para>- 게임 콘텐츠 변경으로 ocid가 변경될 수 있습니다. ocid 기반 서비스 갱신 시 유의해 주시길 바랍니다.</para>
         /// </summary>
         /// <param name="ocid">캐릭터 식별자</param>
-        public Task<CharacterSetEffectDTO> GetCharacterSetEffectAsync(string ocid)
+        public Task<CharacterSetEffectDTO> GetCharacterSetEffect(string ocid)
         {
-            return GetCharacterSetEffectAsync(ocid, null);
+            return GetCharacterSetEffect(ocid, null);
         }
 
         /// <summary>
@@ -416,7 +416,7 @@ public Task<CharacterSetEffectDTO> GetCharacterSetEffectAsync(string ocid)
         /// </summary>
         /// <param name="ocid">캐릭터 식별자</param>
         /// <param name="dateTimeOffset">조회 기준일 (KST)</param>
-        public async Task<CharacterSetEffectDTO> GetCharacterSetEffectAsync(string ocid, DateTimeOffset? dateTimeOffset)
+        public async Task<CharacterSetEffectDTO> GetCharacterSetEffect(string ocid, DateTimeOffset? dateTimeOffset)
         {
             var path = "maplestory/v1/character/set-effect";
             var date = dateTimeOffset != null
@@ -1432,7 +1432,7 @@ public async Task<OverallRankingResponseDTO> GetOverallRanking(string? worldName
             var query = new Dictionary<string, string?>()
             {
                 { "date", ToDateString(MinDate(2023, 12, 22), dateTimeOffset) },
-                { "ocid", ocid },
+                { "world_name", worldName },
                 { "world_type", worldType?.ToString() },
                 { "class", characterClass },
                 { "ocid", ocid },
diff --git a/csharp/MapleStory.Test/CharacterAPI.cs b/csharp/MapleStory.Test/CharacterAPI.cs
new file mode 100644
index 0000000..f1061c3
--- /dev/null
+++ b/csharp/MapleStory.Test/CharacterAPI.cs
@@ -0,0 +1,162 @@
+﻿using NUnit.Framework;
+using MapleStory.OpenAPI;
+
+namespace MapleStory.Test
+{
+    [TestFixture]
+    public class CharacterAPI
+    {
+        private static readonly string apiKey = Environment.GetEnvironmentVariable("API_KEY")!; // Your API Key
+        private static readonly MapleStoryAPI api = new MapleStoryAPI(apiKey);
+        private static readonly string ocid = "e0a4f439e53c369866b55297d2f5f4eb";
+
+        [Test]
+        public async Task GetCharacter()
+        {
+            var nickname = "아델";
+            var response = await api.GetCharacter(nickname);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetCharacterList()
+        {
+            var response = await api.GetCharacterList();
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetCharacterBasic()
+        {
+            var response = await api.GetCharacterBasic(ocid);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetCharacterPopularity()
+        {
+            var response = await api.GetCharacterPopularity(ocid);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetCharacterStat()
+        {
+            var response = await api.GetCharacterStat(ocid);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetCharacterHyperStat()
+        {
+            var response = await api.GetCharacterHyperStat(ocid);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetCharacterPropensity()
+        {
+            var response = await api.GetCharacterPropensity(ocid);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetCharacterAbility()
+        {
+            var response = await api.GetCharacterAbility(ocid);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetCharacterItemEquipment()
+        {
+            var response = await api.GetCharacterItemEquipment(ocid);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetCharacterCashItemEquipment()
+        {
+            var response = await api.GetCharacterCashItemEquipment(ocid);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetCharacterSymbolEquipment()
+        {
+            var response = await api.GetCharacterSymbolEquipment(ocid);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetCharacterSetEffect()
+        {
+            var response = await api.GetCharacterSetEffect(ocid);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetCharacterBeautyEquipment()
+        {
+            var response = await api.GetCharacterBeautyEquipment(ocid);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetCharacterAndroidEquipment()
+        {
+            var response = await api.GetCharacterAndroidEquipment(ocid);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetCharacterPetEquipment()
+        {
+            var response = await api.GetCharacterPetEquipment(ocid);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetCharacterSkill()
+        {
+            var skillGrade = "0";
+            var response = await api.GetCharacterSkill(ocid, skillGrade);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetCharacterLinkSkill()
+        {
+            var response = await api.GetCharacterLinkSkill(ocid);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetCharacterVMatrix()
+        {
+            var response = await api.GetCharacterVMatrix(ocid);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetCharacterHexaMatrix()
+        {
+            var response = await api.GetCharacterHexaMatrix(ocid);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetCharacterHexaMatrixStat()
+        {
+            var response = await api.GetCharacterHexaMatrixStat(ocid);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetCharacterDojang()
+        {
+            var response = await api.GetCharacterDojang(ocid);
+            Console.WriteLine(response.ToJson());
+        }
+    }
+}
diff --git a/csharp/MapleStory.Test/GuildAPI.cs b/csharp/MapleStory.Test/GuildAPI.cs
new file mode 100644
index 0000000..7a156b5
--- /dev/null
+++ b/csharp/MapleStory.Test/GuildAPI.cs
@@ -0,0 +1,29 @@
+﻿using NUnit.Framework;
+using MapleStory.OpenAPI;
+
+namespace MapleStory.Test
+{
+    [TestFixture]
+    public class GuildAPI
+    {
+        private static readonly string apiKey = Environment.GetEnvironmentVariable("API_KEY")!; // Your API Key
+        private static readonly MapleStoryAPI api = new MapleStoryAPI(apiKey);
+        private static readonly string ogid = "f5992063d46d6f8718e514fca5428960";
+
+        [Test]
+        public async Task GetGuild()
+        {
+            var guildName = "붕붕";
+            var worldName = "크로아";
+            var response = await api.GetGuild(guildName, worldName);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetGuildBasic()
+        {
+            var response = await api.GetGuildBasic(ogid);
+            Console.WriteLine(response.ToJson());
+        }
+    }
+}
diff --git a/csharp/MapleStory.Test/HistoryAPI.cs b/csharp/MapleStory.Test/HistoryAPI.cs
new file mode 100644
index 0000000..887a6d2
--- /dev/null
+++ b/csharp/MapleStory.Test/HistoryAPI.cs
@@ -0,0 +1,36 @@
+﻿using NUnit.Framework;
+using MapleStory.OpenAPI;
+
+namespace MapleStory.Test
+{
+    [TestFixture]
+    public class HistoryAPI
+    {
+        private static readonly string apiKey = Environment.GetEnvironmentVariable("API_KEY")!; // Your API Key
+        private static readonly MapleStoryAPI api = new MapleStoryAPI(apiKey);
+
+        [Test]
+        public async Task GetStarforceHistory()
+        {
+            int count = 1000;
+            var response = await api.GetStarforceHistory(count);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetPotentialHistory()
+        {
+            int count = 1000;
+            var response = await api.GetPotentialHistory(count);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetCubeHistory()
+        {
+            int count = 1000;
+            var response = await api.GetCubeHistory(count);
+            Console.WriteLine(response.ToJson());
+        }
+    }
+}
diff --git a/csharp/MapleStory.Test/MapleStory.Test.csproj b/csharp/MapleStory.Test/MapleStory.Test.csproj
new file mode 100644
index 0000000..66935b3
--- /dev/null
+++ b/csharp/MapleStory.Test/MapleStory.Test.csproj
@@ -0,0 +1,24 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net6.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+
+    <IsPackable>false</IsPackable>
+    <IsTestProject>true</IsTestProject>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
+    <PackageReference Include="NUnit" Version="3.13.3" />
+    <PackageReference Include="NUnit3TestAdapter" Version="4.4.2" />
+    <PackageReference Include="NUnit.Analyzers" Version="3.6.1" />
+    <PackageReference Include="coverlet.collector" Version="3.2.0" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\MapleStory.OpenAPI\MapleStory.OpenAPI.csproj" />
+  </ItemGroup>
+
+</Project>
diff --git a/csharp/MapleStory.Test/RankingAPI.cs b/csharp/MapleStory.Test/RankingAPI.cs
new file mode 100644
index 0000000..ee764f0
--- /dev/null
+++ b/csharp/MapleStory.Test/RankingAPI.cs
@@ -0,0 +1,54 @@
+﻿using NUnit.Framework;
+using MapleStory.OpenAPI;
+
+namespace MapleStory.Test
+{
+    [TestFixture]
+    public class RankingAPI
+    {
+        private static readonly string apiKey = Environment.GetEnvironmentVariable("API_KEY")!; // Your API Key
+        private static readonly MapleStoryAPI api = new MapleStoryAPI(apiKey);
+
+        [Test]
+        public async Task GetOverallRanking()
+        {
+            var response = await api.GetOverallRanking(null, 0, null, null, null);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetUnionRanking()
+        {
+            var response = await api.GetUnionRanking(null, null, null);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetGuildRanking()
+        {
+            var response = await api.GetGuildRanking(null, 0, null, null);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetDojangRanking()
+        {
+            var response = await api.GetDojangRanking(null, 0, null, null, null);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetSeedRanking()
+        {
+            var response = await api.GetTheSeedRanking(null, null, null);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetAchievementRanking()
+        {
+            var response = await api.GetAchievementRanking(null, null);
+            Console.WriteLine(response.ToJson());
+        }
+    }
+}
diff --git a/csharp/MapleStory.Test/UnionAPI.cs b/csharp/MapleStory.Test/UnionAPI.cs
new file mode 100644
index 0000000..622e8d2
--- /dev/null
+++ b/csharp/MapleStory.Test/UnionAPI.cs
@@ -0,0 +1,34 @@
+﻿using NUnit.Framework;
+using MapleStory.OpenAPI;
+
+namespace MapleStory.Test
+{
+    [TestFixture]
+    public class UnionAPI
+    {
+        private static readonly string apiKey = Environment.GetEnvironmentVariable("API_KEY")!; // Your API Key
+        private static readonly MapleStoryAPI api = new MapleStoryAPI(apiKey);
+        private static readonly string ocid = "e0a4f439e53c369866b55297d2f5f4eb";
+
+        [Test]
+        public async Task GetUnion()
+        {
+            var response = await api.GetUnion(ocid);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetUnionRaider()
+        {
+            var response = await api.GetUnionRaider(ocid);
+            Console.WriteLine(response.ToJson());
+        }
+
+        [Test]
+        public async Task GetUnionArtifact()
+        {
+            var response = await api.GetUnionArtifact(ocid);
+            Console.WriteLine(response.ToJson());
+        }
+    }
+}
diff --git a/csharp/MapleStory.Test/Utils.cs b/csharp/MapleStory.Test/Utils.cs
new file mode 100644
index 0000000..2904626
--- /dev/null
+++ b/csharp/MapleStory.Test/Utils.cs
@@ -0,0 +1,12 @@
+﻿using System.Text.Json;
+
+namespace MapleStory.Test
+{
+    public static class Utils
+    {
+        public static string ToJson(this object obj)
+        {
+            return JsonSerializer.Serialize(obj, new JsonSerializerOptions { WriteIndented = true });
+        }
+    }
+}
diff --git a/java/build.gradle b/java/build.gradle
index 45f985f..c1d7d32 100644
--- a/java/build.gradle
+++ b/java/build.gradle
@@ -25,7 +25,10 @@ dependencies {
     annotationProcessor 'org.projectlombok:lombok:1.18.30'
 }
 
-tasks.test {
+test {
+    def apiKey = project.findProperty('apiKey')
+    systemProperty('API_KEY', apiKey)
+
     useJUnitPlatform()
 }
 
diff --git a/java/src/main/java/dev/spiralmoon/maplestory/api/MapleStoryApi.java b/java/src/main/java/dev/spiralmoon/maplestory/api/MapleStoryApi.java
index 878be5d..158110c 100644
--- a/java/src/main/java/dev/spiralmoon/maplestory/api/MapleStoryApi.java
+++ b/java/src/main/java/dev/spiralmoon/maplestory/api/MapleStoryApi.java
@@ -815,8 +815,8 @@ public void getCharacterSymbolEquipmentAsync(@NonNull String ocid, LocalDateTime
      *
      * @param ocid 캐릭터 식별자
      */
-    public CharacterSetEffectDTO getCharacterSetEffectAsync(@NonNull String ocid) throws IOException {
-        return this.getCharacterSetEffectAsync(ocid, null);
+    public CharacterSetEffectDTO getCharacterSetEffect(@NonNull String ocid) throws IOException {
+        return this.getCharacterSetEffect(ocid, null);
     }
 
     /**
@@ -828,7 +828,7 @@ public CharacterSetEffectDTO getCharacterSetEffectAsync(@NonNull String ocid) th
      *
      * @param ocid 캐릭터 식별자
      */
-    public CharacterSetEffectDTO getCharacterSetEffectAsync(@NonNull String ocid, LocalDateTime localDateTime) throws IOException {
+    public CharacterSetEffectDTO getCharacterSetEffect(@NonNull String ocid, LocalDateTime localDateTime) throws IOException {
 
         final String date = localDateTime != null
                 ? toDateString(minDate(2023, 12, 21), localDateTime)
@@ -904,7 +904,7 @@ public CharacterBeautyEquipmentDTO getCharacterBeautyEquipment(@NonNull String o
      * @param ocid          캐릭터 식별자
      * @param localDateTime 조회 기준일 (KST)
      */
-    public CharacterBeautyEquipmentDTO getCharacterBeautyEquipment(@NonNull String ocid, @NonNull LocalDateTime localDateTime) throws IOException {
+    public CharacterBeautyEquipmentDTO getCharacterBeautyEquipment(@NonNull String ocid, LocalDateTime localDateTime) throws IOException {
 
         final String date = localDateTime != null
                 ? toDateString(minDate(2023, 12, 21), localDateTime)
diff --git a/js/jest.config.js b/js/jest.config.js
new file mode 100644
index 0000000..5bf040e
--- /dev/null
+++ b/js/jest.config.js
@@ -0,0 +1,13 @@
+const { pathsToModuleNameMapper } = require('ts-jest');
+const { compilerOptions } = require('./tsconfig.paths.json');
+
+module.exports = {
+  testMatch: [
+    '**/?(*.)+(spec|test).[jt]s?(x)'
+  ],
+  transform: {
+    '^.+\\.tsx?$': 'ts-jest'
+  },
+  moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
+  moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { prefix: '<rootDir>/' }),
+};
diff --git a/js/package.json b/js/package.json
index 11ae52f..6e4c433 100644
--- a/js/package.json
+++ b/js/package.json
@@ -42,7 +42,7 @@
     "lint": "eslint ./src/** --fix",
     "format": "prettier --write ./**/*.{ts,js}",
     "prepublishOnly": "npm run build",
-    "test": "uvu -r tsm test",
+    "test": "jest",
     "ts_sample": "ts-node -r tsconfig-paths/register sample/ts_sample.ts",
     "types": "tsc --noEmit"
   },
@@ -54,6 +54,7 @@
   "devDependencies": {
     "@rollup/plugin-commonjs": "^21.1.0",
     "@rollup/plugin-node-resolve": "13.1.3",
+    "@types/jest": "^29.5.12",
     "@types/xml2js": "^0.4.14",
     "@typescript-eslint/eslint-plugin": "^6.15.0",
     "@typescript-eslint/parser": "^6.15.0",
@@ -67,11 +68,12 @@
     "rollup": "2.66.1",
     "rollup-plugin-terser": "7.0.2",
     "rollup-plugin-typescript2": "0.27.1",
+    "jest": "^29.7.0",
     "tsconfig-paths": "^4.2.0",
+    "ts-jest": "^29.1.4",
     "ts-node": "^10.9.1",
     "tsm": "2.2.1",
-    "typescript": "^5.2.2",
-    "uvu": "0.5.3"
+    "typescript": "^5.2.2"
   },
   "engines": {
     "node": ">=12"
diff --git a/js/tsconfig.json b/js/tsconfig.json
index 126e17a..fdd3f72 100644
--- a/js/tsconfig.json
+++ b/js/tsconfig.json
@@ -12,7 +12,7 @@
     "allowSyntheticDefaultImports": true,
     "esModuleInterop": true
   },
-  "include": ["@types/**/*", "src/**/*"],
+  "include": ["@types/**/*", "src/**/*", "test/**/*"],
   "exclude": ["node_modules"],
   "extends": "./tsconfig.paths.json",
   "ts-node": {
@@ -21,6 +21,6 @@
       "module": "commonjs",
       "sourceMap": true
     },
-    "include": ["test/**/*", "sample/**/*"]
+    "include": ["sample/**/*"]
   }
 }
diff --git a/python/maplestory_openapi/api/dto/character/character_android_equipment.py b/python/maplestory_openapi/api/dto/character/character_android_equipment.py
index f9cf4e9..13de45c 100644
--- a/python/maplestory_openapi/api/dto/character/character_android_equipment.py
+++ b/python/maplestory_openapi/api/dto/character/character_android_equipment.py
@@ -52,13 +52,14 @@ class CharacterAndroidEquipmentPreset(BaseModel):
     android_description: str
     android_gender: str | None
     android_grade: str
-    android_skin_name: str
-    android_hair: CharacterAndroidEquipmentHairDtoBody
-    android_face: CharacterAndroidEquipmentFaceDtoBody
+    android_skin_name: str | None
+    android_hair: CharacterAndroidEquipmentHair
+    android_face: CharacterAndroidEquipmentFace
     android_ear_sensor_clip_flag: str
     android_non_humanoid_flag: str
     android_shop_usable_flag: str
 
+
 class CharacterAndroidCashItemEquipmentColoringPrism(BaseModel):
     """안드로이드 캐시아이템 컬러링프리즘 정보
 
diff --git a/python/maplestory_openapi/api/dto/character/character_dojang.py b/python/maplestory_openapi/api/dto/character/character_dojang.py
index 89e877e..73c851a 100644
--- a/python/maplestory_openapi/api/dto/character/character_dojang.py
+++ b/python/maplestory_openapi/api/dto/character/character_dojang.py
@@ -16,5 +16,5 @@ class CharacterDojang(BaseModel):
     character_class: str
     world_name: str
     dojang_best_floor: int
-    date_dojang_record: datetime
+    date_dojang_record: datetime | None
     dojang_best_time: int
diff --git a/python/maplestory_openapi/api/dto/character/character_link_skill.py b/python/maplestory_openapi/api/dto/character/character_link_skill.py
index 8125f8b..09c9483 100644
--- a/python/maplestory_openapi/api/dto/character/character_link_skill.py
+++ b/python/maplestory_openapi/api/dto/character/character_link_skill.py
@@ -16,7 +16,7 @@ class CharacterLinkSkillInfo(BaseModel):
     skill_description: str
     skill_level: int
     skill_effect: str
-    skill_effect_next: str | None
+    skill_effect_next: str | None = None
     skill_icon: str
 
 
diff --git a/python/maplestory_openapi/api/dto/union/union_raider.py b/python/maplestory_openapi/api/dto/union/union_raider.py
index 0aa7e97..3efd013 100644
--- a/python/maplestory_openapi/api/dto/union/union_raider.py
+++ b/python/maplestory_openapi/api/dto/union/union_raider.py
@@ -88,7 +88,7 @@ class UnionRaider(BaseModel):
     union_occupied_stat: list[str]
     union_inner_stat: list[UnionRaiderInnerStat]
     union_block: list[UnionRaiderBlock]
-    use_preset_not: int
+    use_preset_no: int
     union_raider_preset_1: UnionRaiderPreset | None
     union_raider_preset_2: UnionRaiderPreset | None
     union_raider_preset_3: UnionRaiderPreset | None
diff --git a/python/maplestory_openapi/api/maplestory_api.py b/python/maplestory_openapi/api/maplestory_api.py
index c6c257e..99aced4 100644
--- a/python/maplestory_openapi/api/maplestory_api.py
+++ b/python/maplestory_openapi/api/maplestory_api.py
@@ -688,7 +688,7 @@ def get_union_ranking(self, world_name: str | None = None, ocid: str | None = No
         r = self.fetch(path, query)
         return UnionRanking(**r)
 
-    def get_guild_ranking(self, ranking_type: int, world_name: str | None = None, guild_name: str | None = None, page: int = 1,  date: datetime = get_proper_default_datetime(update_hour=8, update_minute=30, day_offset=0), ) -> GuildRanking:
+    def get_guild_ranking(self, ranking_type: int = 0, world_name: str | None = None, guild_name: str | None = None, page: int = 1,  date: datetime = get_proper_default_datetime(update_hour=8, update_minute=30, day_offset=0), ) -> GuildRanking:
         """길드 랭킹 정보를 조회합니다.
 
         - 2023년 12월 22일 데이터부터 조회할 수 있습니다.
diff --git a/python/requirements.txt b/python/requirements.txt
new file mode 100644
index 0000000..0ea055c
--- /dev/null
+++ b/python/requirements.txt
@@ -0,0 +1,3 @@
+requests
+pydantic>=2.5.3
+python-dotenv


Test patch:
diff --git a/.github/workflows/csharp_test.yaml b/.github/workflows/csharp_test.yaml
new file mode 100644
index 0000000..803ab49
--- /dev/null
+++ b/.github/workflows/csharp_test.yaml
@@ -0,0 +1,33 @@
+name: Test C#
+
+on:
+  push:
+    branches:
+      - master
+    paths:
+      - csharp/**
+
+jobs:
+  test_csharp:
+    runs-on: ubuntu-latest
+    defaults:
+      run:
+        working-directory: './csharp'
+
+    env:
+      API_KEY: ${{ secrets.API_KEY }}
+      PROJECT_NAME: MapleStory.Test
+
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v2
+
+      - name: Set up .NET Core
+        uses: actions/setup-dotnet@v1
+        with:
+          dotnet-version: 6.0.100
+
+      - name: Test
+        run: |
+          echo "Testing"
+          dotnet test ./${PROJECT_NAME}/${PROJECT_NAME}.csproj
\ No newline at end of file
diff --git a/.github/workflows/java_test.yaml b/.github/workflows/java_test.yaml
new file mode 100644
index 0000000..cb208f4
--- /dev/null
+++ b/.github/workflows/java_test.yaml
@@ -0,0 +1,36 @@
+name: Test Java
+
+on:
+  push:
+    branches:
+      - master
+    paths:
+      - java/**
+
+jobs:
+  test_java:
+    runs-on: ubuntu-latest
+    defaults:
+      run:
+        working-directory: './java'
+
+    env:
+      API_KEY: ${{ secrets.API_KEY }}
+
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v2
+
+      - name: Set up JDK
+        uses: actions/setup-java@v2
+        with:
+          distribution: 'adopt'
+          java-version: '8.0.392+8'
+
+      - name: Test
+        run: |
+          echo "apiKey=${API_KEY}" >> gradle.properties
+          echo "Testing"
+          ./gradlew test
+        env:
+          GRADLE_USER_HOME: ./gradle
\ No newline at end of file
diff --git a/.github/workflows/js_test.yaml b/.github/workflows/js_test.yaml
new file mode 100644
index 0000000..1e72d23
--- /dev/null
+++ b/.github/workflows/js_test.yaml
@@ -0,0 +1,37 @@
+name: Test JS
+
+on:
+  push:
+    branches:
+      - master
+    paths:
+      - js/**
+
+jobs:
+  test_js:
+    runs-on: ubuntu-latest
+    defaults:
+      run:
+        working-directory: './js'
+
+    env:
+      API_KEY: ${{ secrets.API_KEY }}
+
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v2
+
+      - name: Set up Node.js
+        uses: actions/setup-node@v3
+        with:
+          node-version: 16
+
+      - name: Install dependencies
+        run: |
+          echo "Installing dependencies"
+          npm install
+
+      - name: Test
+        run: |
+          echo "Testing"
+          API_KEY=${API_KEY} npm test
\ No newline at end of file
diff --git a/.github/workflows/python_test.yaml b/.github/workflows/python_test.yaml
new file mode 100644
index 0000000..ff1c83c
--- /dev/null
+++ b/.github/workflows/python_test.yaml
@@ -0,0 +1,38 @@
+name: Test Python
+
+on:
+  push:
+    branches:
+      - master
+    paths:
+      - python/**
+
+jobs:
+  test_python:
+    runs-on: ubuntu-latest
+    defaults:
+      run:
+        working-directory: './python'
+
+    env:
+      API_KEY: ${{ secrets.API_KEY }}
+
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v4
+
+      - name: Set up Python
+        uses: actions/setup-python@v4
+        with:
+          python-version: 3.10
+
+      - name: Install dependencies
+        run: |
+          echo "Installing dependencies"
+          pip3 install -r requirements.txt
+
+      - name: Test
+        run: |
+          echo "API_KEY=${API_KEY}" >> $GITHUB_ENV
+          echo "Testing"
+          pytest
\ No newline at end of file
diff --git a/java/src/test/java/CharacterApi.java b/java/src/test/java/CharacterApi.java
new file mode 100644
index 0000000..0472ded
--- /dev/null
+++ b/java/src/test/java/CharacterApi.java
@@ -0,0 +1,163 @@
+import dev.spiralmoon.maplestory.api.MapleStoryApi;
+import dev.spiralmoon.maplestory.api.dto.character.*;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+
+@DisplayName("캐릭터 정보 조회")
+public class CharacterApi {
+
+    private static final String apiKey = System.getProperty("API_KEY"); // Your API Key
+    private static final MapleStoryApi api = new MapleStoryApi(apiKey);
+    private static final String ocid = "e0a4f439e53c369866b55297d2f5f4eb";
+
+    @Test
+    @DisplayName("캐릭터 식별자(ocid) 조회")
+    void getCharacter() throws IOException {
+        String nickname = "아델";
+        CharacterDTO response = api.getCharacter(nickname);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("캐릭터 목록 조회")
+    void getCharacterList() throws IOException {
+        CharacterListDTO response = api.getCharacterList();
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("기본 정보 조회")
+    void getCharacterBasic() throws IOException {
+        CharacterBasicDTO response = api.getCharacterBasic(ocid);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("인기도 정보 조회")
+    void getCharacterPopularity() throws IOException {
+        CharacterPopularityDTO response = api.getCharacterPopularity(ocid);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("종합 능력치 정보 조회")
+    void getCharacterStat() throws IOException {
+        CharacterStatDTO response = api.getCharacterStat(ocid);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("하이퍼스탯 정보 조회")
+    void getCharacterHyperStat() throws IOException {
+        CharacterHyperStatDTO response = api.getCharacterHyperStat(ocid);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("성향 정보 조회")
+    void getCharacterPropensity() throws IOException {
+        CharacterPropensityDTO response = api.getCharacterPropensity(ocid);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("어빌리티 정보 조회")
+    void getCharacterAbility() throws IOException {
+        CharacterAbilityDTO response = api.getCharacterAbility(ocid);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("장착 장비 정보 조회 (캐시 장비 제외)")
+    void getCharacterItemEquipment() throws IOException {
+        CharacterItemEquipmentDTO response = api.getCharacterItemEquipment(ocid);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("장착 캐시 장비 정보 조회")
+    void getCharacterCashItemEquipment() throws IOException {
+        CharacterCashItemEquipmentDTO response = api.getCharacterCashItemEquipment(ocid);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("장착 심볼 정보 조회")
+    void getCharacterSymbolEquipment() throws IOException {
+        CharacterSymbolEquipmentDTO response = api.getCharacterSymbolEquipment(ocid);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("적용 세트 효과 정보 조회")
+    void getCharacterSetEffect() throws IOException {
+        CharacterSetEffectDTO response = api.getCharacterSetEffect(ocid);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("장착 헤어, 성형, 피부 정보 조회")
+    void getCharacterBeautyEquipment() throws IOException {
+        CharacterBeautyEquipmentDTO response = api.getCharacterBeautyEquipment(ocid);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("장착 안드로이드 정보 조회")
+    void getCharacterAndroidEquipment() throws IOException {
+        CharacterAndroidEquipmentDTO response = api.getCharacterAndroidEquipment(ocid);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("장착 펫 정보 조회")
+    void getCharacterPetEquipment() throws IOException {
+        CharacterPetEquipmentDTO response = api.getCharacterPetEquipment(ocid);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("스킬 정보 조회")
+    void getCharacterSkill() throws IOException {
+        String skillGrade = "0";
+        CharacterSkillDTO response = api.getCharacterSkill(ocid, skillGrade);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("장착 링크 스킬 정보 조회")
+    void getCharacterLinkSkill() throws IOException {
+        CharacterLinkSkillDTO response = api.getCharacterLinkSkill(ocid);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("V매트릭스 정보 조회")
+    void getCharacterVMatrix() throws IOException {
+        CharacterVMatrixDTO response = api.getCharacterVMatrix(ocid);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("HEXA 코어 정보 조회")
+    void getCharacterHexaMatrix() throws IOException {
+        CharacterHexaMatrixDTO response = api.getCharacterHexaMatrix(ocid);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("HEXA 매트릭스 설정 HEXA 스탯 정보 조회")
+    void getCharacterHexaMatrixStat() throws IOException {
+        CharacterHexaMatrixStatDTO response = api.getCharacterHexaMatrixStat(ocid);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("무릉도장 최고 기록 정보 조회")
+    void getCharacterDojang() throws IOException {
+        CharacterDojangDTO response = api.getCharacterDojang(ocid);
+        System.out.println(response.toString());
+    }
+}
diff --git a/java/src/test/java/GuildApi.java b/java/src/test/java/GuildApi.java
new file mode 100644
index 0000000..641c756
--- /dev/null
+++ b/java/src/test/java/GuildApi.java
@@ -0,0 +1,30 @@
+import dev.spiralmoon.maplestory.api.MapleStoryApi;
+import dev.spiralmoon.maplestory.api.dto.guild.*;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+
+@DisplayName("길드 정보 조회")
+public class GuildApi {
+
+    private static final String apiKey = System.getProperty("API_KEY"); // Your API Key
+    private static final MapleStoryApi api = new MapleStoryApi(apiKey);
+    private static final String ogid = "f5992063d46d6f8718e514fca5428960";
+
+    @Test
+    @DisplayName("길드 식별자(oguild_id) 정보 조회")
+    void getGuild() throws IOException {
+        String guildName = "붕붕";
+        String worldName = "크로아";
+        GuildDTO response = api.getGuild(guildName, worldName);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("기본 정보 조회")
+    void getGuildBasic() throws IOException {
+        GuildBasicDTO response = api.getGuildBasic(ogid);
+        System.out.println(response.toString());
+    }
+}
diff --git a/java/src/test/java/HistoryApi.java b/java/src/test/java/HistoryApi.java
new file mode 100644
index 0000000..1933afb
--- /dev/null
+++ b/java/src/test/java/HistoryApi.java
@@ -0,0 +1,37 @@
+import dev.spiralmoon.maplestory.api.MapleStoryApi;
+import dev.spiralmoon.maplestory.api.dto.history.*;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+
+@DisplayName("확률 정보 조회")
+public class HistoryApi {
+
+    private static final String apiKey = System.getProperty("API_KEY"); // Your API Key
+    private static final MapleStoryApi api = new MapleStoryApi(apiKey);
+
+    @Test
+    @DisplayName("스타포스 강화 결과 조회")
+    void getStarforceHistory() throws IOException {
+        int count = 1000;
+        StarforceHistoryResponseDTO response = api.getStarforceHistory(count);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("잠재능력 재설정 이용 결과 조회")
+    void getPotentialHistory() throws IOException {
+        int count = 1000;
+        PotentialHistoryResponseDTO response = api.getPotentialHistory(count);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("큐브 사용 결과 조회")
+    void getCubeHistory() throws IOException {
+        int count = 1000;
+        CubeHistoryResponseDTO response = api.getCubeHistory(count);
+        System.out.println(response.toString());
+    }
+}
diff --git a/java/src/test/java/RankingApi.java b/java/src/test/java/RankingApi.java
new file mode 100644
index 0000000..aaccf15
--- /dev/null
+++ b/java/src/test/java/RankingApi.java
@@ -0,0 +1,55 @@
+import dev.spiralmoon.maplestory.api.MapleStoryApi;
+import dev.spiralmoon.maplestory.api.dto.ranking.*;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+
+@DisplayName("랭킹 정보 조회")
+public class RankingApi {
+
+    private static final String apiKey = System.getProperty("API_KEY"); // Your API Key
+    private static final MapleStoryApi api = new MapleStoryApi(apiKey);
+
+    @Test
+    @DisplayName("종합 랭킹 정보 조회")
+    void getOverallRanking() throws IOException {
+        OverallRankingResponseDTO response = api.getOverallRanking(null, null, null, null, null);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("유니온 랭킹 정보 조회")
+    void getUnionRanking() throws IOException {
+        UnionRankingResponseDTO response = api.getUnionRanking(null, null, null);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("길드 랭킹 정보 조회")
+    void getGuildRanking() throws IOException {
+        GuildRankingResponseDTO response = api.getGuildRanking(null, 0, null, null);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("무릉도장 랭킹 정보 조회")
+    void getDojangRanking() throws IOException {
+        DojangRankingResponseDTO response = api.getDojangRanking(null, 0, null, null, null);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("더 시드 랭킹 정보 조회")
+    void getSeedRanking() throws IOException {
+        TheSeedRankingResponseDTO response = api.getTheSeedRanking(null, null, null);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("업적 랭킹 정보 조회")
+    void getAchievementRanking() throws IOException {
+        AchievementRankingResponseDTO response = api.getAchievementRanking(null, null);
+        System.out.println(response.toString());
+    }
+}
diff --git a/java/src/test/java/UnionApi.java b/java/src/test/java/UnionApi.java
new file mode 100644
index 0000000..e05e0bd
--- /dev/null
+++ b/java/src/test/java/UnionApi.java
@@ -0,0 +1,35 @@
+import dev.spiralmoon.maplestory.api.MapleStoryApi;
+import dev.spiralmoon.maplestory.api.dto.union.*;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+
+@DisplayName("유니온 정보 조회")
+public class UnionApi {
+
+    private static final String apiKey = System.getProperty("API_KEY"); // Your API Key
+    private static final MapleStoryApi api = new MapleStoryApi(apiKey);
+    private static final String ocid = "e0a4f439e53c369866b55297d2f5f4eb";
+
+    @Test
+    @DisplayName("유니온 정보 조회")
+    void getUnion() throws IOException {
+        UnionDTO response = api.getUnion(ocid);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("유니온 공격대 정보 조회")
+    void getUnionRaider() throws IOException {
+        UnionRaiderDTO response = api.getUnionRaider(ocid);
+        System.out.println(response.toString());
+    }
+
+    @Test
+    @DisplayName("유니온 아티팩트 정보 조회")
+    void getUnionArtifact() throws IOException {
+        UnionArtifactDTO response = api.getUnionArtifact(ocid);
+        System.out.println(response.toString());
+    }
+}
diff --git a/js/test/characterApi.test.ts b/js/test/characterApi.test.ts
new file mode 100644
index 0000000..0191132
--- /dev/null
+++ b/js/test/characterApi.test.ts
@@ -0,0 +1,141 @@
+import * as process from 'process';
+
+import { MapleStoryApi } from '@src/maplestory/api/mapleStoryApi';
+
+import { toString } from './utils';
+
+const apiKey = process.env.API_KEY!; // Your API Key
+const api = new MapleStoryApi(apiKey);
+
+const ocid = 'e0a4f439e53c369866b55297d2f5f4eb';
+
+describe('캐릭터 정보 조회', () => {
+
+  test('캐릭터 식별자(ocid) 조회', async () => {
+    const nickname = '아델';
+    const response = await api.getCharacter(nickname);
+
+    console.log(toString(response));
+  });
+
+  test('캐릭터 목록 조회', async () => {
+    const response = await api.getCharacterList();
+
+    console.log(toString(response));
+  });
+
+  test('기본 정보 조회', async () => {
+    const response = await api.getCharacterBasic(ocid);
+
+    console.log(toString(response));
+  });
+
+  test('인기도 정보 조회', async () => {
+    const response = await api.getCharacterPopularity(ocid);
+
+    console.log(toString(response));
+  });
+
+  test('종합 능력치 정보 조회', async () => {
+    const response = await api.getCharacterStat(ocid);
+
+    console.log(toString(response));
+  });
+
+  test('하이퍼스탯 정보 조회', async () => {
+    const response = await api.getCharacterHyperStat(ocid);
+
+    console.log(toString(response));
+  });
+
+  test('성향 정보 조회', async () => {
+    const response = await api.getCharacterPropensity(ocid);
+
+    console.log(toString(response));
+  });
+
+  test('어빌리티 정보 조회', async () => {
+    const response = await api.getCharacterAbility(ocid);
+
+    console.log(toString(response));
+  });
+
+  test('장착 장비 정보 조회 (캐시 장비 제외)', async () => {
+    const response = await api.getCharacterItemEquipment(ocid);
+
+    console.log(toString(response));
+  });
+
+  test('장착 캐시 장비 정보 조회', async () => {
+    const response = await api.getCharacterCashItemEquipment(ocid);
+
+    console.log(toString(response));
+  });
+
+  test('장착 심볼 정보 조회', async () => {
+    const response = await api.getCharacterSymbolEquipment(ocid);
+
+    console.log(toString(response));
+  });
+
+  test('적용 세트 효과 정보 조회', async () => {
+    const response = await api.getCharacterSetEffect(ocid);
+
+    console.log(toString(response));
+  });
+
+  test('장착 헤어, 성형, 피부 정보 조회', async () => {
+    const response = await api.getCharacterBeautyEquipment(ocid);
+
+    console.log(toString(response));
+  });
+
+  test('장착 안드로이드 정보 조회', async () => {
+    const response = await api.getCharacterAndroidEquipment(ocid);
+
+    console.log(toString(response));
+  });
+
+  test('장착 펫 정보 조회', async () => {
+    const response = await api.getCharacterPetEquipment(ocid);
+
+    console.log(toString(response));
+  });
+
+  test('스킬 정보 조회', async () => {
+    const skillGrade = '6';
+    const response = await api.getCharacterSkill(ocid, skillGrade);
+
+    console.log(toString(response));
+  });
+
+  test('장착 링크 스킬 정보 조회', async () => {
+    const response = await api.getCharacterLinkSkill(ocid);
+
+    console.log(toString(response));
+  });
+
+  test('V매트릭스 정보 조회', async () => {
+    const response = await api.getCharacterVMatrix(ocid);
+
+    console.log(toString(response));
+  });
+
+  test('HEXA 코어 정보 조회', async () => {
+    const response = await api.getCharacterHexaMatrix(ocid);
+
+    console.log(toString(response));
+  });
+
+  test('HEXA 매트릭스 설정 HEXA 스탯 정보 조회', async () => {
+    const response = await api.getCharacterHexaMatrixStat(ocid);
+
+    console.log(toString(response));
+  });
+
+  test('무릉도장 최고 기록 정보 조회', async () => {
+    const response = await api.getCharacterDojang(ocid);
+
+    console.log(toString(response));
+  });
+});
diff --git a/js/test/guildApi.test.ts b/js/test/guildApi.test.ts
new file mode 100644
index 0000000..5494cda
--- /dev/null
+++ b/js/test/guildApi.test.ts
@@ -0,0 +1,27 @@
+import process from 'process';
+
+import { MapleStoryApi } from '@src/maplestory/api/mapleStoryApi';
+
+import { toString } from './utils';
+
+const apiKey = process.env.API_KEY!; // Your API Key
+const api = new MapleStoryApi(apiKey);
+
+const ogid = 'f5992063d46d6f8718e514fca5428960';
+
+describe('길드 정보 조회', () => {
+
+  test('길드 식별자(oguild_id) 정보 조회', async () => {
+    const guildName = '붕붕';
+    const worldName = '크로아'
+    const response = await api.getGuild(guildName, worldName);
+
+    console.log(toString(response));
+  });
+
+  test('기본 정보 조회', async () => {
+    const response = await api.getGuildBasic(ogid);
+
+    console.log(toString(response));
+  });
+});
diff --git a/js/test/historyApi.test.ts b/js/test/historyApi.test.ts
new file mode 100644
index 0000000..e6b2305
--- /dev/null
+++ b/js/test/historyApi.test.ts
@@ -0,0 +1,32 @@
+import process from 'process';
+
+import { MapleStoryApi } from '@src/maplestory/api/mapleStoryApi';
+
+import { toString } from './utils';
+
+const apiKey = process.env.API_KEY!; // Your API Key
+const api = new MapleStoryApi(apiKey);
+
+describe('확률 정보 조회', () => {
+
+  test('스타포스 강화 결과 조회', async () => {
+    const count = 1000;
+    const response = await api.getStarforceHistory(count);
+
+    console.log(toString(response));
+  });
+
+  test('잠재능력 재설정 이용 결과 조회', async () => {
+    const count = 1000;
+    const response = await api.getPotentialHistory(count);
+
+    console.log(toString(response));
+  });
+
+  test('큐브 사용 결과 조회', async () => {
+    const count = 1000;
+    const response = await api.getCubeHistory(count);
+
+    console.log(toString(response));
+  });
+});
diff --git a/js/test/index.ts b/js/test/index.ts
deleted file mode 100644
index 8b1a393..0000000
--- a/js/test/index.ts
+++ /dev/null
@@ -1,1 +0,0 @@
-// empty
diff --git a/js/test/rankingApi.test.ts b/js/test/rankingApi.test.ts
new file mode 100644
index 0000000..f7ef605
--- /dev/null
+++ b/js/test/rankingApi.test.ts
@@ -0,0 +1,47 @@
+import process from 'process';
+
+import { MapleStoryApi } from '@src/maplestory/api/mapleStoryApi';
+
+import { toString } from './utils';
+
+const apiKey = process.env.API_KEY!; // Your API Key
+const api = new MapleStoryApi(apiKey);
+
+describe('랭킹 정보 조회', () => {
+
+  test('종합 랭킹 정보 조회', async () => {
+    const response = await api.getOverallRanking();
+
+    console.log(toString(response));
+  });
+
+  test('유니온 랭킹 정보 조회', async () => {
+    const response = await api.getUnionRanking();
+
+    console.log(toString(response));
+  });
+
+  test('길드 랭킹 정보 조회', async () => {
+    const response = await api.getGuildRanking();
+
+    console.log(toString(response));
+  });
+
+  test('무릉도장 랭킹 정보 조회', async () => {
+    const response = await api.getDojangRanking();
+
+    console.log(toString(response));
+  });
+
+  test('더 시드 랭킹 정보 조회', async () => {
+    const response = await api.getSeedRanking();
+
+    console.log(toString(response));
+  });
+
+  test('업적 랭킹 정보 조회', async () => {
+    const response = await api.getAchievementRanking();
+
+    console.log(toString(response));
+  });
+});
diff --git a/js/test/unionApi.test.ts b/js/test/unionApi.test.ts
new file mode 100644
index 0000000..5f6d66e
--- /dev/null
+++ b/js/test/unionApi.test.ts
@@ -0,0 +1,31 @@
+import process from 'process';
+
+import { MapleStoryApi } from '@src/maplestory/api/mapleStoryApi';
+
+import { toString } from './utils';
+
+const apiKey = process.env.API_KEY!; // Your API Key
+const api = new MapleStoryApi(apiKey);
+
+const ocid = 'e0a4f439e53c369866b55297d2f5f4eb';
+
+describe('유니온 정보 조회', () => {
+
+  test('유니온 정보 조회', async () => {
+    const response = await api.getUnion(ocid);
+
+    console.log(toString(response));
+  });
+
+  test('유니온 공격대 정보 조회', async () => {
+    const response = await api.getUnionRaider(ocid);
+
+    console.log(toString(response));
+  });
+
+  test('유니온 아티팩트 정보 조회', async () => {
+    const response = await api.getUnionArtifact(ocid);
+
+    console.log(toString(response));
+  });
+});
diff --git a/js/test/utils.ts b/js/test/utils.ts
new file mode 100644
index 0000000..3bf672d
--- /dev/null
+++ b/js/test/utils.ts
@@ -0,0 +1,1 @@
+export const toString = (obj: unknown) => JSON.stringify(obj, null, 2);
diff --git a/python/test.py b/python/test.py
deleted file mode 100644
index 7cc074a..0000000
--- a/python/test.py
+++ /dev/null
@@ -1,56 +0,0 @@
-from datetime import datetime
-import os
-
-from maplestory_openapi.api.maplestory_api import MapleStoryApi
-from dotenv import load_dotenv
-
-load_dotenv()
-
-api = MapleStoryApi(
-    api_key=os.environ.get('NEXON_API_KEY'), timeout=5000)
-character_name = '아델'
-ocid = api.get_character_id(character_name).ocid
-basic = api.get_character_basic(ocid)
-
-# popularity = api.get_character_popularity(ocid)
-# stat = api.get_character_stat(ocid)
-# hyper_stat = api.get_character_hyper_stat(ocid)
-# propensity = api.get_character_propensity(ocid)
-# ability = api.get_character_ability(ocid)
-# equipment = api.get_character_item_equipment(ocid)
-# cash_equipment = api.get_character_cashitem_equipment(ocid)
-# symbol = api.get_character_symbol_equipment(ocid)
-# set_effect = api.get_character_set_effect(ocid)
-# beauty_equipment = api.get_character_beauty_equipment(ocid)
-# android_equipment = api.get_character_android_equipment(ocid)
-# pet_equipment = api.get_character_pet_equipment(ocid)
-# skill = api.get_character_skill(ocid, '6')
-# link_skill = api.get_character_link_skill(ocid)
-# vmatrix = api.get_character_vmatrix(ocid)
-# hexamatrix = api.get_character_hexamatrix(ocid)
-# hexamatrix_stat = api.get_character_hexamatrix_stat(ocid)
-# dojang = api.get_character_dojang(ocid)
-# union = api.get_union(ocid)
-# union_radar = api.get_union_raider(ocid)
-
-oguild_id = api.get_guild_id(
-    guild_name=basic.character_guild_name, world_name=basic.world_name).oguild_id
-# guild = api.get_guild_basic(oguild_id)
-
-# cube_history = api.get_cube_history(
-#     count=10, date=datetime(2023, 7, 16), cursor=None)
-# cube_history_next = api.get_cube_history(
-#     count=10, date=None, cursor=cube_history.next_cursor)
-# starforce_history = api.get_starforce_history(
-#     count=10, date=datetime(2023, 12, 28), cursor=None)
-# starforce_history_next = api.get_starforce_history(
-#     count=10, cursor=starforce_history.next_cursor)
-
-# overall_ranking = api.get_overall_ranking()
-# union_ranking = api.get_union_ranking()
-# guild_ranking = api.get_guild_ranking(ranking_type=1)
-# dojang_ranking = api.get_dojang_ranking()
-# theseed_ranking = api.get_theseed_ranking()
-# achievement_ranking = api.get_achievement_ranking()
-
-print(basic)
diff --git a/python/test/character_api_test.py b/python/test/character_api_test.py
new file mode 100644
index 0000000..6308794
--- /dev/null
+++ b/python/test/character_api_test.py
@@ -0,0 +1,102 @@
+import os
+import unittest
+
+from maplestory_openapi import MapleStoryApi
+
+api_key = os.environ['API_KEY']
+api = MapleStoryApi(api_key)
+
+ocid = 'e0a4f439e53c369866b55297d2f5f4eb'
+
+
+# 캐릭터 정보 조회
+class CharacterApi(unittest.TestCase):
+    def test_character(self):
+        nickname = '아델'
+        response = api.get_character_id(nickname)
+        print(response)
+
+    def test_character_list(self):
+        response = api.get_character_list()
+        print(response)
+
+    def test_character_basic(self):
+        response = api.get_character_basic(ocid)
+        print(response)
+
+    def test_character_popularity(self):
+        response = api.get_character_popularity(ocid)
+        print(response)
+
+    def test_character_stat(self):
+        response = api.get_character_stat(ocid)
+        print(response)
+
+    def test_character_hyper_stat(self):
+        response = api.get_character_hyper_stat(ocid)
+        print(response)
+
+    def test_character_propensity(self):
+        response = api.get_character_propensity(ocid)
+        print(response)
+
+    def test_character_ability(self):
+        response = api.get_character_ability(ocid)
+        print(response)
+
+    def test_character_item_equipment(self):
+        response = api.get_character_item_equipment(ocid)
+        print(response)
+
+    def test_character_cashitem_equipment(self):
+        response = api.get_character_cashitem_equipment(ocid)
+        print(response)
+
+    def test_character_symbol_equipment(self):
+        response = api.get_character_symbol_equipment(ocid)
+        print(response)
+
+    def test_character_set_effect(self):
+        response = api.get_character_set_effect(ocid)
+        print(response)
+
+    def test_character_beauty_equipment(self):
+        response = api.get_character_beauty_equipment(ocid)
+        print(response)
+
+    def test_character_android_equipment(self):
+        response = api.get_character_android_equipment(ocid)
+        print(response)
+
+    def test_character_pet_equipment(self):
+        response = api.get_character_pet_equipment(ocid)
+        print(response)
+
+    def test_character_skill(self):
+        skill_grade = '0'
+        response = api.get_character_skill(ocid, skill_grade)
+        print(response)
+
+    def test_character_link_skill(self):
+        response = api.get_character_link_skill(ocid)
+        print(response)
+
+    def test_character_v_matrix(self):
+        response = api.get_character_vmatrix(ocid)
+        print(response)
+
+    def test_character_hexa_matrix(self):
+        response = api.get_character_hexamatrix(ocid)
+        print(response)
+
+    def test_character_hexa_matrix_stat(self):
+        response = api.get_character_hexamatrix_stat(ocid)
+        print(response)
+
+    def test_character_dojang(self):
+        response = api.get_character_dojang(ocid)
+        print(response)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/python/test/guild_api_test.py b/python/test/guild_api_test.py
new file mode 100644
index 0000000..5e42985
--- /dev/null
+++ b/python/test/guild_api_test.py
@@ -0,0 +1,26 @@
+import os
+import unittest
+
+from maplestory_openapi import MapleStoryApi
+
+api_key = os.environ['API_KEY']
+api = MapleStoryApi(api_key)
+
+ogid = 'f5992063d46d6f8718e514fca5428960'
+
+
+# 길드 정보 조회
+class GuildApi(unittest.TestCase):
+    def test_guild_id(self):
+        guild_name = '붕붕'
+        world_name = '크로아'
+        response = api.get_guild_id(guild_name, world_name)
+        print(response)
+
+    def test_guild_basic(self):
+        response = api.get_guild_basic(ogid)
+        print(response)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/python/test/history_api_test.py b/python/test/history_api_test.py
new file mode 100644
index 0000000..265ca62
--- /dev/null
+++ b/python/test/history_api_test.py
@@ -0,0 +1,29 @@
+import os
+import unittest
+
+from maplestory_openapi import MapleStoryApi
+
+api_key = os.environ['API_KEY']
+api = MapleStoryApi(api_key)
+
+
+# 확률 정보 조회
+class HistoryApi(unittest.TestCase):
+    def test_starforce_history(self):
+        count = 1000
+        response = api.get_starforce_history(count)
+        print(response)
+
+    def test_potential_history(self):
+        count = 1000
+        response = api.get_potential_history(count)
+        print(response)
+
+    def test_cube_history(self):
+        count = 1000
+        response = api.get_cube_history(count)
+        print(response)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/python/test/ranking_api_test.py b/python/test/ranking_api_test.py
new file mode 100644
index 0000000..a9f2c09
--- /dev/null
+++ b/python/test/ranking_api_test.py
@@ -0,0 +1,38 @@
+import os
+import unittest
+
+from maplestory_openapi import MapleStoryApi
+
+api_key = os.environ['API_KEY']
+api = MapleStoryApi(api_key)
+
+
+# 랭킹 정보 조회
+class RankingApi(unittest.TestCase):
+    def test_overall_ranking(self):
+        response = api.get_overall_ranking()
+        print(response)
+
+    def test_union_ranking(self):
+        response = api.get_union_ranking()
+        print(response)
+
+    def test_guild_ranking(self):
+        response = api.get_guild_ranking()
+        print(response)
+
+    def test_dojang_ranking(self):
+        response = api.get_dojang_ranking()
+        print(response)
+
+    def test_seed_ranking(self):
+        response = api.get_theseed_ranking()
+        print(response)
+
+    def test_achievement_ranking(self):
+        response = api.get_achievement_ranking()
+        print(response)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/python/test/union_api_test.py b/python/test/union_api_test.py
new file mode 100644
index 0000000..dbf5ac4
--- /dev/null
+++ b/python/test/union_api_test.py
@@ -0,0 +1,28 @@
+import os
+import unittest
+
+from maplestory_openapi import MapleStoryApi
+
+api_key = os.environ['API_KEY']
+api = MapleStoryApi(api_key)
+
+ocid = 'e0a4f439e53c369866b55297d2f5f4eb'
+
+
+# 유니온 정보 조회
+class UnionApi(unittest.TestCase):
+    def test_union(self):
+        response = api.get_union(ocid)
+        print(response)
+
+    def test_union_raider(self):
+        response = api.get_union_raider(ocid)
+        print(response)
+
+    def test_union_artifact(self):
+        response = api.get_union_artifact(ocid)
+        print(response)
+
+
+if __name__ == '__main__':
+    unittest.main()