Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generation of test data factories for stubs and testing from Swagger #896

Closed
jdrew1303 opened this issue Dec 21, 2014 · 5 comments
Closed

Comments

@jdrew1303
Copy link

Using the parser from #895 to generate test data factories using factory-girl and Faker.js to allow for more concise unit testing of functions requiring models. If the models are wrapped in a UMD, they can be reused for unit tests, stubs and UI/Visual regression tests.

@jdubois
Copy link
Member

jdubois commented Dec 22, 2014

I'm sorry but I don't know any of those tools or libraries, so I have no idea if this is do-able, or if this is even an interesting idea or not.
@jmirc do you know any of those?

@jmirc
Copy link
Member

jmirc commented Dec 22, 2014

I don't know too. If you could provide an example, it will speed us to understand how it works and to see if we want to include it.

@jdrew1303
Copy link
Author

No problem, Ill put up a demo of how Id use them :shipit: Its like using the test data builder pattern in Java for tests that require models or mother objects.

We use factory-girl because we can iterate quite a bit on APIs for our applications and its a pain to have to change our test and stubs because the model changes.

@jdrew1303
Copy link
Author

Factories and mock data

This is the standard practice in unit tests

var response = [
    {name: "first author", post_count: 1234, address: "1234 Angular Lane"},
    {name: "second author", post_count: 5, address: "1 Google Way"},
    {name: "third author", post_count: 23, address: "24 Facebook Court"},
    {name: "fourth author", post_count: 18, address: "114 Twitter Drive"}
]

$httpBackend.whenGET('/authors').respond(200, response);

We can replace the use of fixtures in tests with Factories (this definition using Rosie.js). In this case we are also using faker to generate default fake data.

Factory.define('author')
  .attr('id', faker.random.uuid )
  .attr('name', faker.name.findName )
  .attr('post_count', function () { return faker.random.number({max : 1234}); })
  .attr('address', faker.address.streetAddress);

We can now generate a list of authors like so:

var response = Factory.buildList('author', 4);

if I wanted to test for a specific attr I can do either of the following:

var response = Factory.build('author', {name : 'Jacob Marley'});

the alternative is to reference the value passed in during assertion:

response.name

The same factory defined with factory-girl is:

Factory.define('author', {
  id : faker.random.uuid,     // hopefully soon to be included, pull request by yours truly.
  name : faker.name.findName,
  post_count : function () {return faker.random.number({ max : 1234 })},
  address : faker.address.streetAddress
})

var response = Factory.build('author');

The main advantage that factory-girl has over rosie (for front end development) is the ability to handle relations:

Factory.define('book', {
  author : Factory.assoc('author', 'id'),   // this is our association with the author model
  title : faker.company.catchPhrase,
  blurb : faker.lorem.paragraph
})

If we want to include the model in the book object (this relation is stupid but just for illustration) we can do the following:

Factory.define('book', {
  author : Factory.assoc('author'),   // in this case the model is included
  title : faker.company.catchPhrase,
  blurb : faker.lorem.paragraph
})

As you can see there is no coupling to the actual model, if its not needed specifically for the test. We only couple on the attribute name if necessary. It can also be used in stubs for the front end to generate data for UI tests. You can use them in Form Objects (a smaller more specialised page object) to generate the data for entry into a form and allow the developer to pass in the overrides that they need for testing.

By wrapping the factory definitions in a UMD module we can now use them for unit tests (which in the case of angular run in the browser), or in UI test (which, when using Protractor, are run on the server in node.js).

It might seem like a little bit more work (and in some cases it is), but it reduces the coupling in your application, and allows you to reduce the need for heavy mocking, and stubbing, which in turn leads to brittle tests.

(function (root, factory) {
  if (typeof define === "function" && define.amd) {
    define(["faker", "Factory"], factory);
  } else if (typeof exports === "object") {
    module.exports = factory(require("faker"), require("Factory"));
  } else {
    factory(root.faker, root.Factory);
  }
}(this, function (faker, Factory) {

  // NOTE: this is where I defined my module implementation

  Factory.define('author', {
    id : faker.random.uuid,     // hopefully soon to be included, pull request by yours truly.
    name : faker.name.findName,
    post_count : function () {return faker.random.number({ max : 1234 })},
    address : faker.address.streetAddress
  })

  Factory.define('book', {
    author : Factory.assoc('author'),   // in this case the model is included
    title : faker.company.catchPhrase,
    blurb : faker.lorem.paragraph
  })

}));

Generation of factories from Swagger

In the same way that client libraries are generated we can generate factories. We parse the DTO definition and pass the values to templates (moustach, underscrore, etc.). Enabling the generation of factories and client libraries significantly speeds up frontend development and allows for rapid iteration on REST APIs. It can also allows for validation of the retrieved models which, if done by hand, can be error prone and a significant drain on resources.

@jdubois
Copy link
Member

jdubois commented Jan 17, 2015

This looks complex to me, I would like to focus on #897 and then we can see if can do your other requests

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants