Here’s how to test arrays
Tags: ruby, testing, tdd
Published: 2020-03-05 18:13:05

---
title: Here’s how to test arrays
published: true
description: Here’s another way you can systemize your unit testing.
tags: ruby, testing, tdd
cover_image: https://cdn.pixabay.com/photo/2017/10/13/05/26/silk-tie-2846862_1280.jpg
---

If I give you a function that returns an array of objects, how do you test the returned value? How many unit tests do you need? One, two, three? What are their descriptions?

In this post I’ll show you how I do it.

It follows on from my post last week about how to test dependencies. It’s written in Ruby but it applies to any dynamic language, including JavaScript. (Statically typed languages don’t necessarily need these types of test).

{% post d_ir/here-s-how-to-test-dependencies-with-mocks-ia6 %}

> These posts are all about **systemizing your testing**.
>
> Testing gets kind of _boring_ once you have a system for it. But boring is good. Boring produces consistency. It means you aren’t getting stuck, or having to _think_ too much.
>
> Every time I have to test a returned array, I use this exact same approach.

## Test 1. The right number of elements are returned


Imagine we’re testing a `filter` function that takes a source array of objects and a string to match on. Our tests will define the behavior of the function.

```ruby
RSpec.describe "#filter" do
  let(:source) do
    [
      { id: 123, name: "Daniel", diet: :vegan, pet: :cat },
      { id: 234, name: "Danny", diet: :anything, pet: :goldfish },
      { id: 345, name: "Jane", diet: :vegetarian, pet: nil }
    ]
  end

  it "returns all elements matching the name parameter" do
    result = filter(source, "Dan")
    expect(result.length).to eq 2
    expect(result[0][:id]).to eq 123
    expect(result[1][:id]).to eq 234
  end

end
```

Here my test list has 3 items: you can normally prove everything with just items. No need for a longer list.

Now let’s look at how we might make that test pass.

```ruby
def filter(source, name)
  source.select { |s| s[:name].include?(name) }
end
```

## Test 2.  That it returns the correct form of object


In the second test you want to ensure you test that you get all the properties that you’re expecting. Occassionally this test isn’t necessary: 

```ruby
it "returns all the right information" do
  result = filter(source, "Dan")
  expect(result.first).to eq source[0]
end
```

This test already passes, so we don’t need to write it. But if you aren’t returning the exact item from the source, then write out the value you expect instead:

```ruby
it "returns all the right information" do
  result = filter(source, "Dan")
  expect(result.first).to eq({
    name: "Daniel",
    pet: :cat
  })
end
```

Now we actually have to write code to make that pass:

```ruby
def filter(source, name)
  source
    .select { |s| s[:name].include?(name) }
    .map { |s| s.except(:diet) }
end
```

# Test 3. That they’re in the right order

Sometimes, you care about the order that elements are returned. For this we need a new source list, again with at least three elements:

```ruby
let(:unsorted_source) do
    [
      { id: 123, name: "Daniel" },
      { id: 234, name: "Dan" },
      { id: 345, name: "Danny" }
    ]
end

it "orders results alphabetically" do
  result = filter(unsorted_source, "Dan")
  expect(result[0][:name]).to eq("Dan")
  expect(result[1][:name]).to eq("Daniel")
  expect(result[2][:name]).to eq("Danny")
end
```

The elements must be in an unsorted order: neither ascending nor descending order. Otherwise, you could make this test pass simply by returning the same array or by calling `reverse` rather than `sort_by`.

And here’s the final result:

```
def filter(source, name)
  source
    .select { |s| s[:name].include?(name) }
    .map { |s| s.except(:diet) }
    .sort_by { |s| s[:name] }
end
```

## Modification 1: Join tests 1 and 3

If I know I’m going to order my tests, I’ll join 1 and 3 together.

```ruby
it "returns all elements matching the name, in alphabetical order" do
  result = filter(unsorted_source, "Dan")
  expect(result[0][:id]).to eq 234
  expect(result[1][:id]).to eq 123
  expect(result[2][:id]).to eq 345
end
```

Or perhaps you’d rather use `map`:

```ruby
it "returns all elements matching the name, in alphabetical order" do
  result = filter(unsorted_source, "Dan")
  expect(result.map { |r| r[:id] }).to match_array [234, 123, 345]
end
```

And that’s all there is to it! 🎉