### Tutorial

This tutorial introduces the capabilities of `factory-girl`. We'll start with a simple
factory for a hypothetical `User` model and gradually add to it. This tutorial may not
cover all aspects of `factory-girl`, but should serve as a good starting point.

### The `User` Factory

Let's start with a simple `User` factory, as we go on, we'll keep on modifying
this factory to add functionality and show how `factory-girl` works.

```javascript
import factory from 'factory-girl';
import User from '../models/User';

factory.define('User', User, {
  email: 'user@my-domain.com',
  password: 'some-password'
});

factory.build('User').then(user => {
  console.log(user);
  // => User { email: 'user@my-domain.com', password: 'some-password' }
});
```

Whenever we need a `User` object now, we can just ask `factory-girl` to  build one for us.
That's awesome, but not very useful yet. All the objects we  get back from `factory-girl`
currently have same property values.

The `#build` api allows us to pass attributes to override default ones, so we can do:

```javascript
factory.build('User').then(user => console.log(user));
// => User { email: 'user@my-domain.com', password: 'some-password' }
factory.build('User', {email: 'another-user@my-domain.com'}).then(user => console.log(user));
// => User { email: 'another-user@my-domain.com', password: 'some-password' }
```

Once again, this may be handy, but not very useful, it requires us to keep providing a
new email value each time we want to create a `User` model.

`factory-girl` has a solution: sequences. Instead of providing a hardcoded  value, we can
tell factory-girl to instead use a sequence. A slight modification to the model-factory
definition:

```javascript
factory.define('User', User, {
  email: factory.sequence('User.email', n => `dummy-user-${n}@my-domain.com`),
  password: 'some-password'
});
```

Now we get a new email address every time we ask `factory-girl` for a  `User` instance:

```javascript
factory.build('User').then(user => console.log(user));
// => User { email: 'dummy-user-1@my-domain.com', password: 'some-password' }
factory.build('User').then(user => console.log(user));
// => User { email: 'dummy-user-2@my-domain.com', password: 'some-password' }
```

Better! Let's add `name` and `about` attributes for our `User` models:

```javascript
factory.define('User', User, {
  email: factory.sequence('User.email', n => `dummy-user-${n}@my-domain.com`),
  password: 'some-password'
  name: factory.sequence('User.name', n => `user name ${n}`),
  about: 'this ideally should be a paragraph about user',
});
```

