# Testing

Upfront is fully tested to give as much confidence in the code as possible. To carry on this ethos in your application, upfront offers helpful tools to for testing with mock data and test outgoing requests.

# Testing service implementations

Swapping out services of upfront is easy as setting them in the GlobalConfig. For example if you want to test your custom method you added to a model, you could do something similar to the following:

# Factories

To make testing your code easier with real data independent of your back end, upfront provides factories. With factories, you can construct models hydrated with data that you have defined and/or using a random data generator like ChanceJs (opens new window) set in your configuration. With the factory defined, construction of a model is easy as User.factory().create()

To define a factory what you need to do is extend upfront's Factory in you class then in you model create a factory() method which returns your constructed factory.

# definition

The definition is the only required method on the factory. This base definition will be used by all your factory calls. In the above example definition() indicates that it takes 2 arguments. the first being an empty instance of the model you're creating. The second is an index starting from 1 which you may use to add some dynamic variable to your attributes (this is especially useful when creating multiple models in the same go e.g.: username: 'nickname_' + index').

Furthermore, you may define attributes as functions which will be called with the attributes resolved up to the point of calling your function. For example

return {
    firstName: () => 'user',
    lastName: 'name',
    fullName: (attributes) => {
        return attributes.firstName + ' ' + attributes.lastName // 'user name'
    }
}
1
2
3
4
5
6
7

If you would like to define relationships in your definition you could pass the relation's value, and the relation's foreign key.

const team = Team.factory().create()
return {
    teamId: team.getKey(),
    team: team // both Model and raw attributes are acceptable here
}
1
2
3
4
5

# States

States are a way to encapsulate some changes to the return value of your definition() method. It is resolved the same way as the definition, so you may use methods, and the previously resolved attributes.

To define a state in your factory add a method with your state name:

Then call your state like: User.factory().state('nameOverride').create() For more instructions on how to use the method see state.

# Factory Hooks

Factory hooks are methods that called when creating the models. There are two available hooks: afterMaking and afterCreating. These hooks if implemented in your factory will receive the result of the builder at their respective times. As these are passed by reference you, can change their values to your liking without having to return it.

# random

If in the configuration you have set the key randomDataGenerator, the value will be available to use in your factories like so:

import { Factory, ModelCollection } from '@upfrontjs/framework';

export default UserFactory extends Factory
{
    definition()
    {
        return {
            name: this.random.name()
        };
    }
}
1
2
3
4
5
6
7
8
9
10
11

With the factory defined you're ready to start creating models. by either the factory method or by the static factory method on your model Model.factory()

# FactoryBuilder

FactoryBuilder is a class that you can access by the factory helper method or by calling the factory method on your model constructor. This utility enables us to fluently build models. It offers a couple methods to that extent.

# times

The times method sets the amount of models that is returned by the builder. The builder by default returns 1 model. Setting this amount to more than 1 will cause the builder to return a ModelCollection. Floats are rounded to the nearest value.

User.factory().times(3).create() // ModelCollection
1

# with

The with method is a way to add relations in line to the model. This is an alternative to adding it to the argument at the creation, and is the preferred way as it's more concise and provides type safety. You can pass he model constructor or FactoryBuilder instance to the method. You may pass an optional second argument to specify the relation's name with or without the relation prefix

User.factory().with(Contract).create().team // Contract
User.factory().with(Shift.factory().times(2)).create().shifts // ModelCollection
1
2

WARNING

  • The passed in values will be constructed with the same method (raw, make, create) as the builder itself.
  • As expected a belongsTo relationship will need the foreign key to be set on the model you're building.

As with relations, it is going to respect the relation type, that is for example, if you pass a model constructor that is defined as hasMany relation, it will return a ModelCollection even if it's a builder with times set to 1.

TIP

Adding relation can also be added manually in the definition method, states and in the creation's argument.

# state

The state method applies the states defined in your factory. You may pass a string, or an array of strings as an argument.

User.factory().state('admin').create()
User.factory().state(['admin', 'withTeam']).create()
1
2

The following are the last methods to be called on the builder. These methods return the result of the build.

# raw

The raw method instruct the builder to construct your model's or models' data. It will return the attributes in an object literal or a Collection of them depending on the times method call or lack there of. This will not include any primary keys and timestamps.

# rawOne

The rawOne method instruct the builder the construct your model's attributes ignoring the specified times setting and will always return the attributes object literal.

# rawMany

The rawMany method instruct the builder the construct your model's attributes. This will always return a Collection regardless of the times setting.

# make

The make method instruct the builder to construct your model(s). It will return an instance of your model or a ModelCollection depending on the times method call or lack there of. This will not include any primary keys and timestamps.

# makeOne

The makeOne method instruct the builder to construct your model. It will always return an instance of your model regardless of the times setting. This will not include any primary keys and timestamps.

# makeMany

The makeMany method instruct the builder to construct your models. It will always return an instance of ModelCollection regardless of the times setting. This will not include any primary keys and timestamps.

# create

The create method instruct the builder to construct your model(s). It will return an instance of your model or a ModelCollection depending on the times method call or lack there of. This will include primary keys and timestamps.

# createOne

The createOne method instruct the builder to construct your model. It will always return an instance of your model regardless of the times setting. This will include primary keys and timestamps.

# createMany

The createMany method instruct the builder to construct your model. It will always return an instance of a ModelCollection regardless of the times setting. This will include primary keys and timestamps.