How to get Elixir and Phoenix up and running

Maybe you’ve recently read an awesome blog post about Elixir and would like to try it out for yourself or you are simply trying to get back into it after taking a hiatus(the boat I’m in). This post is aimed at Elixir newbies who would like to get up and running with a new project that has integration tests configured and some basic CI with GitHub Actions.

Install Elixir and Erlang

First things first, you need to make sure you have Elixir and Erlang installed. At Gaslight, we recommend using asdf to manage versions. It eliminates the need for multiple version managers(think rbenv, nvm, etc. combined) and allows for global and local, project-specific tool versions. Installation instructions can be found here.

At the time of writing this post, v1.12.0 is the latest version of Elixir and v24.0.0 is the latest version of Erlang/OTP. Go ahead and install these versions and set them globally

asdf plugin-add elixir
asdf plugin-add erlang

asdf install erlang 24.0.0
asdf global erlang 24.0.0

asdf install elixir 1.12.0
asdf global elixir 1.12.0

Verify you have the correct versions installed:

erl -v 
elixir -v

If you see Erlang/OTP 24 and Elixir 1.12.0 (compiled with Erlang/OTP 22) after running those commands, you’re good to go. If not, I’d recommend checking StackOverflow to see if someone else has had the same issue.

Generate a New Project

Next up you’ll want to make sure you have phx_new installed so you can generate a new project. mix archive.install hex phx_new 1.5.9

Once you have phx_new installed, you’re ready to generate a new project. There are several options when creating a new phoenix project but for simplicity, we’re going to use the base phx.new command. You can find all of the flags and options for generating a new project here.

Go ahead and generate a new project: mix phx.new hello_world

When prompted to fetch and install dependencies, enter y. cd into the directory and create your database. If you’ve never installed Postgres, here’s a solid post about getting it set up. cd hello_world && mix ecto.create

Start your application to make sure it’s working properly by going to http://localhost:4000/ mix phx.server

Commit to GitHub

Congrats! You now have a working Elixir/Phoenix application. Now let’s get it committed to GitHub so we can start setting up integration tests.

git init
git add .

One trick I learned from a fellow developer at Gaslight was to make your first commit the command you ran to generate your project so you don’t lose track of the flags you used.

git commit -m "mix phx.new hello_world"
git branch -M master
git remote add origin https://github.com/atuley/hello_world.git
git push -u origin master

Add Integration Test Library

Perfect. Now we have source control and can begin setting up your integration tests. For this post, we will use Wallaby.

Add Wallaby as a dependency in your deps function inside of mix.exs

defp deps do
    [
      …,
	{:wallaby, "~> 0.28.0", runtime: false, only: :test}
    ]
end

Fetch your dependencies mix deps.get

Configure Wallaby

Next, we need to make some adjustments to your config/test.exs file to account for Wallaby. Under your Endpoint configuration, toggle server to true

config :hello_world, HelloWorldWeb.Endpoint,
  http: [port: 4002],
  server: true

We also need to configure Wallaby to use chromedriver. Add this to the bottom of your config/test.exs file.

config :wallaby,
  driver: Wallaby.Chrome,
  otp_app: :hello_world,
  chromedriver: [
    headless: false
  ]

config :hello_world, :sandbox, Ecto.Adapters.SQL.Sandbox

After that, we need to ensure Wallaby is started up whenever mix test is run. Add the following lines to your test/test_helper.exs file.

{:ok, _} = Application.ensure_all_started(:wallaby)
Application.put_env(:wallaby, :base_url, HelloWorldWeb.Endpoint.url)

Now we want to enable concurrent testing by adding the Phoenix.Ecto.SQL.Sandbox to your Endpoint(endpoint.ex). Make sure this is before any other plugs in the file.

if sandbox = Application.get_env(:hello_world, :sandbox) do
    plug Phoenix.Ecto.SQL.Sandbox, sandbox: sandbox
  end

To recompile assets when you run mix test you will need to add an alias to your mix config that does this. Wallaby has provided a small function to do this in their documentation:

Adjust your test aliases in mix.exs and add compile_assets/1

test: [
        "assets.compile --quiet",
        "ecto.create --quiet",
        "ecto.migrate",
        "test",
      ],
      "assets.compile": &compile_assets/1
]

defp compile_assets(_) do
    Mix.shell().cmd("cd assets && ./node_modules/.bin/webpack --mode development",
      quiet: true
    )
  end

More information about configuration and troubleshooting can be found in their documentation.

Writing your First Wallaby Test

Now that we have everything configured, we need to make sure it’s working as expected. Let’s write a simple test that checks for text on the home page.

First, let’s create a new folder under test/hello_world_web/ called feature to house your integration tests. Create a new file and name it what you’d like. Make sure it ends with _test.exs so it can get picked up by mix test. I’ll call mine first_feature_test.exs (naming is hard :D)

Add the following code to first_feature_test.exs

