diff --git a/CHANGELOG.md b/CHANGELOG.md
index 03c93f70df..1cc6b3f0d4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,7 @@
 	- センシティブチャンネルのノートはユーザープロフィールに表示されません
 - 二要素認証のバックアップコードが生成されるようになりました ref. https://github.com/MisskeyIO/misskey/pull/121
 - 二要素認証でパスキーをサポートするようになりました
+- 通知をテストできるようになりました
 
 ### Client
 - プロフィールにその人が作ったPlayの一覧出せるように
@@ -33,7 +34,6 @@
 - 投稿フォームのプレビューの表示状態を記憶するように
 - AiScriptからMisskeyサーバーAPIを呼び出す際の制限を撤廃
 - Playで直接投稿フォームを埋め込めるように(`Ui:C:postForm`)
-- 通知をテストできるように
 - Enhance: ユーザーメニューでスイッチでユーザーリストに追加・削除できるように
 - Enhance: 自分が押したリアクションのデザインを改善
 - Enhance: ノート検索にローカルのみ検索可能なオプションの追加
@@ -46,6 +46,7 @@
 - リアクションの表示サイズをより大きくできるように
 - ノート詳細ページ読み込み時のパフォーマンスを改善
 - タイムラインでリスト/アンテナ選択時のパフォーマンスを改善
+- 新しい実績を追加
 - Fix: サーバー情報画面(`/instance-info/{domain}`)でブロックができないのを修正
 - Fix: 未読のお知らせの「わかった」をクリック・タップしてもその場で「わかった」が消えない問題を修正
 - Fix: iOSで画面を回転させるとテキストサイズが変わる問題を修正
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 36897285c4..746c51c991 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -1467,6 +1467,10 @@ export interface Locale {
                 "description": string;
                 "flavor": string;
             };
+            "_smashTestNotificationButton": {
+                "title": string;
+                "description": string;
+            };
         };
     };
     "_role": {
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index f9427e13ec..3cde23ca91 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1391,6 +1391,9 @@ _achievements:
       title: "Brain Diver"
       description: "Brain Diverへのリンクを投稿した"
       flavor: "Misskey-Misskey La-Tu-Ma"
+    _smashTestNotificationButton:
+      title: "テスト過剰"
+      description: "通知のテストをごく短時間のうちに連続して行った"
 
 _role:
   new: "ロールの作成"
diff --git a/packages/backend/src/core/AchievementService.ts b/packages/backend/src/core/AchievementService.ts
index aa810015ed..a35acd3680 100644
--- a/packages/backend/src/core/AchievementService.ts
+++ b/packages/backend/src/core/AchievementService.ts
@@ -85,6 +85,7 @@ export const ACHIEVEMENT_TYPES = [
 	'setNameToSyuilo',
 	'cookieClicked',
 	'brainDiver',
+	'smashTestNotificationButton',
 ] as const;
 
 @Injectable()
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index 31d5dd93ec..b486e6d80f 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -96,7 +96,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<option value="horizontal"><i class="ti ti-carousel-horizontal"></i> {{ i18n.ts.horizontal }}</option>
 			</MkRadios>
 
-			<MkButton @click="testNotification('client')">{{ i18n.ts._notification.checkNotificationBehavior }}</MkButton>
+			<MkButton @click="testNotification">{{ i18n.ts._notification.checkNotificationBehavior }}</MkButton>
 		</div>
 	</FormSection>
 
@@ -176,6 +176,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, ref, watch } from 'vue';
+import * as Misskey from 'misskey-js';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkSelect from '@/components/MkSelect.vue';
 import MkRadios from '@/components/MkRadios.vue';
@@ -192,7 +193,8 @@ import { unisonReload } from '@/scripts/unison-reload';
 import { i18n } from '@/i18n';
 import { definePageMetadata } from '@/scripts/page-metadata';
 import { miLocalStorage } from '@/local-storage';
-import { testNotification } from '@/scripts/test-notification';
+import { globalEvents } from '@/events';
+import { claimAchievement } from '@/scripts/achievements';
 
 const lang = ref(miLocalStorage.getItem('lang'));
 const fontSize = ref(miLocalStorage.getItem('fontSize'));
@@ -305,6 +307,32 @@ function removeEmojiIndex(lang: string) {
 	os.promiseDialog(main());
 }
 
