diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5c45689..d31d07d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+# 0.3.0
+
+- Added lifetime parameter to `HttpMethod`
+- Changed `HttpMethod::OTHER(&'static str)` to `HttpMethod::OTHER(Cow<'a, str>)`
+- Added unit tests
+- Converted one Into impl to From
+
 # 0.2.4
 
 - Update dependencies
diff --git a/Cargo.toml b/Cargo.toml
index f5a1314..282fe45 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "digest_auth"
-version = "0.2.4"
+version = "0.3.0"
 authors = ["Ondřej Hruška <ondra@ondrovo.com>"]
 edition = "2018"
 description = "Implementation of the Digest Auth algorithm as defined in IETF RFC 2069, 2617, and 7616, intended for HTTP clients"
@@ -20,3 +20,10 @@ hex = "0.4"
 sha2 = "0.9"
 md-5 = "0.9"
 digest = "0.9"
+
+[dependencies.http]
+version = "0.2.4"
+optional = true
+
+[features]
+default = []
diff --git a/src/enums.rs b/src/enums.rs
index 9cfedc8..6be2f7e 100644
--- a/src/enums.rs
+++ b/src/enums.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::upper_case_acronyms)]
+
 use crate::{Error, Error::*, Result};
 use std::fmt;
 use std::fmt::{Display, Formatter};
@@ -6,6 +8,7 @@ use std::str::FromStr;
 use digest::{Digest, DynDigest};
 use md5::Md5;
 use sha2::{Sha256, Sha512Trunc256};
+use std::borrow::Cow;
 
 /// Algorithm type
 #[derive(Debug, PartialEq, Clone, Copy)]
@@ -127,10 +130,9 @@ pub enum QopAlgo<'a> {
 }
 
 // casting back...
