Repository: phoenixframework/phoenix
Pull request #2479 (2017-09-07 05:31:04)

Update testing guides for 1.3
This fixes phoenixframework/phoenix_guides#721 and completes the remaining work mentioned in phoenixframework/phoenix_guides#707, updating the testing guides for Phoenix 1.3. Some of this was just mix task renaming (`phoenix.xxx` -> `phx.xxx`), but there was a lot of rework to the (former) testing_models guide.

Details:
- Tweaked the Ecto guide to fix a phx.gen.schema command, adding the context module name
- Removed the empty testing/testing_views.md file
- In testing.md, updated the generated directory structure, output examples, and ecto guide name
- In testing_channels, updated the mix task name and removed mention of ChannelCase importing query stuff (it doesn't)
- Renamed testing_models to testing_schemas, and updated: the generators no longer create a schema test (so I have the reader create it), and lots of the test output needed to be updated.
- Added a "work in progress" note to the Testing overview, since maybe we ought to add more coverage of context testing? (I started to document the generated context tests, but what I came up with didn't seem useful, and I wasn't sure what strategies to convey about adding more context tests.)
- Updated the controller testing guide with Phoenix 1.3-ish module names, paths, etc.


jeregrine: @bryanstearns Looks good so far! I look forward to the controller test. 

Patch:
diff --git a/guides/docs/ecto.md b/guides/docs/ecto.md
index 3f9986db2..446cffc89 100644
--- a/guides/docs/ecto.md
+++ b/guides/docs/ecto.md
@@ -27,11 +27,11 @@ ALTER USER postgres WITH SUPERUSER;
 Now that we have Ecto and Postgres installed and configured, the easiest way to use Ecto is to generate an Ecto *schema* through the `phx.gen.schema` task. Ecto schemas are a way for us to specify how Elixir data types map to and from external sources, such as database tables. Let's generate a `User` schema with `name`, `email`, `bio`, and `number_of_pets` fields.
 
 ```console
-$ mix phx.gen.schema User users name:string email:string \
+$ mix phx.gen.schema Accounts.User users name:string email:string \
 bio:string number_of_pets:integer
 
-* creating ./lib/hello/user.ex
-* creating priv/repo/migrations/20170523151118_create_user.exs
+* creating ./lib/hello/accounts/user.ex
+* creating priv/repo/migrations/20170523151118_create_users.exs
 
 Remember to update your repository by running migrations:
 
@@ -162,10 +162,10 @@ Ecto schemas are responsible for mapping Elixir values to external data sources,
 Here's the `User` schema that Phoenix generated for us.
 
 ```elixir
-defmodule Hello.User do
+defmodule Hello.Accounts.User do
   use Ecto.Schema
   import Ecto.Changeset
-  alias Hello.User
+  alias Hello.Accounts.User
 
 
   schema "users" do
@@ -206,13 +206,13 @@ Right now, we have two transformations in our pipeline. In the first call, we in
 `cast/3` first takes a struct, then the parameters (the proposed updates), and then the final field is the list of columns to be updated. `cast/3` also will only take fields that exist in the schema.
 Next, `validate_required/3` checks that this list of fields is present in the changeset that `cast/3` returns. By default with the generator, all fields are required.
 
-We can verify this functionality in iex. Let's fire up our application inside `iex` by running `iex -S mix`. In order to minimize typing and make this easier to read, let's alias our `Hello.User` struct.
+We can verify this functionality in iex. Let's fire up our application inside `iex` by running `iex -S mix`. In order to minimize typing and make this easier to read, let's alias our `Hello.Accounts.User` struct.
 
 ```console
 $ iex -S mix
 
-iex> alias Hello.User
-Hello.User
+iex> alias Hello.Accounts.User
+Hello.Accounts.User
 ```
 
 Next, let's build a changeset from our schema with an empty `User` struct, and an empty map of parameters.
@@ -225,7 +225,7 @@ iex> changeset = User.changeset(%User{}, %{})
   email: {"can't be blank", [validation: :required]},
   bio: {"can't be blank", [validation: :required]},
   number_of_pets: {"can't be blank", [validation: :required]}],
- data: #Hello.User<>, valid?: false>
+ data: #Hello.Accounts.User<>, valid?: false>
 ```
 
 Once we have a changeset, we can check it if it is valid.
@@ -288,7 +288,7 @@ iex> changeset = User.changeset(%User{}, params)
 #Ecto.Changeset<action: nil,
  changes: %{bio: "An example to all", email: "joe@example.com",
    name: "Joe Example", number_of_pets: 5}, errors: [],
- data: #Hello.User<>, valid?: true>
+ data: #Hello.Accounts.User<>, valid?: true>
 ```
 
 Our new changeset is valid.
