TDD by the 3 laws!
# Creating a new test
spruce create.test
# Runing tests
spruce test
Fixtures are utility classes to help you setup your environment for testing.
- View Fixture
- Store Fixture
- Mercury Fixture
- Person Fixture
- Location Fixture
- Organization Fixture
- Role Fixture
- Seed Fixture
Fixtures are available when extending AbstractSpruceFixtureTest
and anything that extends it (all abstract tests that come with the sdk extend this class).
Note that all built in fixtures are available via protected fields on the AbstractSpruceFixtureTest
.
this.views
=>ViewFixture
this.roles
=>RoleFixture
this.locations
=>LocationFixture
this.organizations
=>OrganizationFixture
this.people
=>PersonFixture
this.seeder
=>SeedFixture
this.skills
=>SkillFixture
this.mercury
=>MercuryFixture
export default class RenderingRootViewControllerTest extends AbstractSpruceFixtureTest {
@test()
protected static gettingFixtures() {
const organizationFixture = this.organizations
assert.isTruthy(organizationFixture)
//Save time by accessing the fixture via protected pro
assert.isTruthy(this.organizations)
assert.isTruthy(this.locations)
}
}
@login()
export default class MySkillViewControllerTest extends AbstractSpruceFixtureTest {
@test()
protected static async beforeEach() {
await super.beforeEach()
/**
* Is the exact same as @login decorator, don't bother doing this manually
* const { client } = await this.Fixture('view').loginAsDemoPerson(DEMO_NUMBER_ROOT_SVC)
* MercuryFixture.setDefaultClient(client)
**/
const client = login.getClient()
const { client: client2 } = await this.Fixture('view').loginAsDemoPerson()
assert.isEqual(client, client2) //once default client is set, unless you pass a new number, the client is reused
const { client: client3 } = await this.Fixture('view').loginAsDemoPerson(DEMO_NUMBER_ROOT_2)
assert.isNotEqual(client,client3)
}
}
Seeders for core data (people, locations, roles, etc.) are provided through some killer decorators.
You can also @seed
from any of your local stores.
//@login sets the default client for all fixtures and seeders going forward
@login()
export default class RenderingRootViewControllerTest extends AbstractSpruceFixtureTest {
@seed('organizations', 2)
protected static async beforeEach() {
await super.beforeEach()
const totalOrgs = await this.organizations.listOrganizations()
assert.isLength(totalOrgs, 2)
//since this is in the beforeEach(), every test will come with 2 organizations
}
@test()
@seed('locations',10)
protected static async locationsShouldSeed() {
const currentOrg = await this.organizations.getNewestOrganization()
const locations = await this.locations.listLocations({ organizationId: currentOrg?.id })
assert.isLength(locations, 10)
}
@test()
protected static async seedingEntireAccount() {
// will seed data under newest organization
const {
locations,
guests,
managers,
owners,
teammates
} = await this.seeder.seedAccount({
totalLocations: 1,
totalGuests: 3,
totalManagers: 5,
totalOwners: 2,
totalTeammetes: 3,
startingPhone: DEMO_NUMBER_SEED_STARTING_PHONE
})
}
}
@login()
export default class RenderingRootViewControllerTest extends AbstractSpruceFixtureTest {
@test()
@seed('organization',1)
@install.skills('skill-namespace-1', 'skill-namespace-2')
protected static async skillsArInstalled() {
//the skill is only installed at the newest organizatios
//now your skill can emit events to skills that are installed at the newest org
}
}
Everything you need to know is under the Views section!
- Create an abstract test for your skill on the first test where you need something twice.
- E.g.
AbstractProfileTest
- All your future tests should extend this test.
- Creating helpers like
getNewestInvite
andlistOrgs
is extremely helpful. - Set fixtures you need often to local props in
beforeEach()
:- Name the prop the plural name of the fixturce
this.views = this.Fixture('views')
this.orgs = this.Fixture('organizations')
- Set stores to local props in
beforeEach
.- Name the prop the name of the store:
this.invites = await this.Store('invites')
this.profiles = await this.Store('profiles')
- E.g.
- Don't create fixtures over and over, save them as protected properties on your Abstract Test.
- Create helpful getters for things you fetch over and over, e.g.
this.getNewestOrganization()
.- Use assertions and helpful error messages to guide future you through proper test setup.
This is an example of what your skill's test file may look like after a few tests.
export default class AbstractProfileTest extends AbstractViewControllerTest {
protected static profiles: ProfilesStore
protected static router: Router
protected static async beforeEach() {
await super.beforeEach()
this.profiles = await this.stores.Store('profiles')
this.router = this.views.getRouter()
}
protected static async getNewestProfile() {
const profile = await this.profiles.findOne({})
assert.isTruthy(profile, `You gotta @seed('profiles',1) to continue.`)
return profile
}
protected static async getNewestOrg() {
const org = await this.organizations.getNewestOrganization()
assert.isTruthy(org, `You gotta @seed('organizations',1) to continue.`)
return org
}
protected static async listProfiles () {
const profiles = await this.profiles.findOne({})
assert.isAbove(profiles.length, 0, `You gotta @seed('profiles',1) to continue.`)
return profiles
}
}