-impl<'a> Into<Option<Qop>> for QopAlgo<'a> {
-    /// Convert to ?Qop
-    fn into(self) -> Option<Qop> {
-        match self {
+impl<'a> From<QopAlgo<'a>> for Option<Qop> {
+    fn from(algo: QopAlgo<'a>) -> Self {
+        match algo {
             QopAlgo::NONE => None,
             QopAlgo::AUTH => Some(Qop::AUTH),
             QopAlgo::AUTH_INT(_) => Some(Qop::AUTH_INT),
@@ -167,12 +169,12 @@ impl Display for Charset {
 }
 
 /// HTTP method (used when generating the response hash for some Qop options)
-#[derive(Debug)]
+#[derive(Debug, PartialEq, Clone)]
 pub enum HttpMethod<'a> {
     GET,
     POST,
     HEAD,
-    OTHER(&'a str),
+    OTHER(Cow<'a, str>),
 }
 
 impl<'a> Default for HttpMethod<'a> {
@@ -192,3 +194,234 @@ impl<'a> Display for HttpMethod<'a> {
         })
     }
 }
+
+impl<'a> From<&'a str> for HttpMethod<'a> {
+    fn from(s: &'a str) -> Self {
+        match s {
+            "GET" => HttpMethod::GET,
+            "POST" => HttpMethod::POST,
+            "HEAD" => HttpMethod::HEAD,
+            s => HttpMethod::OTHER(Cow::Borrowed(s)),
+        }
+    }
+}
+
+impl<'a> From<&'a [u8]> for HttpMethod<'a> {
+    fn from(s: &'a [u8]) -> Self {
+        String::from_utf8_lossy(s).into()
+    }
+}
+
+impl<'a> From<String> for HttpMethod<'a> {
+    fn from(s: String) -> Self {
+        match &s[..] {
+            "GET" => HttpMethod::GET,
+            "POST" => HttpMethod::POST,
+            "HEAD" => HttpMethod::HEAD,
+            _ => HttpMethod::OTHER(Cow::Owned(s)),
+        }
+    }
+}
+
+impl<'a> From<Cow<'a, str>> for HttpMethod<'a> {
+    fn from(s: Cow<'a, str>) -> Self {
+        match &s[..] {
+            "GET" => HttpMethod::GET,
+            "POST" => HttpMethod::POST,
+            "HEAD" => HttpMethod::HEAD,
+            _ => HttpMethod::OTHER(s),
+        }
+    }
+}
+
+#[cfg(feature = "http")]
+impl From<http::Method> for HttpMethod<'static> {
+    fn from(method: http::Method) -> Self {
+        match method {
+            http::Method::GET => HttpMethod::GET,
+            http::Method::POST => HttpMethod::POST,
+            http::Method::HEAD => HttpMethod::HEAD,
+            other => HttpMethod::OTHER(other.to_string().into()),
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::error::Error::{BadCharset, BadQop, UnknownAlgorithm};
+    use crate::{Algorithm, AlgorithmType, Charset, HttpMethod, Qop, QopAlgo};
+    use std::borrow::Cow;
+    use std::str::FromStr;
+
+    #[test]
+    fn test_algorithm_type() {
+        // String parsing
+        assert_eq!(
+            Ok(Algorithm::new(AlgorithmType::MD5, false)),
+            Algorithm::from_str("MD5")
+        );
+        assert_eq!(
+            Ok(Algorithm::new(AlgorithmType::MD5, true)),
+            Algorithm::from_str("MD5-sess")
+        );
+        assert_eq!(
+            Ok(Algorithm::new(AlgorithmType::SHA2_256, false)),
+            Algorithm::from_str("SHA-256")
+        );
+        assert_eq!(
+            Ok(Algorithm::new(AlgorithmType::SHA2_256, true)),
+            Algorithm::from_str("SHA-256-sess")
+        );
+        assert_eq!(
+            Ok(Algorithm::new(AlgorithmType::SHA2_512_256, false)),
+            Algorithm::from_str("SHA-512-256")
+        );
+        assert_eq!(
+            Ok(Algorithm::new(AlgorithmType::SHA2_512_256, true)),
+            Algorithm::from_str("SHA-512-256-sess")
+        );
+        assert_eq!(
+            Err(UnknownAlgorithm("OTHER_ALGORITHM".to_string())),
+            Algorithm::from_str("OTHER_ALGORITHM")
+        );
+
+        // String building
+        assert_eq!(
+            "MD5".to_string(),
+            Algorithm::new(AlgorithmType::MD5, false).to_string()
+        );
+        assert_eq!(
+            "MD5-sess".to_string(),
+            Algorithm::new(AlgorithmType::MD5, true).to_string()
+        );
+        assert_eq!(
+            "SHA-256".to_string(),
+            Algorithm::new(AlgorithmType::SHA2_256, false).to_string()
+        );
+        assert_eq!(
+            "SHA-256-sess".to_string(),
+            Algorithm::new(AlgorithmType::SHA2_256, true).to_string()
+        );
+        assert_eq!(
+            "SHA-512-256".to_string(),
+            Algorithm::new(AlgorithmType::SHA2_512_256, false).to_string()
+        );
+        assert_eq!(
+            "SHA-512-256-sess".to_string(),
+            Algorithm::new(AlgorithmType::SHA2_512_256, true).to_string()
+        );
+
+        // Default
+        assert_eq!(
+            Algorithm::new(AlgorithmType::MD5, false),
+            Default::default()
+        );
+
+        // Hash calculation
+        assert_eq!(
+            "e2fc714c4727ee9395f324cd2e7f331f".to_string(),
+            Algorithm::new(AlgorithmType::MD5, false).hash("abcd".as_bytes())
+        );
+
+        assert_eq!(
+            "e2fc714c4727ee9395f324cd2e7f331f".to_string(),
+            Algorithm::new(AlgorithmType::MD5, false).hash_str("abcd")
+        );
+
+        assert_eq!(
+            "88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589".to_string(),
+            Algorithm::new(AlgorithmType::SHA2_256, false).hash("abcd".as_bytes())
+        );
+
+        assert_eq!(
+            "d2891c7978be0e24948f37caa415b87cb5cbe2b26b7bad9dc6391b8a6f6ddcc9".to_string(),
+            Algorithm::new(AlgorithmType::SHA2_512_256, false).hash("abcd".as_bytes())
+        );
+    }
+
+    #[test]
+    fn test_qop() {
+        assert_eq!(Ok(Qop::AUTH), Qop::from_str("auth"));
+        assert_eq!(Ok(Qop::AUTH_INT), Qop::from_str("auth-int"));
+        assert_eq!(Err(BadQop("banana".to_string())), Qop::from_str("banana"));
+
+        assert_eq!("auth".to_string(), Qop::AUTH.to_string());
+        assert_eq!("auth-int".to_string(), Qop::AUTH_INT.to_string());
+    }
+
+    #[test]
+    fn test_qop_algo() {
+        assert_eq!(Option::<Qop>::None, QopAlgo::NONE.into());
+        assert_eq!(Some(Qop::AUTH), QopAlgo::AUTH.into());
+        assert_eq!(
+            Some(Qop::AUTH_INT),
+            QopAlgo::AUTH_INT("foo".as_bytes()).into()
+        );
+    }
+
+    #[test]
+    fn test_charset() {
+        assert_eq!(Ok(Charset::UTF8), Charset::from_str("UTF-8"));
+        assert_eq!(Err(BadCharset("ASCII".into())), Charset::from_str("ASCII"));
+
+        assert_eq!("UTF-8".to_string(), Charset::UTF8.to_string());
+        assert_eq!("ASCII".to_string(), Charset::ASCII.to_string());
+    }
+
+    #[test]
+    fn test_http_method() {
+        // Well known 'static
+        assert_eq!(HttpMethod::GET, "GET".into());
+        assert_eq!(HttpMethod::POST, "POST".into());
+        assert_eq!(HttpMethod::HEAD, "HEAD".into());
+        // As bytes
+        assert_eq!(HttpMethod::GET, "GET".as_bytes().into());
+        assert_eq!(
+            HttpMethod::OTHER(Cow::Borrowed("ěščř")),
+            "ěščř".as_bytes().into()
+        );
+        assert_eq!(
+            HttpMethod::OTHER(Cow::Owned("AB�".to_string())),
+            (&[65u8, 66, 156][..]).into()
+        );
+        // Well known String
+        assert_eq!(HttpMethod::GET, String::from("GET").into());
+        // Custom String
+        assert_eq!(
+            HttpMethod::OTHER(Cow::Borrowed("NonsenseMethod")),
+            "NonsenseMethod".into()
+        );
+        assert_eq!(
+            HttpMethod::OTHER(Cow::Owned("NonsenseMethod".to_string())),
+            "NonsenseMethod".to_string().into()
+        );
+        // Custom Cow
+        assert_eq!(HttpMethod::HEAD, Cow::Borrowed("HEAD").into());
+        assert_eq!(
+            HttpMethod::OTHER(Cow::Borrowed("NonsenseMethod")),
+            Cow::Borrowed("NonsenseMethod").into()
+        );
+        // to string
+        assert_eq!("GET".to_string(), HttpMethod::GET.to_string());
+        assert_eq!("POST".to_string(), HttpMethod::POST.to_string());
+        assert_eq!("HEAD".to_string(), HttpMethod::HEAD.to_string());
+        assert_eq!(
+            "NonsenseMethod".to_string(),
+            HttpMethod::OTHER(Cow::Borrowed("NonsenseMethod")).to_string()
+        );
+        assert_eq!(
+            "NonsenseMethod".to_string(),
+            HttpMethod::OTHER(Cow::Owned("NonsenseMethod".to_string())).to_string()
+        );
+    }
+
+    #[cfg(feature = "http")]
+    #[test]
+    fn test_http_crate() {
+        assert_eq!(HttpMethod::GET, http::Method::GET.into());
+        assert_eq!(
+            HttpMethod::OTHER(Cow::Owned("BANANA".to_string())),
+            http::Method::from_str("BANANA").unwrap().into()
+        );
+    }
+}
diff --git a/src/error.rs b/src/error.rs
index 06457fc..e3b5aad 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,7 +1,7 @@
 use std::fmt::{self, Display, Formatter};
 use std::result;
 
-#[derive(Debug)]
+#[derive(Debug, PartialEq)]
 pub enum Error {
     BadCharset(String),
     UnknownAlgorithm(String),
diff --git a/src/lib.rs b/src/lib.rs
index 371453b..2e7bb20 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -62,9 +62,13 @@ pub fn parse(www_authorize: &str) -> Result<WwwAuthenticateHeader> {
     WwwAuthenticateHeader::parse(www_authorize)
 }
 
-#[test]
-fn test_parse_respond() {
-    let src = r#"
+#[cfg(test)]
+mod test {
+    use crate::{AuthContext, Error};
+
+    #[test]
+    fn test_parse_respond() {
+        let src = r#"
     Digest
        realm="http-auth@example.org",
        qop="auth, auth-int",
@@ -73,17 +77,17 @@ fn test_parse_respond() {
        opaque="FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS"
     "#;
 
-    let mut context = AuthContext::new("Mufasa", "Circle of Life", "/dir/index.html");
-    context.set_custom_cnonce("f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ");
+        let mut context = AuthContext::new("Mufasa", "Circle of Life", "/dir/index.html");
+        context.set_custom_cnonce("f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ");
 
-    let mut prompt = crate::parse(src).unwrap();
-    let answer = prompt.respond(&context).unwrap();
+        let mut prompt = crate::parse(src).unwrap();
+        let answer = prompt.respond(&context).unwrap();
 
-    let str = answer.to_string().replace(", ", ",\n  ");
+        let str = answer.to_string().replace(", ", ",\n  ");
 
-    assert_eq!(
-        str,
-        r#"
+        assert_eq!(
+            str,
+            r#"
 Digest username="Mufasa",
   realm="http-auth@example.org",
   nonce="7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v",
@@ -95,11 +99,12 @@ Digest username="Mufasa",
   opaque="FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS",
   algorithm=MD5
 "#
-        .trim()
-    );
-}
+            .trim()
+        );
+    }
 
-#[test]
-fn test_cast_error() {
-    let _m: Box<dyn std::error::Error> = Error::UnknownAlgorithm("Uhhh".into()).into();
+    #[test]
+    fn test_cast_error() {
+        let _m: Box<dyn std::error::Error> = Error::UnknownAlgorithm("Uhhh".into()).into();
+    }
 }