DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Coding
  3. Languages
  4. Creating Objects in Java Unit Tests

Creating Objects in Java Unit Tests

Jay Fields user avatar by
Jay Fields
·
Jan. 13, 09 · Interview
Like (0)
Save
Tweet
Share
31.05K Views

Join the DZone community and get the full member experience.

Join For Free
Most Java unit tests consist of a Class Under Test (CUT), and possibly dependencies or collaborators. The CUT, dependencies and collaborators need to be created somewhere. Some people create each object using 'new' (vanilla construction), others use patterns such as Test Data Builders or Object Mother. Sometimes dependencies and collaborators are instances of concrete classes and sometimes it makes more sense to use Test Doubles. This entry is about the patterns I've found most helpful for creating objects within my tests.

In January of 2006, Martin Fowler wrote a quick blog entry that included definitions for the various types of test doubles. I've found working with those definitions to be very helpful.

Test Doubles: Mocks and Stubs

Mockito gives you the ability to easily create mocks and stubs. More precisely, I use Mockito 'mocks' for both mocking and stubbing. Mockito's mocks are not strict: They do not throw an exception when an 'unexpected' method is called. Instead, Mockito records all method calls and allows you to verify at the end of your test. Any method calls that are not verified are simply ignored. The ability to ignore method calls you don't care about makes Mockito perfect for stubbing as well.

Test Doubles: Dummies

I didn't find many cases where I needed a dummy. A dummy is really nothing more than a traditional mock (one that throws exceptions on 'unexpected' method calls) with no expected method calls. In cases where I wanted to ensure that nothing was called on an object, I usually used the verifyZeroInteractions method on Mockito mocks. However, there are exceptional cases where I don't want to verify that no methods were called, but I would like to be alerted if a method is called. This is more of a warning scenario than an actual failure.

On my current project we ended up rolling our own dummies where necessary and overriding (or implementing) each method with code that would throw a new Exception. This was fairly easy to do in IntelliJ; however, I'm looking forward to the next version of Mockito, which should allow for easier dummy creation.

Test Doubles: Fakes
My current project does make use of Fakes. We define a fake as an instance that works for and is targeted towards a single problem. For example, if your class depended on a Jetlang fiber you could pass in a fake Jetlang fiber that synchronously executed a command as soon as it was given one. The fake fiber wouldn't allow you to schedule tasks, but that's okay, it's designed to process synchronous requests and that's it.

We don't have a large amount of fakes, but they can be a superior alternative when compared to creating multiple mocks that behave in the same way. If I need a CreditCardProcessingGateway to return a result once, it's good to use a mock. However, if I need a CreditCardProcessingGateway to consistently return true when given a luhn valid credit card number (or false otherwise) a fake can be a superior option.

Concrete Classes

I'm a big fan of Nat Pryce's Test Data Builders for creating concrete classes. I use test data builders to create the majority of dependencies and collaborators. Test data builders also allow me to easily drop in a test double where necessary. The following example code shows how I'll use a test data builder to easily create a car object.
aNew().car().
with(mock(Engine.class)).
with(fake().radio().fm_only()).
with(aNew().powerWindows()).build();


The (contrived) example demonstrates 4 difference concepts:
  • A car can easily be built with a mock engine
  • A car can easily be built with a fake fm radio
  • A car can easily be built with a power windows concrete implementation
  • All other dependencies will be sensible defaults
Nat's original entry on the topic states that each builder should have 'sensible defaults'. This left things a bit open for interpretation so we tried various defaults. In the end it made the most sense to have all test data builders use other test builders as defaults, or null. We never use any test doubles as defaults. In practice this is not painful in anyway, since you can easily drop in your own mock or fake in a specific test that requires it.

The builders are easy to work with because you know the default dependencies are concrete, instead of having to look at the code to determine if the dependencies are concrete, mocks, or fakes. This convention makes writing and modifying tests much faster.

You may have also noticed the aNew() and fake() methods from the previous example. The aNew() method returns a DomainObjectBuilder class and the fake() method returns a Faker class. These methods are convenience methods that can be staticly imported. The implementations of these classes are very simple. Given a domain object Radio, the DomainObjectBuilder would have a method defined similar to the example below.
public RadioBuilder radio() {
RadioBuilder.create();
}

This allows you to import the aNew method and then have access to all the test data builders in a code completeable manner. Keeping the defaults in the create method of each builder ensures that any shared builders will come packaged with their defaults. You could also create a no-arg constructor, but I prefer each builder to have only one constructor that contains all the dependencies necessary for creating the actual concrete class.

The following code shows how all these things work together.

// RadioTest.java
public class RadioTest {
    public void shouldBeOn() {
        Radio radio = aNew().radio().build();
        radio.turnOn();
        assertTrue(true, radio.isOn());
    }
    // .. other tests...
}

// DomainObjectBuilder.java
public class DomainObjectBuilder {
    public static DomainObjectBuilder aNew() {
        return new DomainObjectBuilder();
    }

    public RadioBuilder radio() {
        return RadioBuilder.create();
    }
}

// RadioBuilder.java
public class RadioBuilder {
    private int buttons;
    private CDPlayer cdPlayer;
    private MP3Player mp3Player;

    public static RadioBuilder create() {
        return new RadioBuilder(4,
            CDPlayerBuilder.create().build(),
            MP3PlayerBuilder.create().build());
    }

    public RadioBuilder(int buttons, CDPlayer cdPlayer, MP3Player mp3Player) {
        this.buttons = buttons;
        this.cdPlayer = cdPlayer;
        this.mp3Player = mp3Player;
    }

    public RadioBuilder withButtons(int buttons) {
        return new RadioBuilder(buttons, cdPlayer, mp3Player);
    }

    public RadioBuilder with(CDPlayer cdPlayer) {
        return new RadioBuilder(buttons, cdPlayer, mp3Player);
    }

    public RadioBuilder with(MP3Player mp3Player) {
        return new RadioBuilder(buttons, cdPlayer, mp3Player);
    }

    public Radio build() {
        return new Radio(buttons, cdPlayer, mp3Player);
    }
}
As you can see from the example, it's easy to create a radio with sensible defaults, but you can also replace dependencies where necessary using the 'with' methods.

Since Java gives me method overloading based on types, I always name my methods 'with' when I can (like the example shows). This doesn't always work, if for example you have two different properties that are of the same type. This usually happens with built in types, and in those cases I create methods such as withButtons, withUpperLimit, or withLowerLimit.

The one other habit I've gotten into is using Builders to create all objects within my tests, even the Class Under Test. This results in more maintainable tests. If you use the the Class Under Test's constructor explicitly within your test and you add a dependency you'll end up having to change each line that creates a Class Under Test instance. However, if you use a builder you may not need to change anything, and if you do have to change anything it will probably only be for a subset of the tests.

Conclusion
I'm a big fan of Test Data Builders and Test Doubles. Combining the two concepts has resulted in being able to write tests faster and maintain them more easily. These ideas can be incrementally added as well, which is a nice bonus for someone looking to add this style to an existing codebase.
unit test Object (computer science) Test data Java (programming language) Dependency Data (computing)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • What Is JavaScript Slice? Practical Examples and Guide
  • Journey to Event Driven, Part 1: Why Event-First Programming Changes Everything
  • What Are the Benefits of Java Module With Example
  • mTLS Everywere

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: