diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index 3b46075..fab51eb 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -2,6 +2,7 @@
     "recommendations": [
         "dbaeumer.vscode-eslint",
         "esbenp.prettier-vscode",
-        "gruntfuggly.todo-tree"
+        "gruntfuggly.todo-tree",
+        "eg2.vscode-npm-script"
     ]
 }
diff --git a/package-lock.json b/package-lock.json
index d1ee44e..8a60e38 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -110,6 +110,12 @@
         "fastq": "^1.6.0"
       }
     },
+    "@types/jasmine": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.6.2.tgz",
+      "integrity": "sha512-AzfesNFLvOs6Q1mHzIsVJXSeUnqVh4ZHG8ngygKJfbkcSLwzrBVm/LKa+mR8KrOfnWtUL47112gde1MC0IXqpQ==",
+      "dev": true
+    },
     "@types/jquery": {
       "version": "3.5.5",
       "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.5.tgz",
@@ -610,6 +616,12 @@
         "readable-stream": "^2.0.6"
       }
     },
+    "arg": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+      "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+      "dev": true
+    },
     "argparse": {
       "version": "1.0.10",
       "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
@@ -1571,6 +1583,12 @@
         }
       }
     },
+    "create-require": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+      "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+      "dev": true
+    },
     "cross-spawn": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz",
@@ -1733,6 +1751,12 @@
       "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=",
       "dev": true
     },
+    "diff": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+      "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+      "dev": true
+    },
     "dir-glob": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
@@ -3748,6 +3772,22 @@
       "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
       "dev": true
     },
+    "jasmine": {
+      "version": "3.6.3",
+      "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.6.3.tgz",
+      "integrity": "sha512-Th91zHsbsALWjDUIiU5d/W5zaYQsZFMPTdeNmi8GivZPmAaUAK8MblSG3yQI4VMGC/abF2us7ex60NH1AAIMTA==",
+      "dev": true,
+      "requires": {
+        "glob": "^7.1.6",
+        "jasmine-core": "~3.6.0"
+      }
+    },
+    "jasmine-core": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.6.0.tgz",
+      "integrity": "sha512-8uQYa7zJN8hq9z+g8z1bqCfdC8eoDAeVnM5sfqs7KHv9/ifoJ500m018fpFc7RDaO6SWCLCXwo/wPSNcdYTgcw==",
+      "dev": true
+    },
     "js-base64": {
       "version": "2.6.4",
       "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz",
@@ -4326,6 +4366,12 @@
         }
       }
     },
+    "make-error": {
+      "version": "1.3.6",
+      "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+      "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+      "dev": true
+    },
     "make-iterator": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz",
@@ -5993,6 +6039,24 @@
         "urix": "^0.1.0"
       }
     },
+    "source-map-support": {
+      "version": "0.5.19",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
+      "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
+      "dev": true,
+      "requires": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
+      }
+    },
     "source-map-url": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
@@ -6514,6 +6578,20 @@
         "glob": "^7.1.2"
       }
     },
+    "ts-node": {
+      "version": "9.1.1",
+      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz",
+      "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==",
+      "dev": true,
+      "requires": {
+        "arg": "^4.1.0",
+        "create-require": "^1.1.0",
+        "diff": "^4.0.1",
+        "make-error": "^1.1.1",
+        "source-map-support": "^0.5.17",
+        "yn": "3.1.1"
+      }
+    },
     "tslib": {
       "version": "1.14.1",
       "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
@@ -7102,6 +7180,12 @@
         "object.assign": "^4.1.0"
       }
     },