+let smashCount = 0;
+let smashTimer: number | null = null;
+function testNotification(): void {
+	const notification: Misskey.entities.Notification = {
+		id: Math.random().toString(),
+		createdAt: new Date().toUTCString(),
+		isRead: false,
+		type: 'test',
+	};
+
+	globalEvents.emit('clientNotification', notification);
+
+	// セルフ通知破壊 実績関連
+	smashCount++;
+	if (smashCount >= 10) {
+		claimAchievement('smashTestNotificationButton');
+		smashCount = 0;
+	}
+	if (smashTimer) {
+		clearTimeout(smashTimer);
+	}
+	smashTimer = window.setTimeout(() => {
+		smashCount = 0;
+	}, 300);
+}
+
 const headerActions = $computed(() => []);
 
 const headerTabs = $computed(() => []);
diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue
index b20add724c..193795cdec 100644
--- a/packages/frontend/src/pages/settings/notifications.vue
+++ b/packages/frontend/src/pages/settings/notifications.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	</FormSection>
 	<FormSection>
 		<div class="_gaps_m">
-			<FormLink @click="testNotification('server')">{{ i18n.ts._notification.sendTestNotification }}</FormLink>
+			<FormLink @click="testNotification">{{ i18n.ts._notification.sendTestNotification }}</FormLink>
 		</div>
 	</FormSection>
 	<FormSection>
@@ -46,7 +46,6 @@ import { i18n } from '@/i18n';
 import { definePageMetadata } from '@/scripts/page-metadata';
 import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowButton.vue';
 import { notificationTypes } from '@/const';
-import { testNotification } from '@/scripts/test-notification';
 
 let allowButton = $shallowRef<InstanceType<typeof MkPushNotificationAllowButton>>();
 let pushRegistrationInServer = $computed(() => allowButton?.pushRegistrationInServer);
@@ -89,6 +88,10 @@ function onChangeSendReadMessage(v: boolean) {
 	});
 }
 
+function testNotification(): void {
+	os.api('notifications/test-notification');
+}
+
 const headerActions = $computed(() => []);
 
 const headerTabs = $computed(() => []);
diff --git a/packages/frontend/src/scripts/achievements.ts b/packages/frontend/src/scripts/achievements.ts
index 8b3a518830..54242b330d 100644
--- a/packages/frontend/src/scripts/achievements.ts
+++ b/packages/frontend/src/scripts/achievements.ts
@@ -81,6 +81,7 @@ export const ACHIEVEMENT_TYPES = [
 	'setNameToSyuilo',
 	'cookieClicked',
 	'brainDiver',
+	'smashTestNotificationButton',
 ] as const;
 
 export const ACHIEVEMENT_BADGES = {
@@ -454,6 +455,11 @@ export const ACHIEVEMENT_BADGES = {
 		bg: 'linear-gradient(0deg, rgb(144, 224, 255), rgb(255, 168, 252))',
 		frame: 'bronze',
 	},
+	'smashTestNotificationButton': {
+		img: '/fluent-emoji/1f514.png',
+		bg: 'linear-gradient(0deg, rgb(187 183 59), rgb(255 143 77))',
+		frame: 'bronze',
+	},
 /* @see <https://github.com/misskey-dev/misskey/pull/10365#discussion_r1155511107>
 } as const satisfies Record<typeof ACHIEVEMENT_TYPES[number], {
 	img: string;
diff --git a/packages/frontend/src/scripts/test-notification.ts b/packages/frontend/src/scripts/test-notification.ts
deleted file mode 100644
index 0e8289e19e..0000000000
--- a/packages/frontend/src/scripts/test-notification.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import * as Misskey from 'misskey-js';
-import * as os from '@/os';
-import { globalEvents } from '@/events';
-
-/**
- * テスト通知を送信
- * 
- * - `client` … 通知ポップアップのみを表示
- * - `server` … サーバー側から通知を送信
- * 
- * @param type 通知タイプを指定
- */
-export function testNotification(type: 'client' | 'server'): void {
-	const notification: Misskey.entities.Notification = {
-		id: Math.random().toString(),
-		createdAt: new Date().toUTCString(),
-		isRead: false,
-		type: 'test',
-	};
-
-	switch (type) {
-		case 'server':
-			os.api('notifications/test-notification');
-			break;
-		case 'client':
-			globalEvents.emit('clientNotification', notification);
-			break;
-	}
-}