diff --git a/CHANGELOG.md b/CHANGELOG.md
index 96980f6ac448c5f85608076df4dcdda6dad91e2d..44e9edb0c03179f7b740df112e10f3f34591d8e9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
 and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
 
+## [Unreleased]
+
+### Added
+- [DEV] Initial test scaffolding
+
 ## [0.2.0]
 
 ### Added
diff --git a/Cargo.lock b/Cargo.lock
index 1ed2edd6b716fcad485fe45ec5979810872714df..b084fa9e4b12cfd4b2fc6d40cf3350029df31ddd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -235,6 +235,11 @@ dependencies = [
  "unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "interpolate_idents"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "isatty"
 version = "0.1.7"
@@ -639,6 +644,7 @@ dependencies = [
  "docopt 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "formdata 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "interpolate_idents 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "rhai 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "rocket 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -858,6 +864,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2f407128745b78abc95c0ffbe4e5d37427fdc0d45470710cfef8c44522a2e37"
 "checksum hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)" = "368cb56b2740ebf4230520e2b90ebb0461e69034d85d1945febd9b3971426db2"
 "checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d"
+"checksum interpolate_idents 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c038526b1556151b78f71b3e4cb107cf58c4dfc426a64a398c61f76a42a4e08"
 "checksum isatty 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a118a53ba42790ef25c82bb481ecf36e2da892646cccd361e69a6bb881e19398"
 "checksum itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c069bbec61e1ca5a596166e55dfe4773ff745c3d16b700013bcaff9a6df2c682"
 "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
diff --git a/Cargo.toml b/Cargo.toml
index f181189886d045f97ae2617cb6e62dd371954fc7..f92735d5f80859c70b91584235cf589435caeec0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -37,3 +37,6 @@ hyper = "0.10"
 rand = "0.3"
 rhai = "0.7"
 serde_yaml = "0.7.3"
+
+[dev-dependencies]
+interpolate_idents = "0.2.4"
diff --git a/example/files/adorable-puppy.jpg b/example/files/adorable-puppy.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..caa08afbae4082a0efccfa24060f06485d8009e9
Binary files /dev/null and b/example/files/adorable-puppy.jpg differ
diff --git a/example/files/attribution.csv b/example/files/attribution.csv
new file mode 100644
index 0000000000000000000000000000000000000000..66bd3e19ebd286eef77994042c0b72185eaf4cfa
--- /dev/null
+++ b/example/files/attribution.csv
@@ -0,0 +1,3 @@
+file,author/attribution, link
+adorable-puppy.jpg,Photo by mentatdgt from Pexels,https://www.pexels.com/photo/white-brown-and-black-shih-tzu-puppy-936317/
+math.aswm,Github: Hanks10100,https://github.com/Hanks10100/wasm-examples
diff --git a/example/files/math.wasm b/example/files/math.wasm
new file mode 100644
index 0000000000000000000000000000000000000000..4140e01798748e33698b7f0eb0b0ef8c0bcc52df
Binary files /dev/null and b/example/files/math.wasm differ
diff --git a/example/index.html b/example/index.html
index 24ac4ba12acf44383c15860708aaaecbe5a2d922..080c0fc49cfb6a8e4ab7847645034944714928e4 100644
--- a/example/index.html
+++ b/example/index.html
@@ -3,10 +3,11 @@
 	<head>
 		<meta charset="UTF-8">
 		<title>Swerve Example</title>
-		<link rel="stylesheet" href="/css/styles.css">
+		<link rel="stylesheet" href="css/styles.css">
 	</head>
 	<body>
 		<h1>It's Swervin' Time</h1>
 		<p>This page is part of the swerve example, and includes a stylesheet and stuff.</p>
+		<script async src="js/say_hello.js"></script>
 	</body>
 </html>
\ No newline at end of file
diff --git a/example/js/say_hello.js b/example/js/say_hello.js
new file mode 100644
index 0000000000000000000000000000000000000000..d0e38b2bfa1dd85d51824d730d50606e193c6c01
--- /dev/null
+++ b/example/js/say_hello.js
@@ -0,0 +1,3 @@
+document.addEventListener('DOMContentLoaded', function() {
+	console.log("The dom has loaded!")
+})
\ No newline at end of file
diff --git a/src/cli/cli.rs b/src/cli/cli.rs
index 578dc8c79d2e00028b89073ce3cd6c361c8ca533..f2272d2e0012db5ced535db6f556b78ff3f9079a 100644
--- a/src/cli/cli.rs
+++ b/src/cli/cli.rs
@@ -1,5 +1,6 @@
 pub use std::path::PathBuf;
 pub use std::env::current_dir;
+pub use std::default::Default;
 
 pub const USAGE: &'static str = "
 Static file swerver and api mocker for local development. 
@@ -21,7 +22,7 @@ Web Server Options:
     -c=<path>, --config=<path>       Path to the .swerve config file
     -t=<num>, --threads=<num>        Number of worker threads to use for serving files; defaults to 32
 
-Data Handling Options
+Data Handling Options:
     -u, --upload                     Support file uploads to '/upload'
     -U=<path>, --upload-path=<path>  Set the url path that will accept file uploads. Implies 'upload' flag if not present
 
@@ -54,4 +55,22 @@ impl Args {
             ).to_string_lossy().into_owned())
         )
     }
