Skip to content

Testing Applications That Use ORM and OAuth 2.0

This document describes how to set up your test code to test applications that use the ORM and OAuth 2.0. These types of applications require extra initialization steps, e.g. set up a test database.

Testing Applications That Use the ORM

Conduit's ORM uses PostgreSQL as its database. Before your tests run, Conduit will create your application's database tables in a local PostgreSQL database. After the tests complete, it will delete those tables. This allows you to start with an empty database for each test suite as well as control exactly which records are in your database while testing, but without having to manage database schemas or use a mock implementation (e.g., SQLite).

!!! warning "You Must Install PostgreSQL Locally" On macOS, Postgres.app is a simple, self-contained PostgreSQL instance that you can run as a normal application. (See PostgreSQL installation for other platforms.)

Local Database for Tests

The same database is reused for testing all of your applications. You only have to create this database once per development machine, or when running in a CI tool like TravisCI. From PostgreSQL's prompt, run:

CREATE DATABASE dart_test;
CREATE USER dart WITH createdb;
ALTER USER dart WITH password 'dart';
GRANT all ON DATABASE dart_test TO dart;

A database configuration in your application's config.yaml.src must match the following:

username: dart
password: dart
host: localhost
port: 5432
databaseName: dart_test

Your application, when run with a subclass of TestHarness<T>, will configure its database connection to connect to the local test database. You must mixin TestHarnessORMMixin with your test harness and invoke resetData by overriding onSetUp. You may also override seed to insert test data into the database.

class Harness extends TestHarness<AppChannel> with TestHarnessORMMixin {
  @override
  ManagedContext get context => channel.context;

  @override
  Future onSetUp() async {
    await resetData();
  }

  @override
  Future seed() async {
    /* insert some rows here */
  }
}

!!! tip "Seeding Data" You should only seed static data in the seed method; this may include things like categories or country codes that cannot be changed during runtime. Data that is manipulated for specific test cases should be invoked in a test setUp callback or the test itself.

Local Database for Running an Application

A database separate from the test database should be used for running an application locally. You can create a database locally by running psql to open a PostgreSQL terminal and run the following commands:

CREATE DATABASE my_app_name;
CREATE USER my_app_user WITH PASSWORD 'mypassword';
GRANT ALL ON DATABASE my_app_name TO my_app_user;

Add your schema to the local database by generating and executing migration scripts:

conduit db generate
conduit db upgrade --connect postgres://my_app_user:mypassword@localhost:5432/my_app_name

Testing Applications That Use OAuth 2.0

Applications that use OAuth 2.0 should mixin TestHarnessAuthMixin. This mixin adds methods for registering a client identifier and authenticating a user. Both methods return an Agent with default headers with authorization information for the client identifier or user.

Most often, you use package:conduit_core/managed_auth for an ORM-driven OAuth2 delegate. You must also mixin TestHarnessORMMixin when using this mixin.

class Harness extends TestHarness<AppChannel> with TestHarnessAuthMixin<AppChannel>, TestHarnessORMMixin {
  @override
  ManagedContext get context => channel.context;

  @override
  AuthServer get authServer => channel.authServer;

  Agent publicAgent;

  @override
  Future onSetUp() async {    
    await resetData();
    publicAgent = await addClient("com.conduit.public");
  }

  Future<Agent> registerUser(User user, {Agent withClient}) async {
    withClient ??= publicAgent;

    final req = withClient.request("/register")
      ..body = {"username": user.username, "password": user.password};
    await req.post();

    return loginUser(withClient, user.username, user.password);
  }
}