+    "yn": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+      "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+      "dev": true
+    },
     "zip-stream": {
       "version": "4.0.4",
       "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.0.4.tgz",
diff --git a/package.json b/package.json
index 7099f55..2125458 100644
--- a/package.json
+++ b/package.json
@@ -11,11 +11,13 @@
         "clean": "gulp clean && gulp link --clean",
         "update": "npm install --save-dev git+https://git.f3l.de/dungeonslayers/foundry-pc-types.git#f3l-fixes",
         "lint": "eslint 'src/**/*.ts' --cache",
-        "lint:fix": "eslint 'src/**/*.ts' --cache --fix"
+        "lint:fix": "eslint 'src/**/*.ts' --cache --fix",
+        "test": "ts-node ./node_modules/jasmine/bin/jasmine"
     },
     "author": "",
     "license": "",
     "devDependencies": {
+        "@types/jasmine": "^3.6.2",
         "@typescript-eslint/eslint-plugin": "^4.11.0",
         "@typescript-eslint/parser": "^4.11.0",
         "archiver": "^5.1.0",
@@ -31,10 +33,12 @@
         "gulp-sass": "^4.1.0",
         "gulp-typescript": "^6.0.0-alpha.1",
         "husky": "^4.3.6",
+        "jasmine": "^3.6.3",
         "json-stringify-pretty-compact": "^2.0.0",
         "lint-staged": "^10.5.3",
         "prettier": "^2.2.1",
         "sass": "^1.30.0",
+        "ts-node": "^9.1.1",
         "typescript": "^4.1.3",
         "yargs": "^16.2.0"
     },