+}
+
+impl Default for Args {
+	fn default() -> Self {
+		Args {
+			flag_dir: Some(String::from("")),
+			flag_port: Some(8000),
+			flag_config: None,
+			flag_threads: Some(32),
+			flag_address: Some(String::from("localhost")),
+			flag_help: false,
+			flag_quiet: false,
+			flag_no_index: false,
+			flag_upload: false,
+			flag_upload_path: None,
+			flag_license: false,
+		}
+	}
 }
\ No newline at end of file
diff --git a/src/cli/config_file.rs b/src/cli/config_file.rs
index 09e088fa23f7569ed865d94b76c33758726a02f5..7723ae5209ec491e3939a6f9b0c46116881003be 100644
--- a/src/cli/config_file.rs
+++ b/src/cli/config_file.rs
@@ -3,9 +3,8 @@ use std::convert::AsRef;
 use std::io::prelude::*;
 use std::io;
 use std::fs::File;
-use std::io::BufReader;
 use std::default::Default;
-use serde::{Deserialize, Deserializer, de::{self, Error}};
+use serde::{Deserialize, Deserializer, de};
 use std::fmt;
 use serde_yaml as yaml;
 
diff --git a/src/lib.rs b/src/lib.rs
index 26cb6505f0715e0b53800fd011be5f3a08d27f17..d384507b3f4e2159bf5c86a41948d4061d515628 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -13,4 +13,5 @@ extern crate rand;
 
 pub mod cli;
 pub mod routing;
-pub mod scripting;
\ No newline at end of file
+pub mod scripting;
+pub mod server;
diff --git a/src/main.rs b/src/main.rs
index 13e981d28cee3881a790339766f6c4c399fcbeba..832dc7725849cf292ae07853e1bc77283a2d9158 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -8,98 +8,10 @@ extern crate docopt;
 extern crate swerve;
 extern crate rhai;
 
-use rhai::Engine;
-
-use std::{path, process, io};
-use std::fs::{self, File};
+use std::process;
 use docopt::Docopt;
-use rocket::response::NamedFile;
-use rocket::http::ContentType;
-use rocket::{Response, Request};
 use swerve::cli;