defmodule HelloWorldWeb.FirstFeatureTest do
  use ExUnit.Case, async: true
  use Wallaby.Feature

  feature "visit the home page", %{session: session} do
    session
    |> visit("/")
    |> assert_has(Query.css(".welcome-message", text: "Welcome to Phoenix!"))
  end
end

Make sure you add the .welcome-message class to index.html.eex to get the test to pass. <h1 class="welcome-message"><%= gettext "Welcome to %{name}!", name: "Phoenix" %></h1>

session
    |> visit("/")
    ….

Navigates to the home page,

assert_has(Query.css(".welcome-message", text: "Welcome to Phoenix!”)) Asserts the home page has a welcome message by querying for a css class and checks the associated text. If it does not find the provided text, it will raise an error and fail.

Here’s a link to Wallaby’s API documentation if you’re curious what other browser tools you have at your disposal. Now that we have our test, we can go ahead and run it with mix test test/hello_world_web/feature/first_feature_test.exs.

Your test should pass. If you’ve never installed chromedriver you may be experiencing some issues. You can check to see if you have it installed by running chromedriver —version. If you get nothing, you’ll need to install it. I recommend installing with homebrew.

If you’d prefer your tests to not open up a chrome window when running, you can run them in headless mode by changing headless to true in config/test.exs

config :wallaby,
 driver: Wallaby.Chrome,
 otp_app: :hello_world,
 chromedriver: [
   headless: true
 ]

At this point I’d recommend making another commit before we start setting up GitHub Actions.

git add .
git commit -m “Setup Wallaby”
git push origin master

Here’s a link to the HelloWorld project and the commit for setting Wallaby up: https://github.com/atuley/hello_world/commit/662637c60037681b077cab6b960fbc70c593abf8

Setting Up Continuous Integration with GitHub Actions

GitHub Actions are an easy way to get up and running with continuous integration without having to use third-party tools like CircleCI(https://circleci.com/). GitHub has some default workflows that provide a good starting point. If you navigate to the “Actions” tab in your repo, it will more than likely already be suggesting to add an Elixir workflow. If you just want to build and run unit tests, this is a good option. If you want your Wallaby tests to run as well, you’ll need to do some extra configuration.

We’ll use GitHub’s Elixir starter workflow as our base. Go ahead and create a new folder at the root of your project called .github/workflows and add the following elixir.yml file.

name: Elixir CI

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:
  build:

    name: Build and test
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: Set up Elixir
      uses: erlef/setup-elixir@885971a72ed1f9240973bd92ab57af8c1aa68f24
      with:
        elixir-version: ‘1.12.0’ # Define the elixir version [required]
        otp-version: '22.3’ # Define the OTP version [required]
    - name: Restore dependencies cache
      uses: actions/cache@v2
      with:
        path: deps
        key: $-mix-$
        restore-keys: $-mix-
    - name: Install dependencies
      run: mix deps.get
    - name: Run tests
      run: mix test

Be sure to update the elixir-version with the version you are using in your project.

with:
        elixir-version: ‘1.12.0’

In order to use our test database, we need to configure a postgres service container. Add the following under jobs:

test:
    env:
      MIX_ENV: test
      DATABASE_URL: postgres://postgres:postgres@localhost:5432/postgres
    runs-on: ubuntu-latest

    services:
      db:
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: hello_world_test
        image: postgres:11
        ports: ['5432:5432']
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

Be sure to update the POSTGRES_DB field with your test database name. This is typically your application name with _test added at the end. It can be found in

# config/text.exs
database: "hello_world_test#{System.get_env("MIX_TEST_PARTITION”)}”,

Next, we specify that we need to use chromedriver like we did locally. Add the following above - name: Install Dependencies

...
 - uses: nanasess/setup-chromedriver@master
 - name: Install Dependencies
...

Finally, we need to make sure our application compiles all assets and runs migrations before testing. Add the following under run: mix deps.get

- run: mix compile
- run: mix ecto.migrate
- run: npm install
working-directory: ./assets
-run: npm run deploy --prefix ./assets

Make sure your tests are configured to run in headless mode in your config/test.exs file.

config :wallaby,
  driver: Wallaby.Chrome,
  otp_app: :hello_world,
  chromedriver: [
    headless: true
  ]

If you want a separate configuration for CI, you can create another file under config called ci.exs and add your configuration there. Just make sure you change your MIX_ENV to ci.

Now commit our .github/workflows/elixir.yml file and navigate to your repository in GitHub. Under “Actions” you should see your latest commit running and eventually should pass. You should now be in a good place to start writing new features for your application!

If you’d like to add a badge with your applications build status to your README.md, just simply add ![example workflow](https://github.com/<username>/<application>/actions/workflows/elixir.yml/badge.svg)

Here is the link to the full configuration file if you’d like to copy/paste: [https://github.com/atuley/hello_world/blob/master/.github/workflows/elixir.yml _](https://github.com/atuley/hello_world/blob/master/.github/workflows/elixir.yml)_Repository link: https://github.com/atuley/hello_world

Related Posts

Want to learn about the types of products we build?

Check out our projects

Like what you're seeing? Let's keep in touch.

Subscribe to Our Newsletter