@@ -373,13 +373,13 @@ We've talked a lot about migrations and data-storage, but we haven't yet persist
 Let's head back over to IEx with `iex -S mix`, and insert a couple of users to the database.
 
 ```console
-iex> alias Hello.{Repo, User}
-[Hello.Repo, Hello.User]
+iex> alias Hello.{Repo, Accounts.User}
+[Hello.Repo, Hello.Accounts.User]
 iex> Repo.insert(%User{email: "user1@example.com"})
 [debug] QUERY OK db=4.6ms
 INSERT INTO "users" ("email","inserted_at","updated_at") VALUES ($1,$2,$3) RETURNING "id" ["user1@example.com", {{2017, 5, 23}, {19, 6, 4, 822044}}, {{2017, 5, 23}, {19, 6, 4, 822055}}]
 {:ok,
- %Hello.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
+ %Hello.Accounts.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
   bio: nil, email: "user1@example.com", id: 3,
   inserted_at: ~N[2017-05-23 19:06:04.822044], name: nil, number_of_pets: nil,
   updated_at: ~N[2017-05-23 19:06:04.822055]}}
@@ -388,7 +388,7 @@ iex> Repo.insert(%User{email: "user2@example.com"})
 [debug] QUERY OK db=5.1ms
 INSERT INTO "users" ("email","inserted_at","updated_at") VALUES ($1,$2,$3) RETURNING "id" ["user2@example.com", {{2017, 5, 23}, {19, 6, 8, 452545}}, {{2017, 5, 23}, {19, 6, 8, 452556}}]
 {:ok,
- %Hello.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
+ %Hello.Accounts.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
   bio: nil, email: "user2@example.com", id: 4,
   inserted_at: ~N[2017-05-23 19:06:08.452545], name: nil, number_of_pets: nil,
   updated_at: ~N[2017-05-23 19:06:08.452556]}}
@@ -400,11 +400,11 @@ We started by aliasing our `User` and `Repo` modules for easy access. Next, we c
 iex> Repo.all(User)
 [debug] QUERY OK source="users" db=2.7ms
 SELECT u0."id", u0."bio", u0."email", u0."name", u0."number_of_pets", u0."inserted_at", u0."updated_at" FROM "users" AS u0 []
-[%Hello.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
+[%Hello.Accounts.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
   bio: nil, email: "user1@example.com", id: 3,
   inserted_at: ~N[2017-05-23 19:06:04.822044], name: nil, number_of_pets: nil,
   updated_at: ~N[2017-05-23 19:06:04.822055]},
- %Hello.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
+ %Hello.Accounts.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
   bio: nil, email: "user2@example.com", id: 4,
   inserted_at: ~N[2017-05-23 19:06:08.452545], name: nil, number_of_pets: nil,
   updated_at: ~N[2017-05-23 19:06:08.452556]}]


Test patch:
diff --git a/guides/docs/testing/testing.md b/guides/docs/testing/testing.md
index 37027a553..00c701a8e 100644
--- a/guides/docs/testing/testing.md
+++ b/guides/docs/testing/testing.md
@@ -1,5 +1,7 @@
 # Introduction to Testing
 
+> Note: the Testing guides have not been fully updated to Phoenix 1.3; they're a work in progress and more content is coming.
+
 Testing has become integral to the software development process, and the ability to easily write meaningful tests is an indispensable feature for any modern web framework. Phoenix takes this seriously, providing support files to make all the major components of the framework easy to test. It also generates test modules with real-world examples alongside any generated modules to help get us going.
 
 Elixir ships with a built-in testing framework called [ExUnit](https://hexdocs.pm/ex_unit/1.5.1/ExUnit.html). ExUnit strives to be clear and explicit, keeping magic to a minimum. Phoenix uses ExUnit for all of its testing, and we will use it here as well.
@@ -8,7 +10,7 @@ ExUnit refers to a test module as a "test case", and we will do the same.
 
 Let's see this in action.
 
-> Note: Before we proceed, we'll need to have PostgreSQL installed and running on our system. We'll also need to configure our repo with the correct login credentials. [The section on ecto.create in the Mix Tasks guide](mix_tasks.html#ecto-specific-mix-tasks) has more information on this, and the [Ecto Models Guide](ecto_models.html) dives into the details on how it all works.
+> Note: Before we proceed, we'll need to have PostgreSQL installed and running on our system. We'll also need to configure our repo with the correct login credentials. [The section on ecto.create in the Mix Tasks guide](mix_tasks.html#ecto-specific-mix-tasks) has more information on this, and the [Ecto Guide](ecto.html) dives into the details on how it all works.
 
 In a freshly generated application (we use a project named "hello" in the examples), let's run `mix test` at the root of the project. (Please see the [Up and Running Guide](up_and_running.html) for instructions on generating a new application.)
 
@@ -16,7 +18,7 @@ In a freshly generated application (we use a project named "hello" in the exampl
 $ mix test
 ....
 
-Finished in 0.2 seconds (0.2s on load, 0.00s on tests)
+Finished in 0.2 seconds
 4 tests, 0 failures
 
 Randomized with seed 652656
@@ -30,19 +32,19 @@ In fact, we already have a directory structure completely set up for testing, in
 
 ```console
 test
-├── channels
-├── controllers
-│   └── page_controller_test.exs
-├── models
+├── hello_web
+│   ├── channels
+│   ├── controllers
+│   │   └── page_controller_test.exs
+│   └── views
+│       ├── error_view_test.exs
+│       ├── layout_view_test.exs
+│       └── page_view_test.exs
 ├── support
-│   ├── channel_case.ex
-│   ├── conn_case.ex
-│   └── model_case.ex
-├── test_helper.exs
-└── views
-    ├── error_view_test.exs
-    ├── layout_view_test.exs
-    └── page_view_test.exs
+│   ├── channel_case.ex
+│   ├── conn_case.ex
+│   └── data_case.ex
+└── test_helper.exs
 ```
 
 The test cases we get for free include `test/hello_web/controllers/page_controller_test.exs`, `test/hello_web/views/error_view_test.exs`, and `test/hello_web/views/page_view_test.exs`. Nice.
@@ -446,7 +448,7 @@ Randomized with seed 401472
 
 We've seen what Phoenix gives us with a newly generated app. Now let's see what happens when we generate a new HTML resource.
 
-Let's borrow the `users` resource we created in the [Ecto Models Guide](ecto_models.html).
+Let's borrow the `users` resource we created in the [Ecto Guide](ecto.html).
 
 At the root of our new application, let's run the `mix phx.gen.html` task with the following options.
 
diff --git a/guides/docs/testing/testing_channels.md b/guides/docs/testing/testing_channels.md
index 706d9bd57..837a89c09 100644
--- a/guides/docs/testing/testing_channels.md
+++ b/guides/docs/testing/testing_channels.md
@@ -22,7 +22,7 @@ These generated files serve as a good reference for writing channels and their
 corresponding tests. Let's go ahead and generate our Channel:
 
 ```console
-$ mix phoenix.gen.channel Room
+$ mix phx.gen.channel Room
 * creating lib/hello_web/channels/room_channel.ex
 * creating test/hello_web/channels/room_channel_test.exs
 
@@ -44,7 +44,7 @@ Where does this come from?
 When we generate a new Phoenix application, a `test/support/channel_case.ex` file is
 also generated for us. This file houses the `MyAppWeb.ChannelCase` module which we will
 use for all our integration tests for our channels. It automatically imports conveniences
-for testing channels and the Ecto context and query functions(if we use Ecto).
+for testing channels.
 
 Some of the helper functions provided there are for triggering callback functions in our
 channel. The others are there to provide us with special assertions that apply only to channels.
diff --git a/guides/docs/testing/testing_controllers.md b/guides/docs/testing/testing_controllers.md
index 555335041..b6a9c4a03 100644
--- a/guides/docs/testing/testing_controllers.md
+++ b/guides/docs/testing/testing_controllers.md
@@ -4,34 +4,41 @@ We're going to take a look at how we might test drive a controller which has end
 
 Phoenix has a generator for creating a JSON resource which looks like this:
 
-### Set up
-```bash
-mix phoenix.gen.json Thing things some_attr:string another_attr:string
+```console
+mix phoenix.gen.json Stuff Thing things some_attr:string another_attr:string
 ```
 
-Thing is the Model, things is the table name, and some_attr and another_attr are database columns on table things of type string. Don't run this command, we're going to explore test driving out a similar result to what a generator would give us.
+Stuff is the context module name, Thing is the schema module name, things is the table name, and some_attr and another_attr are database columns on table things of type string. Don't run this command, we're going to explore test driving out a similar result to what a generator would give us.
+
+### Set up
+Let's create a `User` schema. Since schema creation is not in scope of this guide, we will use the generator.  If you aren't familiar, read [this section of the Mix guide](mix_tasks.html#phoenix-specific-mix-tasks).
+
+```console
+$ mix phx.gen.schema Accounts.User users name:string email:string
+* creating lib/hello/accounts/user.ex
+* creating priv/repo/migrations/20170908031352_create_users.exs
 
