Scala or Java equivalent of Ruby factory_girl or Python factory_boy (convenient factory pattern for unit testing)
Asked Answered
D

7

6

When I am writing unit tests in dynamically-typed Ruby or Python, I use the libraries factory_girl and factory_boy, respectively, in order to conveniently generate objects under test. They provide convenient features over direct object instantiation, for example:

  • factory inheritance and overrides
  • field defaults and overrides
  • lazily-computed dependent/derived fields
  • construction of dependent/related other objects
  • implicit lazy field dependency resolution

What are some libraries/frameworks I could use while writing unit tests in statically-typed Java or Scala to achieve similar effects with similar benefits?

Thanks in advance!

I found a similar StackOverflow question from the past here, but unfortunately, the top answer is (paraphrased), "there is no direct equivalent because that would be pointless".

Descartes answered 18/2, 2015 at 4:59 Comment(1)
I am not familiar with factory-boy or factory-girl. But it seems that you want to automatically create instances of case classes etc. for feeding them into tests. There is a library called scalacheck scalacheck.org that can be used for something like this. It provides generators for primitive types and mechanisms to derive generators for complex types. However, I think it has a focus on immutability and type safety and therefore is designed in a different way than what you might expect.Freemon
M
10

There is a project called Fixture-Factory(https://github.com/six2six/fixture-factory). It was based in the Factory-Girl's idea.

You could easily create your object's template definition:

Fixture.of(Client.class).addTemplate("valid", new Rule(){{
  add("id", random(Long.class, range(1L, 200L)));
  add("name", random("Anderson Parra", "Arthur Hirata"));
  add("nickname", random("nerd", "geek"));
  add("email", "${nickname}@gmail.com");
  add("birthday", instant("18 years ago"));
  add("address", one(Address.class, "valid"));
}});

And then you can easily use it in your tests:
Client client = Fixture.from(Client.class).gimme("valid");

Miscall answered 28/5, 2015 at 18:44 Comment(0)
I
3

I created a new java framework Factory Duke to provide the same feature as Factory_Girl https://github.com/regis-leray/factory_duke

Really easy to use, define a template (in this case the default)

FactoryDuke.define(User.class, u -> {
    u.setLastName("Scott");
    u.setName("Malcom");
    u.setRole(model.Role.USER);
});

And use it

User user = FactoryDuke.build(User.class);

Please read the document to see advanced features

Inconformity answered 29/1, 2016 at 3:33 Comment(1)
Please don't remove my answer it has been deleted on the other page.Inconformity
L
2

Another solution, very similar to factory-bot (new name for factory-girl) is topicusoverheid/java-factory-bot you can use this way:

Given a model for an Article and a User (getters and setters are omitted):

@Data
public class Article {
    private String title;
    private String content;
    private Date creationDate;
    private User author;
}

@Data
public class User {
    private String username;
    private String firstName;
    private String lastName;
    private String email;
}

You can define factories like


class ArticleFactory extends Factory<Article> {
    Map<String, Attribute> attributes = [
            title       : value { faker.lorem().sentence() },
            content     : value { faker.lorem().paragraph() },
            creationDate: value { faker.date().past(20, TimeUnit.DAYS) },
            author      : hasOne(UserFactory)
    ]
}

class UserFactory extends Factory<User> {
    Map<String, Attribute> attributes = [
            username : value { faker.name().username() },
            firstName: value { faker.name().firstName() },
            lastName : value { faker.name().lastName() },
            email    : value { "${get("firstName")}.${get("lastName")}@example.com" }
    ]
}

And create objects using

Article article = new ArticleFactory().build()
which generates an article with default random but sane attributes. Individual attributes or relations can be overriden by passing them in a map:

Article article = new ArticleFactory().build([title: "Foo", user: [username: "johndoe"]])
Lennie answered 23/7, 2019 at 13:48 Comment(0)
R
1

I wrote (and published) a library for defining object factories: https://github.com/arosini/wildstyle-generator

First you define and register the generator / factory:

WildstyleGenerator.createObjectGenerator(Employee.class)
  .mapField("firstName", new FirstNameValueGenerator(true))
  .mapField("lastName", new LastNameValueGenerator(true))
  .mapField("yearsEmployed", 10)
  .mapField("hourlyWage", new DoubleValueGenerator(10.0, 100.0))
  .mapField("employeeType", new EnumValueGenerator(EmployeeType.class, true)) 
  .register();

Then you can use the generator / factory:

Employee employee = WildstyleGenerator.generate(Employee.class);

There are some more advanced features which you can read about on the project's homepage. Note assertions must be enabled or the library will not function properly.

Roulers answered 16/1, 2017 at 15:27 Comment(0)
T
1

There is a Beanmother library: https://github.com/keepcosmos/beanmother

That helps to create your various and complex objects super easily with fixtures for testing. Beanmother is a implementation of ObjectMother pattern and also fixture replacement tool.

Temperature answered 16/10, 2017 at 9:17 Comment(0)
D
0

Mockito has a mode where it will recursively return dynamically generated mock objects. If you need to do very sophisticated mocking (e.g. of construction or of static methods), you can use PowerMock to provide it, at the cost of doing bytecode manipulation.

But I urge you to consider more idiomatic approaches, rather than trying to write "Ruby/Python in Java/Scala" - there's no point in learning a language that doesn't change the way you think. You /should/ be taking advantage of the type system to ensure correctness, removing the need for many kinds of tests. You should be exposing a small interface which is inherently friendly to "traditional" mocking (i.e. using Easymock or Mockito in its default mode), or even manual stubbing. Particularly in scala, look to something like http://michaelxavier.net/posts/2014-04-27-Cool-Idea-Free-Monads-for-Testing-Redis-Calls.html (Haskell, but the idea is the same) to allow you to write higher-level tests that provide very strong guarantees about what your methods are doing.

Doorplate answered 18/2, 2015 at 13:17 Comment(2)
It is not about writing Ruby in Java. It is about bringing good ideas from some language to another language.Poitiers
It's important to consider the context of the language that lead to those ideas. What's a good idea in one language can easily be a bad idea in another.Doorplate
G
0

There is Fritter Factory library: https://github.com/equinox-one/fritterfactory

A basic sample:

FritterFactory fritterFactory = new FritterFactory();
List<Person> persons = fritterFactory.buildList(Person.class, 3);

A sample with model customization:

FritterFactory fritterFactory = new FritterFactory();
MapMold personMold = new MapMold();
personMold.put(PersonSymbols.NAME, new FirstNameProvider());
personMold.put(PersonSymbols.SURNAME, new FirstNameProvider());
ModelProvider<Person> personProvider = new ModelProvider<Person>(fritterFactory, Person.class, personMold);
fritterFactory.addProvider(Person.class, createPersonProvider(fritterFactory));
List<Person> persons = fritterFactory.buildList(Person.class, 3);

An interesting thing of this library is that it can be used in Android too.

Glower answered 28/1, 2016 at 10:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.