diff --git a/examples/array_methods.ion b/examples/array_methods.ion
new file mode 100644
index 0000000000000000000000000000000000000000..5d2b36458d35d019be1f63ec9ad5628716226aeb
--- /dev/null
+++ b/examples/array_methods.ion
@@ -0,0 +1,5 @@
+echo @split("onetwoone", "two")
+echo @split_at("onetwoone", "3")
+echo @graphemes("onetwo", "3")
+echo @bytes("onetwo")
+echo @chars("onetwo")
diff --git a/examples/array_methods.out b/examples/array_methods.out
new file mode 100644
index 0000000000000000000000000000000000000000..e692ef7b395af9c5c458eac7fb361187fff89ac2
--- /dev/null
+++ b/examples/array_methods.out
@@ -0,0 +1,5 @@
+one one
+one twoone
+o n e t w o
+111 110 101 116 119 111
+o n e t w o
diff --git a/examples/run_examples.sh b/examples/run_examples.sh
index a10e9f17e5611c84537c05ebd4945042804a07eb..2a644419ff81a074d88e6deb981ca85a20370029 100755
--- a/examples/run_examples.sh
+++ b/examples/run_examples.sh
@@ -32,7 +32,7 @@ function check_return_value {
     $PROJECT_DIR/target/debug/ion $1 1> $EXAMPLES_DIR/tmp.out 2> /dev/null
 
     # Compare real and expected output
-    cmp --silent $EXAMPLES_DIR/tmp.out $EXPECTED_OUTPUT_FILE
+    diff "$EXAMPLES_DIR"/tmp.out "$EXPECTED_OUTPUT_FILE" > "$EXAMPLES_DIR"/diff_tmp
     local RET=$?
 
     # Clean up the mess
@@ -40,9 +40,12 @@ function check_return_value {
 
     # Write result
     if [[ $RET -ne 0 ]]; then
+        cat "$EXAMPLES_DIR"/diff_tmp
+        rm "$EXAMPLES_DIR"/diff_tmp
         echo -e "Test ${1} ${TAGFAIL}";
         return 1;
     else
+        rm "$EXAMPLES_DIR"/diff_tmp
         echo -e "Test ${1} ${TAGPASS}";
         return 0;
     fi
diff --git a/src/parser/shell_expand/words/methods/arrays.rs b/src/parser/shell_expand/words/methods/arrays.rs
index 002dabbcfeb6c93120440d8063a5fbd4570d6f03..3c68c5adf44457209b2d388ebc1c1fd316419a1e 100644
--- a/src/parser/shell_expand/words/methods/arrays.rs
+++ b/src/parser/shell_expand/words/methods/arrays.rs
@@ -4,7 +4,6 @@ use super::super::{Index, Select, SelectWithSize};
 use super::super::super::{expand_string, is_expression, Expander};
 use smallstring::SmallString;
 use std::char;
-use std::io::{self, Write};
 use types::Array;
 use unicode_segmentation::UnicodeSegmentation;
 
@@ -18,223 +17,382 @@ pub(crate) struct ArrayMethod<'a> {
 
 impl<'a> ArrayMethod<'a> {
     pub(crate) fn handle<E: Expander>(&self, current: &mut String, expand_func: &E) {
-        match self.method {
-            "split" => {
-                let variable = if let Some(variable) = expand_func.variable(self.variable, false) {
-                    variable
-                } else if is_expression(self.variable) {
-                    expand_string(self.variable, expand_func, false).join(" ")
-                } else {
-                    return;
-                };
-                match (&self.pattern, self.selection.clone()) {
-                    (&Pattern::StringPattern(pattern), Select::All) => current.push_str(&variable
-                        .split(&unescape(expand_string(pattern, expand_func, false).join(" ")))
-                        .collect::<Vec<&str>>()
-                        .join(" ")),
-                    (&Pattern::Whitespace, Select::All) => current.push_str(&variable
-                        .split(char::is_whitespace)
-                        .filter(|x| !x.is_empty())
-                        .collect::<Vec<&str>>()
-                        .join(" ")),
-                    (_, Select::None) => (),
-                    (&Pattern::StringPattern(pattern), Select::Index(Index::Forward(id))) => {
-                        current.push_str(
-                            variable
-                                .split(
-                                    &unescape(expand_string(pattern, expand_func, false).join(" ")),
-                                )
-                                .nth(id)
-                                .unwrap_or_default(),
-                        )
-                    }
-                    (&Pattern::Whitespace, Select::Index(Index::Forward(id))) => current.push_str(
-                        variable
-                            .split(char::is_whitespace)
-                            .filter(|x| !x.is_empty())
-                            .nth(id)
-                            .unwrap_or_default(),
-                    ),
-                    (&Pattern::StringPattern(pattern), Select::Index(Index::Backward(id))) => {
-                        current.push_str(
-                            variable
-                                .rsplit(
-                                    &unescape(expand_string(pattern, expand_func, false).join(" ")),
-                                )
-                                .nth(id)
-                                .unwrap_or_default(),
-                        )
-                    }
-                    (&Pattern::Whitespace, Select::Index(Index::Backward(id))) => current
-                        .push_str(
-                            variable
-                                .rsplit(char::is_whitespace)
-                                .filter(|x| !x.is_empty())
-                                .nth(id)
-                                .unwrap_or_default(),
-                        ),
-                    (&Pattern::StringPattern(pattern), Select::Range(range)) => {
-                        let expansion = unescape(
-                            unescape(expand_string(pattern, expand_func, false).join(" ")),
-                        );
-                        let iter = variable.split(&expansion);
-                        if let Some((start, length)) = range.bounds(iter.clone().count()) {
-                            let range = iter.skip(start).take(length).collect::<Vec<_>>().join(" ");
-                            current.push_str(&range)
-                        }
-                    }
-                    (&Pattern::Whitespace, Select::Range(range)) => {
-                        let len =
-                            variable.split(char::is_whitespace).filter(|x| !x.is_empty()).count();
-                        if let Some((start, length)) = range.bounds(len) {
-                            let range = variable
-                                .split(char::is_whitespace)
-                                .filter(|x| !x.is_empty())
-                                .skip(start)
-                                .take(length)
-                                .collect::<Vec<&str>>()
-                                .join(" ");
-                            current.push_str(&range);
-                        }
-                    }
-                    (_, Select::Key(_)) => (),
-                }
-            }
-            _ => {
-                let stderr = io::stderr();
-                let mut stderr = stderr.lock();
-                let _ = writeln!(stderr, "ion: invalid array method: {}", self.method);
-            }
+        let res = match self.method {
+            "split" => self.split(expand_func).map(|r| r.join(" ")),
+            _ => Err("invalid array method"),
+        };
+        match res {
+            Ok(output) => current.push_str(&output),
+            Err(msg) => eprintln!("ion: {}: {}", self.method, msg)
         }
     }
 
     pub(crate) fn handle_as_array<E: Expander>(&self, expand_func: &E) -> Array {
-        macro_rules! resolve_var {
-            () => {
-                if let Some(variable) = expand_func.variable(self.variable, false) {
-                    variable
-                } else if is_expression(self.variable) {
-                    expand_string(self.variable, expand_func, false).join(" ")
+        let res = match self.method {
+            "split" => self.split(expand_func),
+            "split_at" => self.split_at(expand_func),
+            "graphemes" => self.graphemes(expand_func),
+            "bytes" => self.bytes(expand_func),
+            "chars" => self.chars(expand_func),
+            _ => Err("invalid array method"),
+        };
+
+        res.unwrap_or_else(|m| {
+            eprintln!("ion: {}: {}", self.method, m);
+            array![]
+        })
+    }
+
+    #[inline]
+    fn resolve_var<E: Expander>(&self, expand_func: &E) -> String {
+        if let Some(variable) = expand_func.variable(self.variable, false) {
+            variable
+        } else if is_expression(self.variable) {
+            expand_string(self.variable, expand_func, false).join(" ")
+        } else {
+            "".into()
+        }
+    }
+
+    fn split<E: Expander>(&self, expand_func: &E) -> Result<Array, &'static str> {
+        let variable = self.resolve_var(expand_func);
+        let res = match (&self.pattern, self.selection.clone()) {
+            (_, Select::None) => Some("".into()).into_iter().collect(),
+            (&Pattern::StringPattern(pattern), Select::All) => variable
+                .split(&unescape(expand_string(pattern, expand_func, false).join(" ")))
+                .map(From::from)
+                .collect(),
+            (&Pattern::Whitespace, Select::All) => variable
+                .split(char::is_whitespace)
+                .filter(|x| !x.is_empty())
+                .map(From::from)
+                .collect(),
+            (&Pattern::StringPattern(pattern), Select::Index(Index::Forward(id))) => {
+                variable
+                    .split(&unescape(expand_string(pattern, expand_func, false).join(" ")))
+                    .nth(id)
+                    .map(From::from)
+                    .into_iter()
+                    .collect()
+            }
+            (&Pattern::Whitespace, Select::Index(Index::Forward(id))) => variable
+                .split(char::is_whitespace)
+                .filter(|x| !x.is_empty())
+                .nth(id)
+                .map(From::from)
+                .into_iter()
+                .collect(),
+            (&Pattern::StringPattern(pattern), Select::Index(Index::Backward(id))) => {
+                variable
+                    .rsplit(&unescape(expand_string(pattern, expand_func, false).join(" ")))
+                    .nth(id)
+                    .map(From::from)
+                    .into_iter()
+                    .collect()
+            }
+            (&Pattern::Whitespace, Select::Index(Index::Backward(id))) => variable
+                .rsplit(char::is_whitespace)
+                .filter(|x| !x.is_empty())
+                .nth(id)
+                .map(From::from)
+                .into_iter()
+                .collect(),
+            (&Pattern::StringPattern(pattern), Select::Range(range)) => {
+                let expansion =
+                    unescape(expand_string(pattern, expand_func, false).join(" "));
+                let iter = variable.split(&expansion);
+                if let Some((start, length)) = range.bounds(iter.clone().count()) {
+                    iter.skip(start).take(length).map(From::from).collect()
                 } else {
-                    "".into()
+                    Array::new()
                 }
             }
-        }
-
-        match self.method {
-            "split" => {
-                let variable = resolve_var!();
-                return match (&self.pattern, self.selection.clone()) {
-                    (_, Select::None) => Some("".into()).into_iter().collect(),
-                    (&Pattern::StringPattern(pattern), Select::All) => variable
-                        .split(&unescape(expand_string(pattern, expand_func, false).join(" ")))
-                        .map(From::from)
-                        .collect(),
-                    (&Pattern::Whitespace, Select::All) => variable
-                        .split(char::is_whitespace)
-                        .filter(|x| !x.is_empty())
-                        .map(From::from)
-                        .collect(),
-                    (&Pattern::StringPattern(pattern), Select::Index(Index::Forward(id))) => {
-                        variable
-                            .split(&unescape(expand_string(pattern, expand_func, false).join(" ")))
-                            .nth(id)
-                            .map(From::from)
-                            .into_iter()
-                            .collect()
-                    }
-                    (&Pattern::Whitespace, Select::Index(Index::Forward(id))) => variable
+            (&Pattern::Whitespace, Select::Range(range)) => {
+                let len =
+                    variable.split(char::is_whitespace).filter(|x| !x.is_empty()).count();
+                if let Some((start, length)) = range.bounds(len) {
+                    variable
                         .split(char::is_whitespace)
                         .filter(|x| !x.is_empty())
-                        .nth(id)
-                        .map(From::from)
-                        .into_iter()
-                        .collect(),
-                    (&Pattern::StringPattern(pattern), Select::Index(Index::Backward(id))) => {
-                        variable
-                            .rsplit(&unescape(expand_string(pattern, expand_func, false).join(" ")))
-                            .nth(id)
-                            .map(From::from)
-                            .into_iter()
-                            .collect()
-                    }
-                    (&Pattern::Whitespace, Select::Index(Index::Backward(id))) => variable
-                        .rsplit(char::is_whitespace)
-                        .filter(|x| !x.is_empty())
-                        .nth(id)
+                        .skip(start)
+                        .take(length)
                         .map(From::from)
-                        .into_iter()
-                        .collect(),
-                    (&Pattern::StringPattern(pattern), Select::Range(range)) => {
-                        let expansion =
-                            unescape(expand_string(pattern, expand_func, false).join(" "));
-                        let iter = variable.split(&expansion);
-                        if let Some((start, length)) = range.bounds(iter.clone().count()) {
-                            iter.skip(start).take(length).map(From::from).collect()
-                        } else {
-                            Array::new()
-                        }
-                    }
-                    (&Pattern::Whitespace, Select::Range(range)) => {
-                        let len =
-                            variable.split(char::is_whitespace).filter(|x| !x.is_empty()).count();
-                        if let Some((start, length)) = range.bounds(len) {
-                            variable
-                                .split(char::is_whitespace)
-                                .filter(|x| !x.is_empty())
-                                .skip(start)
-                                .take(length)
-                                .map(From::from)
-                                .collect()
-                        } else {
-                            Array::new()
-                        }
-                    }
-                    (_, Select::Key(_)) => Some("".into()).into_iter().collect(),
-                };
-            }
-            "split_at" => {
-                let variable = resolve_var!();
-                match self.pattern {
-                    Pattern::StringPattern(string) => if let Ok(value) =
-                        expand_string(string, expand_func, false).join(" ").parse::<usize>()
-                    {
-                        if value < variable.len() {
-                            let (l, r) = variable.split_at(value);
-                            return array![SmallString::from(l), SmallString::from(r)];
-                        }
-                        eprintln!("ion: split_at: value is out of bounds");
-                    } else {
-                        eprintln!("ion: split_at: requires a valid number as an argument");
-                    },
-                    Pattern::Whitespace => {
-                        eprintln!("ion: split_at: requires an argument");
-                    }
+                        .collect()
+                } else {
+                    Array::new()
                 }
             }
-            "graphemes" => {
-                let variable = resolve_var!();
-                let graphemes = UnicodeSegmentation::graphemes(variable.as_str(), true);
-                let len = graphemes.clone().count();
-                return graphemes.map(From::from).select(self.selection.clone(), len);
-            }
-            "bytes" => {
-                let variable = resolve_var!();
-                let len = variable.as_bytes().len();
-                return variable.bytes().map(|b| b.to_string()).select(self.selection.clone(), len);
-            }
-            "chars" => {
-                let variable = resolve_var!();
-                let len = variable.chars().count();
-                return variable.chars().map(|c| c.to_string()).select(self.selection.clone(), len);
+            (_, Select::Key(_)) => Some("".into()).into_iter().collect(),
+        };
+        Ok(res)
+    }
+
+    fn split_at<E: Expander>(&self, expand_func: &E) -> Result<Array, &'static str> {
+        let variable = self.resolve_var(expand_func);
+        match self.pattern {
+            Pattern::StringPattern(string) => if let Ok(value) =
+                expand_string(string, expand_func, false).join(" ").parse::<usize>()
+            {
+                if value < variable.len() {
+                    let (l, r) = variable.split_at(value);
+                    Ok(array![SmallString::from(l), SmallString::from(r)])
+                } else {
+                    Err("value is out of bounds")
+                }
+            } else {
+                Err("requires a valid number as an argument")
+            },
+            Pattern::Whitespace => {
+                Err("requires an argument")
             }
-            _ => {
-                let stderr = io::stderr();
-                let mut stderr = stderr.lock();
-                let _ = writeln!(stderr, "ion: invalid array method: {}", self.method);
+        }
+    }
+
+    fn graphemes<E: Expander>(&self, expand_func: &E) -> Result<Array, &'static str> {
+        let variable = self.resolve_var(expand_func);
+        let graphemes: Vec<String> = UnicodeSegmentation::graphemes(variable.as_str(), true)
+            .map(From::from)
+            .collect();
+        let len = graphemes.len();
+        Ok(graphemes.into_iter().select(self.selection.clone(), len))
+    }
+
+    fn bytes<E: Expander>(&self, expand_func: &E) -> Result<Array, &'static str> {
+        let variable = self.resolve_var(expand_func);
+        let len = variable.as_bytes().len();
+        Ok(variable.bytes().map(|b| b.to_string()).select(self.selection.clone(), len))
+    }
+
+    fn chars<E: Expander>(&self, expand_func: &E) -> Result<Array, &'static str> {
+        let variable = self.resolve_var(expand_func);
+        let len = variable.chars().count();
+        Ok(variable.chars().map(|c| c.to_string()).select(self.selection.clone(), len))
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use super::super::Key;
+    use super::super::super::Range;
+    use types::Value;
+
+    struct VariableExpander;
+
+    impl Expander for VariableExpander {
+        fn variable(&self, variable: &str, _: bool) -> Option<Value> {
+            match variable {
+                "FOO" => Some("FOOBAR".to_owned()),
+                "SPACEDFOO" => Some("FOO BAR".to_owned()),
+                _ => None,
             }
         }
+    }
+
+    #[test]
+    fn test_split_string_all() {
+        let mut output = String::new();
+        let method = ArrayMethod {
+            method: "split",
+            variable: "$FOO",
+            pattern: Pattern::StringPattern("OB"),
+            selection: Select::All,
+        };
+        method.handle(&mut output, &VariableExpander);
+        assert_eq!(output, "FO AR");
+    }
+
+    #[test]
+    fn test_split_whitespace_all() {
+        let mut output = String::new();
+        let method = ArrayMethod {
+            method: "split",
+            variable: "$SPACEDFOO",
+            pattern: Pattern::Whitespace,
+            selection: Select::All,
+        };
+        method.handle(&mut output, &VariableExpander);
+        assert_eq!(output, "FOO BAR");
+    }
+
+    #[test]
+    fn test_split_string_index_forward() {
+        let mut output = String::new();
+        let method = ArrayMethod {
+            method: "split",
+            variable: "$FOO",
+            pattern: Pattern::StringPattern("OB"),
+            selection: Select::Index(Index::Forward(1)),
+        };
+        method.handle(&mut output, &VariableExpander);
+        assert_eq!(output, "AR");
+    }
+
+    #[test]
+    fn test_split_whitespace_index_forward() {
+        let mut output = String::new();
+        let method = ArrayMethod {
+            method: "split",
+            variable: "$SPACEDFOO",
+            pattern: Pattern::Whitespace,
+            selection: Select::Index(Index::Forward(1)),
+        };
+        method.handle(&mut output, &VariableExpander);
+        assert_eq!(output, "BAR");
+    }
+
+    #[test]
+    fn test_split_string_index_backward() {
+        let mut output = String::new();
+        let method = ArrayMethod {
+            method: "split",
+            variable: "$FOO",
+            pattern: Pattern::StringPattern("OB"),
+            selection: Select::Index(Index::Backward(1)),
+        };
+        method.handle(&mut output, &VariableExpander);
+        assert_eq!(output, "FO");
+    }
+
+    #[test]
+    fn test_split_whitespace_index_backward() {
+        let mut output = String::new();
+        let method = ArrayMethod {
+            method: "split",
+            variable: "$SPACEDFOO",
+            pattern: Pattern::Whitespace,
+            selection: Select::Index(Index::Backward(1)),
+        };
+        method.handle(&mut output, &VariableExpander);
+        assert_eq!(output, "FOO");
+    }
+
+    #[test]
+    fn test_split_string_range() {
+        let mut output = String::new();
+        let method = ArrayMethod {
+            method: "split",
+            variable: "$FOO",
+            pattern: Pattern::StringPattern("OB"),
+            selection: Select::Range(Range::from(Index::Forward(0))),
+        };
+        method.handle(&mut output, &VariableExpander);
+        assert_eq!(output, "FO AR");
+    }
+
+    #[test]
+    fn test_split_whitespace_range() {
+        let mut output = String::new();
+        let method = ArrayMethod {
+            method: "split",
+            variable: "$SPACEDFOO",
+            pattern: Pattern::Whitespace,
+            selection: Select::Range(Range::from(Index::Forward(0))),
+        };
+        method.handle(&mut output, &VariableExpander);
+        assert_eq!(output, "FOO BAR");
+    }
+
+    #[test]
+    fn test_split_none() {
+        let mut output = String::new();
+        let method = ArrayMethod {
+            method: "split",
+            variable: "$SPACEDFOO",
+            pattern: Pattern::Whitespace,
+            selection: Select::None,
+        };
+        method.handle(&mut output, &VariableExpander);
+        assert_eq!(output, "");
+    }
+
+    #[test]
+    fn test_split_key() {
+        let mut output = String::new();
+        let method = ArrayMethod {
+            method: "split",
+            variable: "$SPACEDFOO",
+            pattern: Pattern::Whitespace,
+            selection: Select::Key(Key::new("1")),
+        };
+        method.handle(&mut output, &VariableExpander);
+        assert_eq!(output, "");
+    }
+
+    #[test]
+    fn test_split_at_failing_whitespace() {
+        let method = ArrayMethod {
+            method: "split_at",
+            variable: "$SPACEDFOO",
+            pattern: Pattern::Whitespace,
+            selection: Select::All,
+        };
+        assert_eq!(method.handle_as_array(&VariableExpander), array![]);
+    }
+
+    #[test]
+    fn test_split_at_failing_no_number() {
+        let method = ArrayMethod {
+            method: "split_at",
+            variable: "$SPACEDFOO",
+            pattern: Pattern::StringPattern("a"),
+            selection: Select::All,
+        };
+        assert_eq!(method.handle_as_array(&VariableExpander), array![]);
+    }
+
+    #[test]
+    fn test_split_at_failing_out_of_bound() {
+        let method = ArrayMethod {
+            method: "split_at",
+            variable: "$SPACEDFOO",
+            pattern: Pattern::StringPattern("100"),
+            selection: Select::All,
+        };
+        assert_eq!(method.handle_as_array(&VariableExpander), array![]);
+    }
+
+    #[test]
+    fn test_split_at_succeeding() {
+        let method = ArrayMethod {
+            method: "split_at",
+            variable: "$FOO",
+            pattern: Pattern::StringPattern("3"),
+            selection: Select::All,
+        };
+        assert_eq!(method.handle_as_array(&VariableExpander), array!["FOO", "BAR"]);
+    }
+
+    #[test]
+    fn test_graphemes() {
+        let method = ArrayMethod {
+            method: "graphemes",
+            variable: "$FOO",
+            pattern: Pattern::StringPattern("3"),
+            selection: Select::All,
+        };
+        assert_eq!(method.handle_as_array(&VariableExpander), array!["F", "O", "O", "B", "A", "R"]);
+    }
+
+    #[test]
+    fn test_bytes() {
+        let method = ArrayMethod {
+            method: "bytes",
+            variable: "$FOO",
+            pattern: Pattern::StringPattern("3"),
+            selection: Select::All,
+        };
+        assert_eq!(method.handle_as_array(&VariableExpander), array!["70", "79", "79", "66", "65", "82"]);
+    }
 
-        array![]
+    #[test]
+    fn test_chars() {
+        let method = ArrayMethod {
+            method: "chars",
+            variable: "$FOO",
+            pattern: Pattern::StringPattern("3"),
+            selection: Select::All,
+        };
+        assert_eq!(method.handle_as_array(&VariableExpander), array!["F", "O", "O", "B", "A", "R"]);
     }
 }