Stubs Java objects with Fake data using the Mockito framework and Faker (A Java port of the Faker ruby gem: faker)
Fackito's makes testing easier and better! by
- increasing test development speed and
- hardening of tests
Fackito achieves the above by
- externalizing data from the actual test definition
- allowing the re-use of defined test data
- randomizing all or parts of the data set
Setting up data for unit tests is a tedious task. You may need to create different variations of the same object to test different flows. You may find the following piece of code all over your code:
User user1 = mock(User.class);
when(user1.getFirstName()).thenReturn(...);
when(user1.getLastName()).thenReturn(...);
User user2 = mock(User.class);
when(user2.getFirstName()).thenReturn(...);
when(user2.getLastName()).thenReturn(...);
In addition to that madness, developers tend to copy and paste test chunks, therefore, after a while, you end up with a bunch of similar mock objects all over your tests!
Fackito solves this by pre-stubbing your mocks by using definition files.
User user = fake(User.class)
All the data found in your definitions would be used to fake
your mock so that you don't have to spend time stubbing it.
In addition to that, with the fact that the definition is externalized, you can now reuse it all over your tests.
User user = fake(User.class, "oldTimer");
User user = fake(User.class, "youngster");
Since the definitions for "oldTimer"
and "youngster"
are externalized, they can be reused all over your tests
with no extra code!
Also, Fackito allows you to randomize your test data.
// provided your definition is set to randomize the "firstName"
User user = fake(User.class);
user.getFirstName(); // returns a different value per test run
Every time your test runs, the user.getFirstName()
will return different data. In some cases, this randomization makes
sense but at the end, it is up to you to make that decision. Fackito can fake
data, such as names, emails, dates,
countries, etc.
Fackito uses Mockito's stubbing functionality to create a fake
object.
When a fake
is requested, Fackito looks up the fake
definition for the class being requested. If the definition
is found, Fackito uses the data to stub a Mockito object.
Fackito uses YAML
to define a fake
object. The file's name convention is {package}.{class}.yaml}
and placed within
the test resources.
src
L test
L resources
L com.fackito.User.yaml # note package + class name + yaml extension
The YAML
file's content (definition) is defined as follows:
{fake-identifier}: # if need only one, then identifier must be 'default'
{attribute-name}: {attribute value or faker}
# Example
---
default:
name: My Name # this fake definition is preset to "My Name"
anotherUser: # another example within the same definition
name: ${name.name}
The class will have to have a getName() method for the above definition to be stubbed.
Fackito uses Faker (A Java port of the Faker ruby gem: faker)
to fake
data, therefore all of its functions can be used.
For instance, Faker.Name
has the following properties:
name
firstName
lastName
nameWithMiddle
prefix
title
The above can be used as follows:
# Example
---
default:
prefix: ${name.prefix}
firstName: ${name.firstName}
lastName: ${name.lastName}
title: ${name.title}
Fackito will match attributes found in the definition with the class' getters and stub them as follows:
F fake = mock(fake); // fake is the class being faked - uses Mockito!
Object fakeValue; // from the definition, either uses a preset value or resolves to the fake data
// for all found getters, stub the mock
when(fakeGetMethod).thenReturn(definitionValue);
All the above is triggered just by calling fake:
User user = fake(User.class); // uses the "default" identifier
User anotherUser = fake(User.class, "anotherUser); // uses the "anotherUser" identifier
Download the library
Maven:
<repository>
<id>oss-sonatype</id>
<name>oss-sonatype</name>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<dependency>
<groupId>com.fackito</groupId>
<artifactId>fackito</artifactId>
<version>1.0.0.SNAPSHOT</version>
<scope>test</scope>
</dependency>
Gradle:
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
classpath 'com.fackito:fackito:1.0.0.SNAPSHOT'
Given a com.fackito.User
class:
package com.fackito;
public class User {
private String name;
private String phoneNumber;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
Create a YAML
file: com.fackito.User.yaml
default:
name: ${name.name}
phoneNumber: ${phoneNumber.phoneNumber}
myName:
name: My Name
phoneNumber: ${phoneNumber.phoneNumber}
Now that everything is setup, test!
// Fake POJO with 'default'
User user = fake(User.class);
// the following prints a random name
System.out.println(user.getName());
// Fake POJO with 'myName'
User myNameUser = fake(User.class, "myName");
// the following prints "My Name" as specified in the YAML file under 'myName'
System.out.println(myNameUser.getName());
// can still use Mockito to mock
// stub it
when(userRepository.save(user)).thenReturn(user);
// the following verification passes because
// .save(user) has been invoked
verify(userRepository).save(user);
// the following verification fails because
// .save(myNameUser) has not been invoked
verify(userRepository).save(myNameUser);
Feedback and contributions are welcome. Feel free to send an email or submit a pull request.
This code is free to use under the terms of the MIT license.