6

当我在动态类型的 Ruby 或 Python 中编写单元测试时,我分别使用库factory_girlfactory_boy,以便方便地生成被测对象。它们提供了比直接对象实例化更方便的特性,例如:

  • 工厂继承和覆盖
  • 字段默认值和覆盖
  • 延迟计算的依赖/派生字段
  • 构建依赖/相关的其他对象
  • 隐式惰性字段依赖解析

在使用静态类型的 Java 或 Scala 编写单元测试时,我可以使用哪些库/框架来实现类似的效果和类似的好处?

提前致谢!

我在这里找到了过去类似的 StackOverflow 问题,但不幸的是,最重要的答案是(意译),“没有直接的等价物,因为那毫无意义”。

4

7 回答 7

10

有一个名为 Fixture-Factory 的项目(https://github.com/six2six/fixture-factory)。它基于工厂女孩的想法。

您可以轻松创建对象的模板定义:

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"));
}});

然后您可以轻松地在测试中使用它:
Client client = Fixture.from(Client.class).gimme("valid");

于 2015-05-28T18:44:33.200 回答
3

我创建了一个新的 java 框架 Factory Duke 以提供与 Factory_Girl https://github.com/regis-leray/factory_duke相同的功能

真的好用,定义一个模板(本例默认)

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

并使用它

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

请阅读文档以查看高级功能

于 2016-01-29T03:33:45.790 回答
1

另一个与 factory-bot(factory-girl 的新名称)非常相似的解决方案是 topicusoverheid/java-factory-bot您可以这样使用:

给定文章和用户的模型(省略了获取器和设置器):

@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;
}

您可以定义工厂,例如


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" }
    ]
}

并使用创建对象

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"]])
于 2019-07-23T13:48:27.410 回答
1

有一个 Beanmother 库:https ://github.com/keepcosmos/beanmother

这有助于使用用于测试的夹具超级轻松地创建各种复杂的对象。Beanmother 是 ObjectMother 模式的实现,也是夹具替换工具。

于 2017-10-16T09:17:37.057 回答
1

我编写(并发布)了一个用于定义对象工厂的库:https ://github.com/arosini/wildstyle-generator

首先定义并注册生成器/工厂:

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();

然后你可以使用生成器/工厂:

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

您可以在项目主页上阅读一些更高级的功能。注意断言必须启用,否则库将无法正常运行。

于 2017-01-16T15:27:27.807 回答
0

Mockito 有一种模式,它将递归地返回动态生成的模拟对象。如果您需要进行非常复杂的模拟(例如构造或静态方法),您可以使用 PowerMock 来提供它,但代价是进行字节码操作。

但我敦促您考虑更多惯用的方法,而不是尝试编写“Java/Scala 中的 Ruby/Python”——学习一门不会改变您思维方式的语言是没有意义的。您/应该/利用类型系统来确保正确性,消除对多种测试的需要。您应该公开一个对“传统”模拟(即在默认模式下使用 Easymock 或 Mockito)甚至手动存根友好的小界面。特别是在 scala 中,查看类似http://michaelxavier.net/posts/2014-04-27-Cool-Idea-Free-Monads-for-Testing-Redis-Calls.html(Haskell,但想法是一样的) 允许您编写更高级别的测试,为您的方法正在做什么提供非常有力的保证。

于 2015-02-18T13:17:44.423 回答
0

有 Fritter Factory 库: https ://github.com/equinox-one/fritterfactory

一个基本样本:

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

具有模型定制的示例:

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);

这个库的一个有趣之处在于它也可以在 Android 中使用。

于 2016-01-28T10:05:03.407 回答