diff --git a/spec/support/ds4rolls.spec.ts b/spec/support/ds4rolls.spec.ts
new file mode 100644
index 0000000..c467c29
--- /dev/null
+++ b/spec/support/ds4rolls.spec.ts
@@ -0,0 +1,19 @@
+import {
+    rollCheckSingleDie,
+    RollOptions,
+    RollProvider,
+    RollResult,
+    RollResultStatus,
+} from "../../src/module/rolls/ds4rolls";
+
+describe("DS4 Rolls", () => {
+    it("Should do a proper single success role", () => {
+        const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]);
+
+        rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(4);
+
+        expect(rollCheckSingleDie(12, new RollOptions(), rollProvider)).toEqual(
+            new RollResult(4, RollResultStatus.SUCCESS, [4]),
+        );
+    });
+});
diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json
new file mode 100644
index 0000000..6d97768
--- /dev/null
+++ b/spec/support/jasmine.json
@@ -0,0 +1,7 @@
+{
+    "spec_dir": "spec",
+    "spec_files": ["**/*[sS]pec.ts"],
+    "helpers": ["helpers/**/*.ts"],
+    "stopSpecOnExpectationFailure": false,
+    "random": true
+}
diff --git a/src/module/rolls/ds4rolls.ts b/src/module/rolls/ds4rolls.ts
index 50c6fed..74c76a4 100644
--- a/src/module/rolls/ds4rolls.ts
+++ b/src/module/rolls/ds4rolls.ts
@@ -1,4 +1,4 @@
-export function roll(testValue: number, rollOptions: RollOptions = new RollOptions()): Promise<RollResult> {
+export function ds4test(testValue: number, rollOptions: RollOptions = new RollOptions()): RollResult {
     const finalRollValue = testValue;
     if (finalRollValue <= 20) {
         return rollCheckSingleDie(finalRollValue, rollOptions);
@@ -7,26 +7,45 @@ export function roll(testValue: number, rollOptions: RollOptions = new RollOptio
     }
 }
 
-async function rollCheckSingleDie(testValue: number, rollOptions: RollOptions): Promise<RollResult> {
-    const roll = new Roll("1d20");
-    roll.roll();
+export class DS4RollProvider {
+    getNextRoll(): number {
+        return new Roll("1d20").roll().total;
+    }
 
-    const pool = new DicePool({ rolls: [roll] });
+    getNextRolls(amount: number): Array<number> {
+        return Array(amount)
+            .fill(0)
+            .map(() => this.getNextRoll());
+    }
+}
 
-    if (roll.total <= rollOptions.maxCritSucc) {
-        return createRollResultPromise(testValue, RollResultStatus.CRITICAL_SUCCESS, pool);
-    } else if (roll.total >= rollOptions.minCritFail) {
-        return createRollResultPromise(0, RollResultStatus.CRITICAL_FAILURE, pool);
+export interface RollProvider {
+    getNextRoll(): number;
+    getNextRolls(number): Array<number>;
+}
+
+export function rollCheckSingleDie(
+    testValue: number,
+    rollOptions: RollOptions,
+    provider: RollProvider = new DS4RollProvider(),
+): RollResult {
+    const roll = provider.getNextRoll();
+    const dice = [roll];
+
+    if (roll <= rollOptions.maxCritSucc) {
+        return createRollResult(testValue, RollResultStatus.CRITICAL_SUCCESS, dice);
+    } else if (roll >= rollOptions.minCritFail) {
+        return createRollResult(0, RollResultStatus.CRITICAL_FAILURE, dice);
     } else {
-        if (roll.total <= testValue) {
-            return createRollResultPromise(roll.total, RollResultStatus.SUCCESS, pool);
+        if (roll <= testValue) {
+            return createRollResult(roll, RollResultStatus.SUCCESS, dice);
         } else {
-            return createRollResultPromise(0, RollResultStatus.FAILURE, pool);
+            return createRollResult(0, RollResultStatus.FAILURE, dice);
         }
     }
 }
 
-async function rollCheckMultipleDice(testValue: number, rollOptions: RollOptions): Promise<RollResult> {
+function rollCheckMultipleDice(testValue: number, rollOptions: RollOptions): RollResult {
     const finalCheck = testValue % 20;
     const numberOfDice = Math.ceil(testValue / 20);
     const rolls = new Array<Roll>();
@@ -35,23 +54,22 @@ async function rollCheckMultipleDice(testValue: number, rollOptions: RollOptions
         roll.roll();
         rolls.concat(roll);
     }
-    const pool = new DicePool({ rolls: rolls });
+    const dice = rolls.map((r) => r.total);
 
-    const firstResult = rolls[1].total;
+    const firstResult = dice[1];
 
     // TODO: Special stuff (Gnomes!)
     if (firstResult == 20) {
-        createRollResultPromise(0, RollResultStatus.CRITICAL_FAILURE, pool);
+        createRollResult(0, RollResultStatus.CRITICAL_FAILURE, dice);
     }
 
-    const [otherRolls, critSuccesses] = pool.rolls
-        .partition((r) => r.total <= rollOptions.maxCritSucc)
-        .map((a) => a.sort((r1, r2) => r2.total - r1.total));
+    const [otherRolls, critSuccesses] = dice
+        .partition((r) => r <= rollOptions.maxCritSucc)
+        .map((a) => a.sort((r1, r2) => r2 - r1));
 
-    const sortedRolls: DicePool = new DicePool({ rolls: critSuccesses.concat(otherRolls) });
+    const sortedRollResults: Array<number> = critSuccesses.concat(otherRolls);
 
-    const evaluationResult = sortedRolls.rolls
-        .map((r) => r.total)
+    const evaluationResult = sortedRollResults
         .map((value, index) => {
             if (index == numberOfDice - 1) {
                 if (value == 1) {
@@ -69,15 +87,11 @@ async function rollCheckMultipleDice(testValue: number, rollOptions: RollOptions
         })
         .reduce((a, b) => a + b);
 
-    return createRollResultPromise(evaluationResult, RollResultStatus.SUCCESS, sortedRolls);
+    return createRollResult(evaluationResult, RollResultStatus.SUCCESS, sortedRollResults);
 }
 
-function createRollResultPromise(
-    totalValue: number,
-    rollResult: RollResultStatus,
-    dicePool: DicePool,
-): Promise<RollResult> {
-    return new Promise((resolve) => resolve(new RollResult(totalValue, RollResultStatus.SUCCESS, dicePool)));
+function createRollResult(totalValue: number, rollResult: RollResultStatus, dice: Array<number>): RollResult {
+    return new RollResult(totalValue, RollResultStatus.SUCCESS, dice);
 }
 
 export class RollOptions {
@@ -85,10 +99,10 @@ export class RollOptions {
 }
 
 export class RollResult {
-    constructor(public value: number, public status: RollResultStatus, public dice: DicePool) {}
+    constructor(public value: number, public status: RollResultStatus, public dice: Array<number>) {}
 }
 
-enum RollResultStatus {
+export enum RollResultStatus {
     FAILURE,
     SUCCESS,
     CRITICAL_FAILURE,
diff --git a/tsconfig.json b/tsconfig.json
index 9293923..64707c3 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -2,6 +2,6 @@
     "compilerOptions": {
         "target": "ES2017",
         "lib": ["DOM", "ES6", "ES2017"],
-        "types": ["foundry-pc-types"]
+        "types": ["foundry-pc-types", "jasmine"]
     }
 }