This should work fine, but what if you have a few test cases that expect `about` to be
actually a paragraph? Or rather have user names that look a bit realistic instead of `user
name 1`? `factory-girl` provides another goodie that we can use for a more 'realistic'
data: `chancejs`. You can learn more about `chancejs` [here](http://chancejs.com/).
`factory-girl` exposes a simple '#chance' api that can be easily used to populate fields
with data generated by `chancejs`.

```javascript
factory.define('User', User, {
  email: factory.sequence('user.email', n => `dummy-user-${n}@my-domain.com`),
  password: factory.chance('word'),
  name: factory.chance('name'),
  about: factory.chance('paragraph'),
});
```

What if you want `about` to have just 2 sentences or names to have a middle
name as well or passwords to be a bit longer? No problem, you can just pass any
options expected by the [chancejs api](http://chancejs.com/):

```javascript
factory.define('User', User, {
  email: factory.sequence('user.email', n => `dummy-user-${n}@my-domain.com`),
  password: factory.chance('word', { syllables: 4 }),
  name: factory.chance('name', { middle: true }),
  about: factory.chance('paragraph', { sentences: 2 })
});
```

Our `User` factory will now create instances such as:

```javascript
User {
  email: 'dummy-user-1@my-domain.com',
  password: 'tavnamgi',
  name: 'Nelgatwu Powuku Heup',
  about: 'Idefeulo foc omoemowa wahteze liv juvde puguprof epehuji upuga zige odfe igo sit pilamhul oto ukurecef.'
}
```

Let's say we want our `User` instances to have a password expiry date. Assuming the date
needs to be in future (apart from the test case where it shouldn't), hard-coding the date
doesn't seems elegant. Let's say by default we want the expiry date to be a month from now
(i.e. when the instance is being created).

Anywhere you need to do something to compute a value for an attribute, you can provide a
function that returns the value. Our `User` factory now becomes:

```javascript
factory.define('User', User, {
  ...
  passwordExpiry: () => moment().add('1 month').toDate(),
});
```

What if you want to do something asynchronous? Anywhere you want to do something
asynchronous, just provide a function that returns a promise that resolves to the
value to be populated.

```javascript
factory.define('User', User, {
  ...
  favoriteJoke: () => fetch('http://api.icndb.com/jokes/random')
      .then(res => res.json()).then(data => data.joke)
});
```

So far we have been dealing with a single model. Most of the times you have 
several models associated with each other there are a few ways `factory-girl` 
allows you to have associations. Let's say we would like to have our users to 
have a profile image. We start by defining a factory for profile image model 
(assuming we already have a `ProfileImage` model):
 
```javascript
factory.define('ProfileImage', ProfileImage, {
  id: factory.sequence('ProfileImage.id'),
  imageUrl: 'http://lorempixel.com/200/200'
});
```

To associate a profile image with factory generated models, we can simply do:

```javascript
factory.define('User', User, {
  ...
  profileImage: factory.assoc('ProfileImage', 'id')
});
```

`factory-girl` will now create a `ProfileImage` instance and place its `id` attribute in
the `profileImage` attribute of the created `User` instance.

What if you want the `ProfileImage` instance itself to be assigned to `profileImage`? Just
don't pass `'id'` and the `ProfileImage` instance itself will be assigned to the
`profileImage` attribute.

> Note that `factory.assoc` will persist the model instance to DB. In case you don't want
the model to be persisted, use `factory.assocAttrs` which just builds the attributes and
does not persist a model to the DB.

At times you may want to associate more than one model instance. For example, let's say we
want our users to have a list of addresses. Assuming we already have an `Address` model,
we first define the `Address` factory:

```javascript
factory.define('Address', Address, {
  id: factory.sequence('Address.id'),
  address1: factory.chance('address'),
  address2: factory.chance('street'),
  city: factory.chance('city', { country: 'us' }),
  state: factory.chance('state', { country: 'us' }),
  country: 'USA'
});
```

Now, we can tell `factory-girl` to associate multiple addresses with our `User` instances:

```javascript
factory.define('User', User, {
  ...
  addresses: factory.assocMany('Address', 3)
});
```

Similar to `factory.assocAttrs` we have `factory.assocAttrsMany` to associate
non-persisted models or their attributes.

So far so good! We can already see `factory-girl` making our life easier to   build model
instances. But, it is still limited to some extent. What if you have 20 test cases for
users with expired password? It's going to be tedious to override the `passwordExpiry`
attribute for each of those test cases. Add a few more attributes that may change with
different test cases and things may soon get out of hand.

`factory-girl` allows you to define model factories with an initializer function instead
of an object. To get started, we just make a simple change:

```javascript
factory.define('User', User, (buildOptions) => {
  return {
    email: factory.sequence('user.email', n => `dummy-user-${n}@my-domain.com`),
    password: factory.chance('word', { syllables: 4 }),
    name: factory.chance('name', { middle: true }),
    about: factory.chance('paragraph', { sentences: 2 }),
    passwordExpiry: () => moment().add('1 month').toDate(),
    favoriteJoke: () => {
      fetch('http://api.icndb.com/jokes/random')
        .then(response => response.value.joke)
    },
    profileImage: factory.assoc('ProfileImage', 'id'),
    addresses: factory.assocMany('Address', 3, 'id')
  };
});
```

Notice that instead of an initializer object, we now have a function that takes
an argument `buildOptions` and returns the initializer object. The `buildOptions`
can be specified when requesting `factory-girl` to create an instance. Using
the initializer function you can customise your models any way you want.

Let's first modify our factory a bit to use `buildOptions`, then we'll see how
to pass `buildOptions`:

```javascript
factory.define('User', User, (buildOptions = {}) => {
  const attrs = {
    ...
    addresses: factory.assocMany('Address', buildOptions.addressCount || 3, 'id')
  };
  if (buildOptions.passwordExpired) {
    attrs['passwordExpiry'] = moment().subtract('1 month').toDate(),
  }
  return attrs;
});
```

Now, when requesting `factory-girl` to create a `User` instance, we can do:

```javascript
factory.create('User', {}, { passwordExpired: true, addressCount: 4 })
  .then(user => console.log(user));
```

Which would result in something like:

```javascript
User {
  email: 'dummy-user-1@my-domain.com',
  password: 'tavnamgi',
  name: 'Nelgatwu Powuku Heup',
  about: 'Idefeulo foc omoemowa wahteze liv juvde puguprof epehuji upuga zige odfe igo sit pilamhul oto ukurecef.',
  passwordExpiry: 'May 22, 2016' // date formatting may be changed,
  favoriteJoke: 'Chuck Norris has two speeds: Walk and Kill.',
  profileImage: 1, // id of the ProfileImage instance created
  addresses: [1, 2, 3, 4] // ids of the Address instances created
}
```