-use swerve::routing;
-use std::io::BufReader;
-use std::path::{Path, PathBuf};
-use rocket::response::Responder;
-
-struct TypedFile {
-    file: File,
-    content_type: Option<ContentType>,
-    path: PathBuf,
-}
-
-impl TypedFile {
-    pub fn open<P: AsRef<Path>>(path: P, content_type: Option<rocket::http::ContentType>) -> TypedFile {
-        let file = File::open(path.as_ref()).unwrap();
-        TypedFile { file, content_type, path: (*path.as_ref()).to_path_buf() }
-    }
-}
-
-impl rocket::response::Responder<'static> for TypedFile {
-    fn respond_to(self, _: &Request) -> Result<Response<'static>, rocket::http::Status> {
-        let mut response = Response::new();
-        if let Some(content_type) = self.content_type {
-            response.set_header(content_type);
-        } else {
-            response.set_header(ContentType::from_extension(&self.path.extension().unwrap().to_string_lossy()).unwrap());
-        }
-        response.set_streamed_body(BufReader::new(self.file));
-        Ok(response)
-    }
-}
-
-#[get("/")]
-fn serve_root(args: rocket::State<cli::Args>) -> Option<TypedFile> {
-    serve_files(None, args)
-}
-
-#[get("/<file..>")]
-fn serve_files(file: Option<path::PathBuf>, args: rocket::State<cli::Args>) -> Option<TypedFile> {
-    let stub = match file {
-        Some(path) => path,
-        None => path::PathBuf::from(""),
-    };
-
-    let path = args.get_dir().join(stub);
-
-    let meta = match fs::metadata(&path) {
-        Ok(metadata) => metadata,
-        _ => return None,
-    };
-
-    if meta.is_dir() && !args.flag_no_index {
-        Some(TypedFile::open(path.join("index.html"), None))
-    } else {
-        if &path.extension().unwrap().to_string_lossy() == "wasm" {
-           Some( TypedFile::open(path, Some(ContentType::new("application", "wasm"))))
-        } else {
-            Some(TypedFile::open(path, None))
-        }
-    }
-}
-
-fn config_from_args(args: cli::Args, config: cli::SwerveConfig) -> rocket::Config {
-    let mut builder = rocket::Config::build(rocket::config::Environment::Development);
-    if let Some(threads) = args.flag_threads {
-        builder = builder.workers(threads);
-    } else {
-        builder = builder.workers(config.server.threads);
-    }
-
-    if let Some(port) = args.flag_port {
-        builder = builder.port(port);
-    } else {
-        builder = builder.port(config.server.port);
-    }
-
-    if let Some(address) = args.flag_address {
-        builder = builder.address(address);
-    } else {
-        builder = builder.address(config.server.address);
-    }
-
-    builder.finalize().unwrap()
-}
+use swerve::server;
 
 fn main() {
     let args: cli::Args = Docopt::new(cli::USAGE)
@@ -107,18 +19,8 @@ fn main() {
         .unwrap_or_else(|e| e.exit());
 
     let is_quiet = args.flag_quiet;
-    macro_rules! printq {
-        ($( $x:expr ),+) => {
-            {
-                if !is_quiet {
-                    println!($($x),*);
-                }
-            }
-        }
-    }
-
     if args.flag_help {
-        printq!("{}", cli::USAGE);
+        if !is_quiet { println!("{}", cli::USAGE); }
         process::exit(0);
     }
 
@@ -133,38 +35,6 @@ fn main() {
         std::process::exit(2);
     });
 
-    let server_config = config_from_args(args.clone(), swerve_config.clone());
-    // printq!("{:?}", swerve_config);
-    printq!("");
-
-    let mut server = rocket::custom(server_config, false)
-        .manage(args.clone())
-        .manage(swerve_config);
-
-    if let Some(ref upload_path) = args.flag_upload_path {
-        printq!("[SETUP] Accepting uploads at {}", upload_path);
-        server = server.mount(upload_path, routes![swerve::routing::mock_upload::to_file]);
-    } else if args.flag_upload {
-        printq!("[SETUP] Accepting uploads at /upload");
-        server = server.mount("/upload", routes![swerve::routing::mock_upload::to_file]);
-    }
-    server = server.mount("/", routes![
-		serve_root,
-		serve_files,
-		routing::scripting::route_script
-	]);
-
-    if !args.flag_quiet {
-        server = server.attach(rocket::fairing::AdHoc::on_launch(move |rckt| {
-            let config = rckt.config();
-            println!("[SETUP] Swerve is configured with {} worker threads", config.workers);
-            println!("[SETUP] Swerving files from http://{}:{}\n", config.address, config.port);
-        }))
-        .attach(rocket::fairing::AdHoc::on_response(|req, _res| {
-            println!("[REQUEST] {} {}", req.method(), req.uri());
-        }));
-    }
-    {
-        server.launch();
-    }
+	let server = server::create_server(args.clone(), swerve_config.clone());
+	server.launch();
 }
\ No newline at end of file
diff --git a/src/routing/core.rs b/src/routing/core.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a847d1dae81df0f8bd6342ebe0c12ec47bf94fea
--- /dev/null
+++ b/src/routing/core.rs
@@ -0,0 +1,35 @@
+use cli;
+use std::fs;
+use std::path;
+use rocket::{self, http::ContentType};
+use routing::request::TypedFile;
+
+#[get("/")]
+fn serve_root(args: rocket::State<cli::Args>) -> Option<TypedFile> {
+    serve_files(None, args)
+}
+
+#[get("/<file..>")]
+fn serve_files(file: Option<path::PathBuf>, args: rocket::State<cli::Args>) -> Option<TypedFile> {
+    let stub = match file {
+        Some(path) => path,
+        None => path::PathBuf::from(""),
+    };
+
+    let path = args.get_dir().join(stub);
+
+    let meta = match fs::metadata(&path) {
+        Ok(metadata) => metadata,
+        _ => return None,
+    };
+
+    if meta.is_dir() && !args.flag_no_index {
+        Some(TypedFile::open(path.join("index.html"), None))
+    } else {
+        if &path.extension().unwrap().to_string_lossy() == "wasm" {
+           Some( TypedFile::open(path, Some(ContentType::new("application", "wasm"))))
+        } else {
+            Some(TypedFile::open(path, None))
+        }
+    }
+}
diff --git a/src/routing/mod.rs b/src/routing/mod.rs
index b7054b18f5f05411ac7ed75c68e51f918937b6ec..d3fa15ff5030dccc4162756be7c3e0f264472775 100644
--- a/src/routing/mod.rs
+++ b/src/routing/mod.rs
@@ -1,3 +1,4 @@
 pub mod mock_upload;
 pub mod request;
