diff --git a/Cargo.toml b/Cargo.toml
index 7c2dd580509449adf89929ac7377ea7eab1528d2..1b71c2c14e02f7bc2757fb2f2f24d36e6ac9e3e6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,13 +7,16 @@ edition = "2018"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-wasm-bindgen = "0.2"
-js-sys = "0.3"
 log = "0.4.11"
+thiserror = "1.0.22"
+
+[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
+tokio-tungstenite = "0.12.0"
 
-[dependencies.web-sys]
-version = "0.3.22"
-features = [
+[target.'cfg(target_arch = "wasm32")'.dependencies]
+wasm-bindgen = "0.2"
+js-sys = "0.3"
+web-sys = { version = "0.3.22", features = [
   "BinaryType",
   "Blob",
   "ErrorEvent",
@@ -21,15 +24,13 @@ features = [
   "MessageEvent",
   "ProgressEvent",
   "WebSocket",
-]
+] }
 
 [dev-dependencies]
 console_log = "0.2.0"
 console_error_panic_hook = "0.1.6"
 wasm-bindgen-futures = "0.4.19"
-
-[dev-dependencies.web-sys]
-version = "0.3.22"
-features = [
+wasm-bindgen-test = "0.3"
+web-sys = { version = "0.3.22", features = [
   "Window",
-]
\ No newline at end of file
+] }
diff --git a/examples/event.rs b/examples/event.rs
index e7b92234c4419d53ad513d7b59070915b1f036fa..cbb529d0d825f2dfe53f4da405b689e2cbeb9067 100644
--- a/examples/event.rs
+++ b/examples/event.rs
@@ -2,10 +2,9 @@ use console_error_panic_hook;
 use console_log;
 use log::{error, info, Level};
 use std::panic;
-use wasm_bindgen::JsValue;
-use wasm_sockets;
+use wasm_sockets::{self, WebSocketError};
 
-fn main() -> Result<(), JsValue> {
+fn main() -> Result<(), WebSocketError> {
     panic::set_hook(Box::new(console_error_panic_hook::hook));
     // console_log and log macros are used instead of println!
     // so that messages can be seen in the browser console
diff --git a/examples/polling.rs b/examples/polling.rs
index e7eb6a8483aadada10a6bd6aad616862c87b4ef0..01f78a735245cb976beb74830e7d286770efee7e 100644
--- a/examples/polling.rs
+++ b/examples/polling.rs
@@ -3,10 +3,11 @@ use log::{info, Level};
 use std::cell::RefCell;
 use std::panic;
 use std::rc::Rc;
+#[cfg(target_arch = "wasm32")]
 use wasm_bindgen::prelude::*;
-use wasm_sockets::{self, ConnectionStatus};
+use wasm_sockets::{self, ConnectionStatus, WebSocketError};
 
-fn main() -> Result<(), JsValue> {
+fn main() -> Result<(), WebSocketError> {
     panic::set_hook(Box::new(console_error_panic_hook::hook));
     // console_log and log macros are used instead of println!
     // so that messages can be seen in the browser console
diff --git a/src/lib.rs b/src/lib.rs
index ac4c33b54d57c557278a77ff9c65d00e0bc153ac..c9b995a3fb2f76f43f18728c90193abaa0f5b39a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -5,10 +5,9 @@
 //! use console_log;
 //! use log::{error, info, Level};
 //! use std::panic;
-//! use wasm_bindgen::JsValue;
-//! use wasm_sockets;
+//! use wasm_sockets::{self, WebSocketError};
 //!
-//! fn main() -> Result<(), JsValue> {
+//! fn main() -> Result<(), WebSocketError> {
 //!     panic::set_hook(Box::new(console_error_panic_hook::hook));
 //!     // console_log and log macros are used instead of println!
 //!     // so that messages can be seen in the browser console
@@ -47,10 +46,11 @@
 //! use std::cell::RefCell;
 //! use std::panic;
 //! use std::rc::Rc;
+//! #[cfg(target_arch = "wasm32")]
 //! use wasm_bindgen::prelude::*;
-//! use wasm_sockets::{self, ConnectionStatus};
+//! use wasm_sockets::{self, ConnectionStatus, WebSocketError};
 //!
-//! fn main() -> Result<(), JsValue> {
+//! fn main() -> Result<(), WebSocketError> {
 //!     panic::set_hook(Box::new(console_error_panic_hook::hook));
 //!     // console_log and log macros are used instead of println!
 //!     // so that messages can be seen in the browser console
@@ -84,11 +84,15 @@
 //!     fn setInterval(closure: &Closure<dyn FnMut()>, time: u32) -> i32;
 //! }
 //! ```
-
+#[cfg(test)]
+mod tests;
 use log::trace;
 use std::cell::RefCell;
 use std::rc::Rc;
+use thiserror::Error;
+#[cfg(target_arch = "wasm32")]
 use wasm_bindgen::prelude::*;
+#[cfg(target_arch = "wasm32")]
 use wasm_bindgen::JsCast;
 use web_sys::{ErrorEvent, MessageEvent, WebSocket};
 
@@ -129,7 +133,7 @@ impl PollingClient {
     /// ```
     /// PollingClient::new("wss://echo.websocket.org")?;
     /// ```
-    pub fn new(url: &str) -> Result<Self, JsValue> {
+    pub fn new(url: &str) -> Result<Self, WebSocketError> {
         // Create connection
         let mut client = EventClient::new(url)?;
         let data = Rc::new(RefCell::new(vec![]));
@@ -195,11 +199,20 @@ impl PollingClient {
         self.event_client.send_binary(message)
     }
 }
+
+#[derive(Debug, Clone, Error)]
+pub enum WebSocketError {
+    #[error("Failed to create websocket connection: {0}")]
+    ConnectionCreationError(String),
+}
+
+#[cfg(target_arch = "wasm32")]
 pub struct EventClient {
     /// The URL this client is connected to
     pub url: Rc<RefCell<String>>,
-    /// The raw web_sys WebSocket object this client is using
-    pub connection: Rc<RefCell<web_sys::WebSocket>>,
+    /// The raw web_sys WebSocket object this client is using.
+    /// Be careful when using this field, as it will be a different type depending on the compilation target.
+    connection: Rc<RefCell<web_sys::WebSocket>>,
     /// The current connection status
     pub status: Rc<RefCell<ConnectionStatus>>,
     /// The function bound to the on_error event
@@ -211,6 +224,8 @@ pub struct EventClient {
     /// The function bound to the on_close event
     pub on_close: Rc<RefCell<Option<Box<dyn Fn() -> ()>>>>,
 }
+
+#[cfg(target_arch = "wasm32")]
 impl EventClient {
     /// Create a new EventClient and connect to a WebSocket URL
     ///
@@ -218,9 +233,14 @@ impl EventClient {
     /// ```
     /// EventClient::new("wss://echo.websocket.org")?;
     /// ```
-    pub fn new(url: &str) -> Result<Self, JsValue> {
+    pub fn new(url: &str) -> Result<Self, WebSocketError> {
         // Create connection
-        let ws: web_sys::WebSocket = WebSocket::new(url)?;
+        let ws: web_sys::WebSocket = match WebSocket::new(url) {
+            Ok(ws) => ws,
+            Err(e) => Err(WebSocketError::ConnectionCreationError(
+                e.as_string().unwrap(),
+            ))?,
+        };
         // For small binary messages, like CBOR, Arraybuffer is more efficient than Blob handling
         ws.set_binary_type(web_sys::BinaryType::Arraybuffer);
 
diff --git a/src/tests.rs b/src/tests.rs
new file mode 100644
index 0000000000000000000000000000000000000000..eb947e16d9b4ab41388b0b4395d501676b351da8
--- /dev/null
+++ b/src/tests.rs
@@ -0,0 +1,40 @@
+use wasm_bindgen_test::*;
+
+wasm_bindgen_test_configure!(run_in_browser);
+use crate as wasm_sockets;
+#[cfg(test)]
+use console_error_panic_hook;
+#[cfg(test)]
+use console_log;
+use log::{error, info, Level};
+use std::panic;
+use wasm_bindgen::JsValue;
+
+#[wasm_bindgen_test]
+fn event() {
+    panic::set_hook(Box::new(console_error_panic_hook::hook));
+    // console_log and log macros are used instead of println!
+    // so that messages can be seen in the browser console
+    console_log::init_with_level(Level::Trace).expect("Failed to enable logging");
+    info!("Creating connection");
+
+    let mut client = wasm_sockets::EventClient::new("wss://echo.websocket.org").unwrap();
+    client.set_on_error(Some(Box::new(|error| {
+        error!("{:#?}", error);
+    })));
+    client.set_on_connection(Some(Box::new(|client: &wasm_sockets::EventClient| {
+        info!("{:#?}", client.status);
+        info!("Sending message...");
+        client.send_string("Hello, World!").unwrap();
+        client.send_binary(vec![20]).unwrap();
+    })));
+    client.set_on_close(Some(Box::new(|| {
+        info!("Connection closed");
+    })));
+    client.set_on_message(Some(Box::new(
+        |client: &wasm_sockets::EventClient, message: wasm_sockets::Message| {
+            info!("New Message: {:#?}", message);
+        },
+    )));
+    info!("Connection successfully created");
+}