-Let's create a `User` model. Since model creation is not in scope of this guide, we will use the generator. If you aren't familiar, read [this section of the Mix guide](mix_tasks.html#phoenix-specific-mix-tasks).
+Remember to update your repository by running migrations:
 
-```bash
-$ mix phoenix.gen.model User users name:string email:string
+    $ mix ecto.migrate
 ```
 
 Now run migrations:
 
-```bash
+```console
 $ mix ecto.migrate
+[info] == Running Hello.Repo.Migrations.CreateUsers.change/0 forward
+[info] create table users
+[info] == Migrated in 0.0s
 ```
 
 ### Test driving
 
-What we are going for is a controller with the standard CRUD actions. We'll start with our test since we're TDDing this. Create a `user_controller_test.exs` file in `test/controllers`
+What we are going for is a controller with the standard CRUD actions. We'll start with our test since we're TDDing this. Create a `user_controller_test.exs` file in `test/hello_web/controllers`
 
 ```elixir
-# test/controllers/user_controller_test.exs
-
-defmodule HelloPhoenix.UserControllerTest do
-  use HelloPhoenix.ConnCase, async: true
+defmodule HelloWeb.UserControllerTest do
+  use HelloWeb.ConnCase, async: true
 
 end
 ```
@@ -39,10 +46,8 @@ end
 There are many ways to approach TDD. Here, we will think about each action we want to perform, and handle the "happy path" where things go as planned, and the error case where something goes wrong, if applicable.
 
 ```elixir
-# test/controllers/user_controller_test.exs
-
-defmodule HelloPhoenix.UserControllerTest do
-  use HelloPhoenix.ConnCase, async: true
+defmodule HelloWeb.UserControllerTest do
+  use HelloWeb.ConnCase, async: true
 
   test "index/2 responds with all Users"
 
@@ -72,19 +77,19 @@ Create, show and update have more typical ways to fail because they need a way t
 
 Let's run the test:
 
-```bash
+```console
 $ mix test test/controllers/user_controller_test.exs
 ```
 
-We get 8 failures that say "Not yet implemented" which is good. Our tests don't have blocks yet.
+We get 8 failures that say "Not implemented" which is good. Our tests don't have blocks yet.
 
 Let's add our first test. We'll start with `index/2`.
 
 ```elixir
-defmodule HelloPhoenix.UserControllerTest do
-  use HelloPhoenix.ConnCase, async: true
+defmodule HelloWeb.UserControllerTest do
+  use HelloWeb.ConnCase, async: true
 
-  alias HelloPhoenix.{Repo, User}
+  alias Hello.{Repo, Accounts.User}
 
   test "index/2 responds with all Users" do
     users = [ User.changeset(%User{}, %{name: "John", email: "john@example.com"}),
@@ -92,8 +97,8 @@ defmodule HelloPhoenix.UserControllerTest do
 
     Enum.each(users, &Repo.insert!(&1))
 
-    response = build_conn
-    |> get(user_path(build_conn, :index))
+    response = build_conn()
+    |> get(user_path(build_conn(), :index))
     |> json_response(200)
 
     expected = %{
@@ -115,8 +120,8 @@ When we run the test we get an error that we have no `user_path` function.
 In our router, we'll add a resource for `User` in our API pipe:
 
 ```elixir
-defmodule HelloPhoenix.Router do
-  use HelloPhoenix.Web, :router
+defmodule HelloWeb.Router do
+  use HelloWeb, :router
 
   pipeline :browser do
     plug :accepts, ["html"]
@@ -128,19 +133,19 @@ defmodule HelloPhoenix.Router do
 
   pipeline :api do
     plug :accepts, ["json"]
-    resources "/users", HelloPhoenix.UserController
+    resources "/users", HelloWeb.UserController
   end
 
-  #...
+  ...
 ```
 
 We should get a new error now. Running the test informs us we don't have a `UserController`. Let's add it, along with the `index/2` action we're testing. Our test description has us returning all users:
 
 ```elixir
-defmodule HelloPhoenix.UserController do
-  use HelloPhoenix.Web, :controller
+defmodule HelloWeb.UserController do
+  use HelloWeb, :controller
 
-  alias HelloPhoenix.{User, Repo}
+  alias Hello.{Accounts.User, Repo}
 
   def index(conn, _params) do
     users = Repo.all(User)
@@ -153,11 +158,11 @@ end
 When we run the test again, our failing test tells us we have no view. Let's add it. Our test specifies a JSON format with a top key of `"data"`, containing an array of users with attributes `"name"` and `"email"`.
 
 ```elixir
-defmodule HelloPhoenix.UserView do
-  use HelloPhoenix.Web, :view
+defmodule HelloWeb.UserView do
+  use HelloWeb, :view
 
   def render("index.json", %{users: users}) do
-    %{data: render_many(users, HelloPhoenix.UserView, "user.json")}
+    %{data: render_many(users, HelloWeb.UserView, "user.json")}
   end
 
   def render("user.json", %{user: user}) do
@@ -182,11 +187,11 @@ Our show tests currently look like this:
 
 Run this test only by running the following command: (if your show tests don't start on line 32, change the line number accordingly)
 
-```bash
-$ mix test test/controllers/user_controller_test.exs:32
+```console
+$ mix test test/hello_web/controllers/user_controller_test.exs:32
 ```
 
-Our first `show/2` test result is, as expected, not yet implemented.
+Our first `show/2` test result is, as expected, not implemented.
 Let's build a test around what we think a successful `show/2` should look like.
 
 ```elixir
@@ -194,8 +199,8 @@ test "Responds with a newly created user if the user is found" do
   user = User.changeset(%User{}, %{name: "John", email: "john@example.com"})
   |> Repo.insert!
 
-  response = build_conn
-  |> get(user_path(build_conn, :show, user.id))
+  response = build_conn()
+  |> get(user_path(build_conn(), :show, user.id))
   |> json_response(200)
 
   expected = %{ "data" => %{ "name" => "John", "email" => "john@example.com" } }
@@ -209,10 +214,10 @@ This is very similar to our `index/2` test, except `show/2` requires a user id,
 When we run our test tells us we need a `show/2` action.
 
 ```elixir
-defmodule HelloPhoenix.UserController do
-  use HelloPhoenix.Web, :controller
+defmodule HelloWeb.UserController do
+  use HelloWeb, :controller
 
-  alias HelloPhoenix.{User, Repo}
+  alias Hello.{Accounts.User, Repo}
 
   def index(conn, _params) do
     users = Repo.all(User)
@@ -232,15 +237,15 @@ You'll notice we only handle the case where we successfully find a user. When we
 Running the test tells us we need a `render/2` function that can pattern match on `"show.json"`:
 
 ```elixir
-defmodule HelloPhoenix.UserView do
-  use HelloPhoenix.Web, :view
+defmodule HelloWeb.UserView do
+  use HelloWeb, :view
 
   def render("index.json", %{users: users}) do
-    %{data: render_many(users, HelloPhoenix.UserView, "user.json")}
+    %{data: render_many(users, HelloWeb.UserView, "user.json")}
   end
 
   def render("show.json", %{user: user}) do
-    %{data: render_one(user, HelloPhoenix.UserView, "user.json")}
+    %{data: render_one(user, HelloWeb.UserView, "user.json")}
   end
 
   def render("user.json", %{user: user}) do
@@ -260,8 +265,8 @@ Walking through our TDD steps, we add a test that supplies a non existent user i
 
 ```elixir
 test "Responds with a message indicating user not found" do
-  response = build_conn
-  |> get(user_path(build_conn, :show, 300))
+  response = build_conn()
+  |> get(user_path(build_conn(), :show, 300))
   |> json_response(404)
 
   expected = %{ "error" => "User not found." }
diff --git a/guides/docs/testing/testing_models.md b/guides/docs/testing/testing_models.md
deleted file mode 100644
index 6b462089c..000000000
--- a/guides/docs/testing/testing_models.md
+++ /dev/null
@@ -1,495 +0,0 @@
-# Testing Models
-
-In the [Ecto Models Guide](ecto_models.html) we generated an HTML resource for users. This gave us a number of modules for free, including a user model and a user model test case. In this guide, we'll use the model and test case to work through the changes we made in the Ecto Models Guide in a test-driven way.
-
-For those of us who haven't worked through the Ecto Models Guide, it's easy to catch up. Please see the "Generating an HTML Resource" section below.
-
-Before we do anything else, let's run `mix test` to make sure our test suite runs cleanly.
-
-```console
-$ mix test
-................
-
-Finished in 0.6 seconds (0.5s on load, 0.1s on tests)
-16 tests, 0 failures
-
-Randomized with seed 638414
-```
-
-Great. We've got sixteen tests and they are all passing!
-
-## Test Driving a Changeset
-
-The focus of this guide is going to be on `test/models/user_test.exs`. Let's take a quick look to get familiar with it.
-
-```elixir
-defmodule HelloPhoenix.UserTest do
-  use HelloPhoenix.ModelCase
-
-  alias HelloPhoenix.User
-
-  @valid_attrs %{bio: "some content", email: "some content", name: "some content", number_of_pets: 42}
-  @invalid_attrs %{}
-
-  test "changeset with valid attributes" do
-    changeset = User.changeset(%User{}, @valid_attrs)
-    assert changeset.valid?
-  end
-
-  test "changeset with invalid attributes" do
-    changeset = User.changeset(%User{}, @invalid_attrs)
-    refute changeset.valid?
-  end
-end
-```
-
-In the first line, we `use HelloPhoenix.ModelCase`, which is defined in `test/support/model_case.ex`. `HelloPhoenix.ModelCase` is responsible for importing and aliasing all the necessary modules for all of our model cases. `HelloPhoenix.ModelCase` will also run all of our model tests within a database transaction unless we've tagged an individual test case with `:async`.
-
-> Note: We should not tag any model case that interacts with a database as `:async`. This may cause  erratic test results and possibly even deadlocks.
-
-`HelloPhoenix.ModelCase` is also a place to define any helper functions we might need to test our models. We get an example function `errors_on/2` for free, and we'll see how that works shortly.
-
-We alias our `HelloPhoenix.User` module so that we can refer to its structs as `%User{}` instead of `%HelloPhoenix.User{}`.
-
-We also define module attributes for `@valid_attrs` and `@invalid_attrs` so they will be available to all our tests.
-
-The generated test attributes we get from `HelloPhoenix.UserTest` are certainly usable as is, but let's change them to look just a bit more realistic. The only one that will really matter is `:email`, as that will need to have an `@` before we're done. The other changes are just cosmetic.
-
-```elixir
-defmodule HelloPhoenix.UserTest do
-  use HelloPhoenix.ModelCase
-
-  alias HelloPhoenix.User
-
-  @valid_attrs %{bio: "my life", email: "pat@example.com", name: "Pat Example", number_of_pets: 4}
-  @invalid_attrs %{}
-
-  ...
-end
-```
-
-We should change the `@valid_attrs` module attribute in `test/controllers/user_controller_test.exs` to match these as well for consistency.
-
-```elixir
-defmodule HelloPhoenix.UserControllerTest do
-  use HelloPhoenix.ConnCase
-
-  alias HelloPhoenix.User
-  @valid_attrs %{bio: "my life", email: "pat@example.com", name: "Pat Example", number_of_pets: 4}
-  @invalid_attrs %{}
-
-  ...
-end
-```
-
-If we run the tests again, all sixteen should still pass.
-
-#### Number of Pets
-
-While Phoenix generated our model with all of the fields required, the number of pets a user has is optional in our domain.
-
-Let's write a new test to verify that.
-
-To test this, we can delete the `:number_of_pets` key and value from the `@valid_attrs` map and make a `User` changeset from those new attributes. Then we can assert that the changeset is still valid.
-
-```elixir
-defmodule HelloPhoenix.UserTest do
-  ...
-
-  test "number_of_pets is not required" do
-    changeset = User.changeset(%User{}, Map.delete(@valid_attrs, :number_of_pets))
-    assert changeset.valid?
-  end
-end
-```
-
-Now, let's run the tests again.
-
-```console
-$ mix test
-.............
-
-  1) test number_of_pets is not required (HelloPhoenix.UserTest)
-     test/models/user_test.exs:19
-     Expected truthy, got false
-     code: changeset.valid?()
-     stacktrace:
-       test/models/user_test.exs:21
-
-...
-
-Finished in 0.4 seconds (0.2s on load, 0.1s on tests)
-17 tests, 1 failure
-
-Randomized with seed 780208
-```
-
-It fails - which is exactly what it should do! We haven't written the code to make it pass yet. To do that, we need to remove the `:number_of_pets` attribute from our `validate_required/3` function in `lib/hello_web/models/user.ex`.
-
-```elixir
-defmodule HelloPhoenix.User do
-  ...
-
-  def changeset(struct, params \\ %{}) do
-    struct
-    |> cast(params, [:name, :email, :bio, :number_of_pets])
-    |> validate_required([:name, :email, :bio])
-  end
-end
-```
-
-Now our tests are all passing again.
-
-```console
-$ mix test
-.................
-
-Finished in 0.3 seconds (0.2s on load, 0.09s on tests)
-17 tests, 0 failures
-
-Randomized with seed 963040
-```
-
-#### The Bio Attribute
-
-In the Ecto Models Guide, we learned that the user's `:bio` attribute has two business requirements. The first is that it must be at least two characters long. Let's write a test for that using the same pattern we've just used.
-
-First, we change the `:bio` attribute to have a value of a single character. Then we create a changeset with the new attributes and test its validity.
-
-```elixir
-defmodule HelloPhoenix.UserTest do
-  ...
-
-  test "bio must be at least two characters long" do
-    attrs = %{@valid_attrs | bio: "I"}
-    changeset = User.changeset(%User{}, attrs)
-    refute changeset.valid?
-  end
-end
-```
-
-When we run the test, it fails, as we would expect.
-
-```console
-$ mix test
-.....
-
-  1) test bio must be at least two characters long (HelloPhoenix.UserTest)
-     test/models/user_test.exs:24
-     Expected false or nil, got true
-     code: changeset.valid?()
-     stacktrace:
-       test/models/user_test.exs:27
-
-............
-
-Finished in 0.3 seconds (0.2s on load, 0.09s on tests)
-18 tests, 1 failure
-
-Randomized with seed 327779
-```
-
-Hmmm. Yes, this test behaved as we expected, but the error message doesn't seem to reflect our test. We're validating the length of the `:bio` attribute, and the message we get is "Expected false or nil, got true". There's no mention of our `:bio` attribute at all.
-
-We can do better.
-
-Let's change our test to get a better message while still testing the same behavior. We can leave the code to set the new `:bio` value in place. In the `assert`, however, we'll use the `errors_on/2` function we get from `ModelCase` to generate a list of errors, and check that the `:bio` attribute error is in that list.
-
-```elixir
-defmodule HelloPhoenix.UserTest do
-  ...
-
-  test "bio must be at least two characters long" do
-    attrs = %{@valid_attrs | bio: "I"}
-    assert {:bio, "should be at least 2 character(s)"} in errors_on(%User{}, attrs)
-  end
-end
-```
-
-> Note: `ModelCase.errors_on/2` returns a keyword list, and an individual element of a keyword list is a tuple.
-
-When we run the tests again, we get a different message entirely.
-
-```console
-$ mix test
-...............
-
-  1) test bio must be at least two characters long (HelloPhoenix.UserTest)
-     test/models/user_test.exs:24
-     Assertion with in failed
-     code: {:bio, "should be at least 2 character(s)"} in errors_on(%User{}, attrs)
-     lhs:  {:bio,
-            "should be at least 2 character(s)"}
-     rhs:  []
-
-..
-
-Finished in 0.4 seconds (0.2s on load, 0.1s on tests)
-18 tests, 1 failure
-
-Randomized with seed 435902
-```
-
-This shows us the assertion we are testing - that our error is in the list of errors from the model's changeset.
-
-```console
-code: {:bio, "should be at least 2 character(s)"} in errors_on(%User{}, attrs)
-```
-
-We see that the left hand side of the expression evaluates to our error.
-
-```console
-lhs:  {:bio, "should be at least 2 character(s)"}
-```
-
-And we see that the right hand side of the expression evaluates to an empty list.
-
-```console
-rhs:  []
-```
-
-That list is empty because we don't yet validate the minimum length of the `:bio` attribute.
-
-Our test has pointed the way. Now let's make it pass by adding that validation.
-
-```elixir
-defmodule HelloPhoenix.User do
-  ...
-
-  def changeset(struct, params \\ %{}) do
-    struct
-    |> cast(params, [:name, :email, :bio, :number_of_pets])
-    |> validate_required([:name, :email, :bio])
-    |> validate_length(:bio, min: 2)
-  end
-end
-```
-
-When we run the tests again, they all pass.
-
-```console
-$ mix test
-..................
-
-Finished in 0.3 seconds (0.2s on load, 0.09s on tests)
-18 tests, 0 failures
-
-Randomized with seed 305958
-```
-
-The other business requirement for the `:bio` field is that it be a maximum of one hundred and forty characters. Let's write a test for that using the `errors_on/2` function again.
-
-Before we actually write the test, how are we going to handle a string that long without making a mess? A new function in `HelloPhoenix.ModelCase` is perfect for this. We'll create a `long_string/1` function which will send us back a string of "a"'s as long as we tell it to be.
-
-```elixir
-defmodule HelloPhoenix.ModelCase do
-  ...
-
-  def long_string(length) do
-    Enum.reduce (1..length), "", fn _, acc ->  acc <> "a" end
-  end
-end
-```
-
-We can now use `long_string/1` when changing the value of the `:bio` key in our `attrs`.
-
-```elixir
-defmodule HelloPhoenix.UserTest do
-  ...
-
-  test "bio must be at most 140 characters long" do
-    attrs = %{@valid_attrs | bio: long_string(141)}
-    assert {:bio, "should be at most 140 character(s)"} in errors_on(%User{}, attrs)
-  end
-end
-```
-
-When we run the test, it fails as we want it to.
-
-```console
-$ mix test
-....
-
-  1) test bio must be at most 140 characters long (HelloPhoenix.UserTest)
-     test/models/user_test.exs:29
-     Assertion with in failed
-     code: {:bio, {:bio, "should be at most 140 character(s)"} in errors_on(%User{}, attrs)
-     lhs:  {:bio,
-            "should be at most 120 character(s)"}
-
-..............
-
-Finished in 0.3 seconds (0.2s on load, 0.1s on tests)
-19 tests, 1 failure
-
-Randomized with seed 593838
-```
-
-To make this test pass, we need to add a new validation for the maximum length of the `:bio` attribute.
-
-```elixir
-defmodule HelloPhoenix.User do
-  ...
-
-  def changeset(struct, params \\ %{}) do
-    struct
-    |> cast(params, [:name, :email, :bio, :number_of_pets])
-    |> validate_required([:name, :email, :bio])
-    |> validate_length(:bio, min: 2)
-    |> validate_length(:bio, max: 140)
-  end
-end
-```
-
-When we run the tests, they all pass.
-
-```console
-$ mix test
-...................
-
-Finished in 0.4 seconds (0.3s on load, 0.1s on tests)
-19 tests, 0 failures
-
-Randomized with seed 468975
-```
-
-#### The Email Attribute
-
-We have one last attribute to validate. Currently, `:email` is just a string like any other. We'd like to make sure that it at least matches an "@". This is no substitute for an email confirmation, but it will weed out some invalid addresses before we even try.
-
-This process will feel familiar by now. First, we change the value of the `:email` attribute to omit the "@". Then we write an assertion which uses `errors_on/2` to check for the correct validation error on the `:email` attribute.
-
-```elixir
-defmodule HelloPhoenix.UserTest do
-  ...
-
-  test "email must contain at least an @" do
-    attrs = %{@valid_attrs | email: "fooexample.com"}
-    assert {:email, "has invalid format"} in errors_on(%User{}, attrs)
-  end
-end
-```
-
-When we run the tests, it fails. We see that we're getting an empty list of errors back from `errors_on/2`.
-
-```console
-$ mix test
-................
-
-  1) test email must contain at least an @ (HelloPhoenix.UserTest)
-     test/models/user_test.exs:34
-     Assertion with in failed
-     code: {:email, "has invalid format"} in errors_on(%User{}, attrs)
-     lhs:  {:email, "has invalid format"}
-     rhs:  []
-     stacktrace:
-       test/models/user_test.exs:36
-
-...
-
-Finished in 0.4 seconds (0.2s on load, 0.1s on tests)
-20 tests, 1 failure
-
-Randomized with seed 962127
-```
-
-Then we add the new validation to generate the error our test is looking for.
-
-```elixir
-defmodule HelloPhoenix.User do
-  ...
-
-  def changeset(struct, params \\ %{}) do
-    struct
-    |> cast(params, [:name, :email, :bio, :number_of_pets])
-    |> validate_required([:name, :email, :bio])
-    |> validate_length(:bio, min: 2)
-    |> validate_length(:bio, max: 140)
-    |> validate_format(:email, ~r/@/)
-  end
-end
-```
-
-Now all the tests are passing again.
-
-```console
-$ mix test
-....................
-
-Finished in 0.3 seconds (0.2s on load, 0.09s on tests)
-20 tests, 0 failures
-
-Randomized with seed 330955
-```
-
-### Generating an HTML Resource
-
-For this section, we're going to assume that we all have a PostgreSQL database installed on our system, and that we generated a default application - one in which Ecto and Postgrex are installed and configured automatically.
-
-If this is not the case, please see the section on adding Ecto and Postgrex of the [Ecto Models Guide](ecto_models.html) and join us when that's done.
-
-Ok, once we're all configured properly, we need to run the `phoenix.gen.html` task with the list of attributes we have here.
-
-```console
-$ mix phoenix.gen.html User users name:string email:string bio:string number_of_pets:integer
-* creating priv/repo/migrations/20150409213440_create_user.exs
-* creating web/models/user.ex
-* creating test/models/user_test.exs
-* creating web/controllers/user_controller.ex
-* creating web/templates/user/edit.html.eex
-* creating web/templates/user/form.html.eex
-* creating web/templates/user/index.html.eex
-* creating web/templates/user/new.html.eex
-* creating web/templates/user/show.html.eex
-* creating web/views/user_view.ex
-* creating test/controllers/user_controller_test.exs
-
-Add the resource to your browser scope in web/router.ex:
-
-    resources "/users", UserController
-
-Remember to update your repository by running migrations:
-
-    $ mix ecto.migrate
-```
-
-Then we need to follow the instructions the task gives us and insert the `resources "/users", UserController` line in the router `lib/hello_web/router.ex`.
-
-```elixir
-defmodule HelloWeb.Router do
-  ...
-
-  scope "/", HelloWeb do
-    pipe_through :browser # Use the default browser stack
-
-    get "/", PageController, :index
-    resources "/users", UserController
-  end
-
-  # Other scopes may use custom stacks.
-  # scope "/api", HelloWeb do
-  #   pipe_through :api
-  # end
-end
-```
-
-With that done, we can create our database with `ecto.create`.
-
-```console
-$ mix ecto.create
-The database for HelloPhoenix.Repo has been created.
-```
-
-Then we can migrate our database to create our `users` table with `ecto.migrate`.
-
-```console
-$ mix ecto.migrate
-
-[info]  == Running HelloPhoenix.Repo.Migrations.CreateUser.change/0 forward
-
-[info]  create table users
-
-[info]  == Migrated in 0.0s
-```
-
-With that, we are ready to continue with the testing guide.
diff --git a/guides/docs/testing/testing_schemas.md b/guides/docs/testing/testing_schemas.md
new file mode 100644
index 000000000..3f3144006
--- /dev/null
+++ b/guides/docs/testing/testing_schemas.md
@@ -0,0 +1,535 @@
+# Testing Schemas
+
+In the [Ecto Guide](ecto.html) we generated an HTML resource for users. This gave us a number of modules for free, including a user schema and a user schema test case. In this guide, we'll use the schema and test case to work through the changes we made in the Ecto Guide in a test-driven way.
+
+For those of us who haven't worked through the Ecto Guide, it's easy to catch up. Please see the "Generating an HTML Resource" section below.
+
+Before we do anything else, let's run `mix test` to make sure our test suite runs cleanly.
+
+```console
+$ mix test
+................
+
+Finished in 0.6 seconds
+20 tests, 0 failures
+
+Randomized with seed 638414
+```
+
+Great. We've got twenty tests and they are all passing!
+
+## Test Driving a Changeset
+
+We'll be adding additional validations to the schema module, so let's create `test/hello/accounts/user_test.exs` with this content:
+
+```elixir
+defmodule Hello.Accounts.UserTest do
+  use Hello.DataCase
+
+  alias Hello.Accounts.User
+
+  @valid_attrs %{bio: "my life", email: "pat@example.com", name: "Pat Example", number_of_pets: 4}
+  @invalid_attrs %{}
+
+  test "changeset with valid attributes" do
+    changeset = User.changeset(%User{}, @valid_attrs)
+    assert changeset.valid?
+  end
+
+  test "changeset with invalid attributes" do
+    changeset = User.changeset(%User{}, @invalid_attrs)
+    refute changeset.valid?
+  end
+end
+```
+
+In the first line, we `use Hello.DataCase`, which is defined in `test/support/data_case.ex`. `Hello.DataCase` is responsible for importing and aliasing all the necessary modules for all of our schema cases. `Hello.DataCase` will also run all of our schema tests within a database transaction unless we've tagged an individual test case with `:async`.
+
+> Note: We should not tag any schema case that interacts with a database as `:async`. This may cause  erratic test results and possibly even deadlocks.
+
+`Hello.DataCase` is also a place to define any helper functions we might need to test our schemas. We get an example function `errors_on/1` for free, and we'll see how that works shortly.
+
+We alias our `Hello.Accounts.User` module so that we can refer to its structs as `%User{}` instead of `%Hello.Accounts.User{}`.
+
+We also define module attributes for `@valid_attrs` and `@invalid_attrs` so they will be available to all our tests.
+
+If we run the tests again, we've got 22, and they should all pass.
+
+#### Number of Pets
+
+While Phoenix generated our model with all of the fields required, the number of pets a user has is optional in our domain.
+
+Let's write a new test to verify that.
+
+To test this, we can delete the `:number_of_pets` key and value from the `@valid_attrs` map and make a `User` changeset from those new attributes. Then we can assert that the changeset is still valid.
+
+```elixir
+defmodule Hello.Accounts.UserTest do
+  ...
+
+  test "number_of_pets is not required" do
+    changeset = User.changeset(%User{}, Map.delete(@valid_attrs, :number_of_pets))
+    assert changeset.valid?
+  end
+end
+```
+
+Now, let's run the tests again.
+
+```console
+$ mix test
+....................
+
+  1) test number_of_pets is not required (Hello.Accounts.UserTest)
+     test/hello/accounts/user_test.exs:19
+     Expected truthy, got false
+     code: assert changeset.valid?()
+     stacktrace:
+       test/hello/accounts/user_test.exs:21: (test)
+..
+
+Finished in 0.4 seconds
+23 tests, 1 failure
+
+Randomized with seed 780208
+```
+
+It fails - which is exactly what it should do! We haven't written the code to make it pass yet. To do that, we need to remove the `:number_of_pets` attribute from our `validate_required/3` function in `lib/hello_web/models/user.ex`.
+
+```elixir
+defmodule Hello.Accounts.User do
+  ...
+
+  def changeset(struct, params \\ %{}) do
+    struct
+    |> cast(params, [:name, :email, :bio, :number_of_pets])
+    |> validate_required([:name, :email, :bio])
+  end
+end
+```
+
+Now our tests are all passing again.
+
+```console
+$ mix test
+.......................
+
+Finished in 0.3 seconds
+23 tests, 0 failures
+
+Randomized with seed 963040
+```
+
+#### The Bio Attribute
+
+In the Ecto Guide, we learned that the user's `:bio` attribute has two business requirements. The first is that it must be at least two characters long. Let's write a test for that using the same pattern we've just used.
+
+First, we change the `:bio` attribute to have a value of a single character. Then we create a changeset with the new attributes and test its validity.
+
+```elixir
+defmodule Hello.Accounts.UserTest do
+  ...
+
+  test "bio must be at least two characters long" do
+    attrs = %{@valid_attrs | bio: "I"}
+    changeset = User.changeset(%User{}, attrs)
+    refute changeset.valid?
+  end
+end
+```
+
+When we run the test, it fails, as we would expect.
+
+```console
+$ mix test
+...................
+
+  1) test bio must be at least two characters long (Hello.Accounts.UserTest)
+     test/hello/accounts/user_test.exs:24
+     Expected false or nil, got true
+     code: refute changeset.valid?()
+     stacktrace:
+       test/hello/accounts/user_test.exs:27: (test)
+
+....
+
+Finished in 0.3 seconds
+24 tests, 1 failure
+
+Randomized with seed 327779
+```
+
+Hmmm. Yes, this test behaved as we expected, but the error message doesn't seem to reflect our test. We're validating the length of the `:bio` attribute, and the message we get is "Expected false or nil, got true". There's no mention of our `:bio` attribute at all.
+
+We can do better.
+
+Let's change our test to get a better message while still testing the same behavior. We can leave the code to set the new `:bio` value in place. In the `assert`, however, we'll use the `errors_on/1` function we get from `DataCase` to generate a map of errors, and check that the `:bio` attribute error is in that map.
+
+```elixir
+defmodule Hello.Accounts.UserTest do
+  ...
+
+  test "bio must be at least two characters long" do
+    attrs = %{@valid_attrs | bio: "I"}
+    changeset = User.changeset(%User{}, attrs)
+    assert %{bio: ["should be at least 2 character(s)"]} = errors_on(changeset)
+  end
+end
+```
+
+When we run the tests again, we get a different message entirely.
+
+```console
+$ mix test
+...................
+
+  1) test bio must be at least two characters long (Hello.Accounts.UserTest)
+     test/hello/accounts/user_test.exs:24
+     match (=) failed
+     code:  assert %{bio: ["should be at least 2 character(s)"]} = errors_on(changeset)
+     right: %{}
+     stacktrace:
+       test/hello/accounts/user_test.exs:27: (test)
+
+....
+
+Finished in 0.4 seconds
+24 tests, 1 failure
+
+Randomized with seed 435902
+```
+
+This shows us the assertion we are testing - that our error is in the map of errors from the model's changeset.
+
+```console
+code:  assert %{bio: ["should be at least 2 character(s)"]} = errors_on(changeset)
+```
+
+And we see that the right hand side of the expression evaluates to an empty map.
+
+```console
+rhs:  %{}
+```
+
+That map is empty because we don't yet validate the minimum length of the `:bio` attribute.
+
+Our test has pointed the way. Now let's make it pass by adding that validation.
+
+```elixir
+defmodule Hello.Accounts.User do
+  ...
+
+  def changeset(struct, params \\ %{}) do
+    struct
+    |> cast(params, [:name, :email, :bio, :number_of_pets])
+    |> validate_required([:name, :email, :bio])
+    |> validate_length(:bio, min: 2)
+  end
+end
+```
+
+When we run the tests again, they all pass.
+
+```console
+$ mix test
+........................
+
+Finished in 0.2 seconds
+24 tests, 0 failures
+
+Randomized with seed 305958
+```
+
+The other business requirement for the `:bio` field is that it be a maximum of one hundred and forty characters. Let's write a test for that using the `errors_on/1` function again.
+
+Before we actually write the test, how are we going to handle a string that long without making a mess? A new function in `Hello.DataCase` is perfect for this. We'll create a `long_string/1` function which will send us back a string of "a"'s as long as we tell it to be.
+
+```elixir
+defmodule Hello.DataCase do
+  ...
+
+  def long_string(length) do
+    Enum.reduce (1..length), "", fn _, acc ->  acc <> "a" end
+  end
+end
+```
+
+We can now use `long_string/1` when changing the value of the `:bio` key in our `attrs`.
+
+```elixir
+defmodule Hello.Accounts.UserTest do
+  ...
+
+  test "bio must be at most 140 characters long" do
+    attrs = %{@valid_attrs | bio: long_string(141)}
+    changeset = User.changeset(%User{}, attrs)
+    assert %{bio: ["should be at most 140 character(s)"]} = errors_on(changeset)
+  end
+end
+```
+
+When we run the test, it fails as we want it to.
+
+```console
+$ mix test
+.......................
+
+  1) test bio must be at most 140 characters long (Hello.Accounts.UserTest)
+     test/hello/accounts/user_test.exs:30
+     match (=) failed
+     code:  assert %{bio: ["should be at most 140 character(s)"]} = errors_on(changeset)
+     right: %{}
+     stacktrace:
+       test/hello/accounts/user_test.exs:33: (test)
+
+.
+
+Finished in 0.3 seconds
+25 tests, 1 failure
+
+Randomized with seed 593838
+```
+
+To make this test pass, we need to add a maximum to the length validation of the `:bio` attribute.
+
+```elixir
+defmodule Hello.Accounts.User do
+  ...
+
+  def changeset(struct, params \\ %{}) do
+    struct
+    |> cast(params, [:name, :email, :bio, :number_of_pets])
+    |> validate_required([:name, :email, :bio])
+    |> validate_length(:bio, min: 2, max: 140)
+  end
+end
+```
+
+When we run the tests, they all pass.
+
+```console
+$ mix test
+.........................
+
+Finished in 0.4 seconds
+25 tests, 0 failures
+
+Randomized with seed 468975
+```
+
+#### The Email Attribute
+
+We have one last attribute to validate. Currently, `:email` is just a string like any other. We'd like to make sure that it at least matches an "@". This is no substitute for an email confirmation, but it will weed out some invalid addresses before we even try.
+
+This process will feel familiar by now. First, we change the value of the `:email` attribute to omit the "@". Then we write an assertion which uses `errors_on/1` to check for the correct validation error on the `:email` attribute.
+
+```elixir
+defmodule Hello.Accounts.UserTest do
+  ...
+
+  test "email must contain at least an @" do
+    attrs = %{@valid_attrs | email: "fooexample.com"}
+    changeset = User.changeset(%User{}, attrs)
+    assert %{email: ["has invalid format"]} = errors_on(changeset)
+  end
+end
+```
+
+When we run the tests, it fails. We see that we're getting an empty map of errors back from `errors_on/1`.
+
+```console
+$ mix test
+.......................
+
+  1) test email must contain at least an @ (Hello.Accounts.UserTest)
+     test/hello/accounts/user_test.exs:36
+     match (=) failed
+     code:  assert %{email: ["has invalid format"]} = errors_on(changeset)
+     right: %{}
+     stacktrace:
+       test/hello/accounts/user_test.exs:39: (test)
+
+..
+
+Finished in 0.4 seconds
+26 tests, 1 failure
+
+Randomized with seed 962127
+```
+
+Then we add the new validation to generate the error our test is looking for.
+
+```elixir
+defmodule Hello.Accounts.User do
+  ...
+
+  def changeset(struct, params \\ %{}) do
+    struct
+    |> cast(params, [:name, :email, :bio, :number_of_pets])
+    |> validate_required([:name, :email, :bio])
+    |> validate_length(:bio, min: 2, max: 140)
+    |> validate_format(:email, ~r/@/)
+  end
+end
+```
+
+Now the schema tests are passing again, but other tests are now failing, if you haven't touched the generated context & controller tests. Here's one failure (but because tests are run in random order, you might see a different failure first):
+
+```console
+$ mix test
+....
+
+  1) test update user renders errors when data is invalid (HelloWeb.UserControllerTest)
+     test/hello_web/controllers/user_controller_test.exs:66
+     ** (MatchError) no match of right hand side value: {:error, #Ecto.Changeset<action: :insert, changes: %{bio: "some bio", email: "some email", name: "some name", number_of_pets: 42}, errors: [email: {"has invalid format", [validation: :format]}], data: #Hello.Accounts.User<>, valid?: false>}
+     stacktrace:
+       test/hello_web/controllers/user_controller_test.exs:11: HelloWeb.UserControllerTest.fixture/1
+       test/hello_web/controllers/user_controller_test.exs:85: HelloWeb.UserControllerTest.create_user/1
+       test/hello_web/controllers/user_controller_test.exs:1: HelloWeb.UserControllerTest.__ex_unit__/2
+  ...
+
+Finished in 0.1 seconds
+26 tests, 12 failures
+
+Randomized with seed 825065
+```
+
+We can fix these tests by editing the module attributes in the failing test files - first, in `test/hello_web/controllers/user_controller_test.exs`, add an "@" to the `:email` values in `@valid_attrs` and `@update_attrs`:
+
+```elixir
+defmodule HelloWeb.UserControllerTest do
+  ...
+  @create_attrs %{bio: "some bio", email: "some@email", name: "some name", number_of_pets: 42}
+  @update_attrs %{bio: "some updated bio", email: "some updated@email", name: "some updated name", number_of_pets: 43}
+  @invalid_attrs %{bio: nil, email: nil, name: nil, number_of_pets: nil}
+  ...
+```
+
+This will fix all of the HelloWeb.UserControllerTest failures.
+
+Make the same changes to the module attributes in `test/hello/accounts/accounts_test.exs`:
+
+```elixir
+defmodule Hello.AccountsTest do
+    ...
+    @valid_attrs %{bio: "some bio", email: "some@email", name: "some name", number_of_pets: 42}
+    @update_attrs %{bio: "some updated bio", email: "updated@email", name: "some updated name", number_of_pets: 43}
+    @invalid_attrs %{bio: nil, email: nil, name: nil, number_of_pets: nil}
+    ...
+```
+
+This will fix all but two of the failures - to fix those last two, we'll need to fix the values those tests are comparing:
+
+```elixir
+defmodule Hello.AccountsTest do
+  ...
+  test "create_user/1 with valid data creates a user" do
+    assert {:ok, %User{} = user} = Accounts.create_user(@valid_attrs)
+    assert user.bio == "some bio"
+    assert user.email == "some@email"
+    assert user.name == "some name"
+    assert user.number_of_pets == 42
+  end
+
+  ...
+
+  test "update_user/2 with valid data updates the user" do
+    user = user_fixture()
+    assert {:ok, user} = Accounts.update_user(user, @update_attrs)
+    assert %User{} = user
+    assert user.bio == "some updated bio"
+    assert user.email == "some updated@email"
+    assert user.name == "some updated name"
+    assert user.number_of_pets == 43
+  end
+
+end
+```
+
+Now all the tests pass again:
+
+```console
+$ mix test
+..........................
+
+Finished in 0.2 seconds
+26 tests, 0 failures
+
+Randomized with seed 330955
+```
+
+### Generating an HTML Resource
+
+For this section, we're going to assume that we all have a PostgreSQL database installed on our system, and that we generated a default application - one in which Ecto and Postgrex are installed and configured automatically.
+
+If this is not the case, please see the section on adding Ecto and Postgrex of the [Ecto Guide](ecto.html) and join us when that's done.
+
+Ok, once we're all configured properly, we need to run the `phx.gen.html` task with the list of attributes we have here.
+
+```console
+$ mix phx.gen.html Accounts User users name:string email:string \
+bio:string number_of_pets:integer
+* creating lib/hello_web/controllers/user_controller.ex
+* creating lib/hello_web/templates/user/edit.html.eex
+* creating lib/hello_web/templates/user/form.html.eex
+* creating lib/hello_web/templates/user/index.html.eex
+* creating lib/hello_web/templates/user/new.html.eex
+* creating lib/hello_web/templates/user/show.html.eex
+* creating lib/hello_web/views/user_view.ex
+* creating test/hello_web/controllers/user_controller_test.exs
+* creating lib/hello/accounts/user.ex
+* creating priv/repo/migrations/20170906212909_create_users.exs
+* creating lib/hello/accounts/accounts.ex
+* injecting lib/hello/accounts/accounts.ex
+* creating test/hello/accounts/accounts_test.exs
+* injecting test/hello/accounts/accounts_test.exs
+
+Add the resource to your browser scope in web/router.ex:
+
+    resources "/users", UserController
+
+Remember to update your repository by running migrations:
+
+    $ mix ecto.migrate
+```
+
+Then we need to follow the instructions the task gives us and insert the `resources "/users", UserController` line in the router `lib/hello_web/router.ex`.
+
+```elixir
+defmodule HelloWeb.Router do
+  ...
+
+  scope "/", HelloWeb do
+    pipe_through :browser # Use the default browser stack
+
+    get "/", PageController, :index
+    resources "/users", UserController
+  end
+
+  # Other scopes may use custom stacks.
+  # scope "/api", HelloWeb do
+  #   pipe_through :api
+  # end
+end
+```
+
+With that done, we can create our database with `ecto.create`.
+
+```console
+$ mix ecto.create
+The database for Hello.Repo has been created.
+```
+
+Then we can migrate our database to create our `users` table with `ecto.migrate`.
+
+```console
+$ mix ecto.migrate
+
+[info]  == Running Hello.Repo.Migrations.CreateUser.change/0 forward
+
+[info]  create table users
+
+[info]  == Migrated in 0.0s
+```
+
+With that, we are ready to continue with the testing guide.
diff --git a/guides/docs/testing/testing_views.md b/guides/docs/testing/testing_views.md
deleted file mode 100644
index 850d77166..000000000
--- a/guides/docs/testing/testing_views.md
+++ /dev/null
@@ -1,1 +0,0 @@
-## FPO