-pub mod scripting;
\ No newline at end of file
+pub mod scripting;
+pub mod core;
diff --git a/src/routing/request.rs b/src/routing/request.rs
index 0ebd52b826307a61b380ed6d630983a8a6d084a4..643abe5ddd56afc24fc01c5970e9bc72b5ea768c 100644
--- a/src/routing/request.rs
+++ b/src/routing/request.rs
@@ -1,6 +1,10 @@
+use rocket::{self, Outcome, http, Response};
 use rocket::request::{FromRequest, Request};
-use rocket::{Outcome, http};
+use rocket::http::ContentType;
 use hyper::header::Headers;
+use std::path::{Path, PathBuf};
+use std::io::BufReader;
+use std::fs::File;
 
 #[derive(Debug)]
 pub struct ConvertedHeaders {
@@ -26,4 +30,30 @@ impl <'a, 'req>FromRequest<'a, 'req> for ConvertedHeaders {
             inner: hyper_headers
         })
     }
-}
\ No newline at end of file
+}
+
+pub struct TypedFile {
+    file: File,
+    content_type: Option<ContentType>,
+    path: PathBuf,
+}
+
+impl TypedFile {
+    pub fn open<P: AsRef<Path>>(path: P, content_type: Option<rocket::http::ContentType>) -> TypedFile {
+        let file = File::open(path.as_ref()).unwrap();
+        TypedFile { file, content_type, path: (*path.as_ref()).to_path_buf() }
+    }
+}
+
+impl rocket::response::Responder<'static> for TypedFile {
+    fn respond_to(self, _: &Request) -> Result<Response<'static>, rocket::http::Status> {
+        let mut response = Response::new();
+        if let Some(content_type) = self.content_type {
+            response.set_header(content_type);
+        } else {
+            response.set_header(ContentType::from_extension(&self.path.extension().unwrap().to_string_lossy()).unwrap());
+        }
+        response.set_streamed_body(BufReader::new(self.file));
+        Ok(response)
+    }
+}
diff --git a/src/server/mod.rs b/src/server/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..1604eface9ba9609f408325378e3fa55eac7927d
--- /dev/null
+++ b/src/server/mod.rs
@@ -0,0 +1,3 @@
+mod server;
+
+pub use self::server::create_server;
\ No newline at end of file
diff --git a/src/server/server.rs b/src/server/server.rs
new file mode 100644
index 0000000000000000000000000000000000000000..37a1b9453d9ddb28ee397b4b369509eb4c239f63
--- /dev/null
+++ b/src/server/server.rs
@@ -0,0 +1,63 @@
+use rocket::{self, Rocket, Config};
+use cli::{Args, SwerveConfig};
+use routing;
+
+pub fn create_server(args: Args, config: SwerveConfig) -> Rocket {
+	let server_config = server_config_from_input(args.clone(), config.clone());
+	let mut server = Rocket::custom(server_config, false)
+		.manage(args.clone())
+		.manage(config.clone());
+
+	let quiet = args.flag_quiet;
+
+    if let Some(ref upload_path) = args.flag_upload_path {
+        if !quiet { println!("[SETUP] Accepting uploads at {}", upload_path) }
+        server = server.mount(upload_path, routes![routing::mock_upload::to_file]);
+    } else if args.flag_upload {
+        if !quiet { println!("[SETUP] Accepting uploads at /upload") }
+        server = server.mount("/upload", routes![routing::mock_upload::to_file]);
+    }
+
+	server = server.mount("/", routes![
+		routing::core::serve_root,
+		routing::core::serve_files,
+		routing::scripting::route_script
+	]);
+
+
+	if !quiet {
+		server = server.attach(rocket::fairing::AdHoc::on_launch(move |rckt| {
+			let conf = rckt.config();
+			println!("[SETUP] Swerve is configured with {} worker threads", conf.workers);
+			println!("[SETUP] Swerving files from http://{}:{}\n", conf.address, conf.port);
+		}))
+		.attach(rocket::fairing::AdHoc::on_response(|req, _res| {
+			println!("[REQUEST] {} {}", req.method(), req.uri());
+		}));
+	}
+
+	server
+}
+
+fn server_config_from_input(args: Args, config: SwerveConfig) -> Config {
+    let mut builder = Config::build(rocket::config::Environment::Development);
+    if let Some(threads) = args.flag_threads {
+        builder = builder.workers(threads);
+    } else {
+        builder = builder.workers(config.server.threads);
+    }
+
+    if let Some(port) = args.flag_port {
+        builder = builder.port(port);
+    } else {
+        builder = builder.port(config.server.port);
+    }
+
+    if let Some(address) = args.flag_address {
+        builder = builder.address(address);
+    } else {
+        builder = builder.address(config.server.address);
+    }
+
+    builder.finalize().unwrap()
+}
\ No newline at end of file
diff --git a/tests/basic_operations.rs b/tests/basic_operations.rs
new file mode 100644
index 0000000000000000000000000000000000000000..b98a39a44ccf09c06ac705802eea02fa8affaf9e
--- /dev/null
+++ b/tests/basic_operations.rs
@@ -0,0 +1,28 @@
+
+extern crate rocket;
+extern crate swerve;
+
+use swerve::cli::{Args, SwerveConfig};
+use swerve::server::create_server;
+
+use rocket::local::Client;
+use rocket::http::Status;
+
+const INDEX_PAGE: &'static str = include_str!("../example/index.html");
+
+#[test]
+fn test_serves_index() {
+	let args = Args {
+		flag_dir: Some(String::from("example")),
+		..Args::default()
+	};
+	let config = SwerveConfig::default();
+
+	let server = create_server(args, config);
+
+	let client = Client::new(server).expect("valid server instance");
+	let mut response = client.get("/").dispatch();
+
+	assert_eq!(response.status(), Status::Ok);
+	assert_eq!(response.body_string(), Some(INDEX_PAGE.into()));
+}
\ No newline at end of file
diff --git a/tests/content_types.rs b/tests/content_types.rs
new file mode 100644
index 0000000000000000000000000000000000000000..c87a97191b0009fb507996fb9904a8717f126815
--- /dev/null
+++ b/tests/content_types.rs
@@ -0,0 +1,56 @@
+#![feature(plugin)]
+#![plugin(interpolate_idents)]
+
+extern crate rocket;
+extern crate swerve;
+
+use swerve::cli::{Args, SwerveConfig};
+use swerve::server::create_server;
+
+use rocket::local::Client;
+use rocket::http::ContentType;
+
+macro_rules! test_type {
+    ($name:ident, $path:expr, $content_path:expr) => (interpolate_idents! {
+        #[test]
+        fn [returns_some_type_for_ $name]() {
+            let args = Args {
+                flag_dir: Some(String::from("example")),
+                flag_quiet: true,
+                ..Args::default()
+            };
+            let config = SwerveConfig::default();
+
+            let server = create_server(args, config);
+            let client = Client::new(server).expect("valid server instance");
+            let response = client.get($path).dispatch();
+
+            assert_eq!(response.content_type(), Some($content_path));
+        }
+    });
+
+    ($name:ident, $path:expr) => (interpolate_idents! {
+        #[test]
+        fn [returns_no_type_for_ $name]() {
+            let args = Args {
+                flag_dir: Some(String::from("example")),
+                flag_quiet: true,
+                ..Args::default()
+            };
+            let config = SwerveConfig::default();
+
+            let server = create_server(args, config);
+            let client = Client::new(server).expect("valid server instance");
+            let response = client.get($path).dispatch();
+
+            assert_eq!(response.content_type(), None);
+        }
+    });
+}
+
+test_type!(html, "/index.html", ContentType::HTML);
+test_type!(css, "/css/styles.css", ContentType::CSS);
+test_type!(javascript, "/js/say_hello.js", ContentType::JavaScript);
+test_type!(csv, "/files/attribution.csv", ContentType::CSV);
+test_type!(image_jpeg, "/files/adorable-puppy.jpg", ContentType::JPEG);
+test_type!(web_assembly, "/files/math.wasm", ContentType::new("application", "wasm"));