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
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

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

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • TDD Typescript NestJS API Layers with Jest Part 1: Controller Unit Test
  • Shallow and Deep Copies in JavaScript: What’s the Difference?
  • Two Cool Java Frameworks You Probably Don’t Need
  • Fighting Fragility With Property-Based Testing

Trending

  • Navigating Double and Triple Extortion Tactics
  • Designing for Sustainability: The Rise of Green Software
  • Mastering Advanced Traffic Management in Multi-Cloud Kubernetes: Scaling With Multiple Istio Ingress Gateways
  • What Is Plagiarism? How to Avoid It and Cite Sources
  1. DZone
  2. Coding
  3. Languages
  4. TypeScript Testing Tips - Creating Dummies

TypeScript Testing Tips - Creating Dummies

By 
Eoin Mullan user avatar
Eoin Mullan
·
Nov. 05, 20 · Tutorial
Likes (3)
Comment
Save
Tweet
Share
10.7K Views

Join the DZone community and get the full member experience.

Join For Free

This is the first post in a series on using TypeScript in practical applications. It shows a simple TypeScript pattern for building type-safe, unit test dummies. The focus is on simplicity for the consumer so tests emphasize what's important and avoid incidental details. 

typescript over computer monitor


At Instil, we’re big fans of TypeScript. Although we appreciate the strengths of dynamic languages, all things considered, strong static typing wins more often than not.

When it comes to a powerful type system few mainstream languages come close to TypeScript. So, in this series of posts, we’re going to document how we use TypeScript practically in real projects.

First up…

Testing With TypeScript

Seasoned TypeScripters may be surprised to learn that the T in TDD can also stand for Test, and we all write those first, right? So that’s where we’ll start.

The first few posts in the series will be recipes we rely on to remove boilerplate from our tests so they stay on point, all while maintaining the benefits of static typing. This post will start simple, looking at how we create dummy objects. Subsequent posts will look at bringing static types to more advanced mocking techniques with Jest.

Creating Dummy Types

You’ll often need to create dummy objects in your test code. Something to pass into the method you’re testing, or to have returned by a mock function. This pattern, which uses the Partial utility type, allows tests to create the object they need while specifying only the properties they care about. Consider an employee interface:

TypeScript
xxxxxxxxxx
1
 
1
export interface Employee {
2
   id: string;
3
   name: string;
4
   department: Department;
5
   position: Position;
6
}

 

Now, create a builder like this:

TypeScript
xxxxxxxxxx
1
16
 
1
export function buildEmployee(
2
  {
3
     id = "abc123",
4
     name = "Jim",
5
     department = buildDepartment(),
6
     position = buildPosition(),
7
   }: Partial<Employee> = {}
8
9
): Employee {
10
  return {
11
     id,
12
     name,
13
     department,
14
     position,
15
  }
16
}

 

Now, tests that need an Employee have the facility to create what they need in a very concise way, emphasizing only what’s important, without unnecessary clutter.

Using These Dummies in Tests

In the simplest case, you may just want an Employee object without caring about any of its property values. This can be useful e.g. if you want to verify an object is being passed through to a dependency correctly. In this case, you can call buildEmployee() without any parameters, and all the defaults will be used.

TypeScript
xxxxxxxxxx
1
 
1
it("should fetch employee pay from payroll service", () => {
2
  const dummyEmployee = buildEmployee();
3
  target.buildPayslip(dummyEmployee);
4
  expect(payrollService.calculatePay).toHaveBeenCalledWith(dummyEmployee);
5
});

 

This verifies that the same employee object your test passes to buildPayslip is then further passed onto the payrollService dependency. Similarly, a simple employee object with default properties can be useful when you need to verify that the object returned by a dependency to your code under test is then further passed back to the calling code.

Another common use case for dummy objects is when you need to ensure the code your testing accesses an object’s properties correctly. The Partial<Employee> type on the input parameter to the builder function means the input parameter is a variation of Employee where all properties are optional. It allows consumers to override specific properties relevant to what’s being tested while taking defaults for all other properties. For example, consider a function that searches for all records containing an employee’s name. 

TypeScript
xxxxxxxxxx
1
 
1
it("should find all records containing employee's name", () => {
2
  const dummyEmployee = buildEmployee({
3
    name: "Jane"
4
  });
5
   
6
  target.findRecords(dummyEmployee);
7
  expect(recordService.findAllMatchingRecords("Jane"));
8
});


In this test, we wish to be explicit about the employee’s name only as all other properties are irrelevant, and the builder lets us do this. The test is self-contained in that we can easily make the visual link between where the employee’s name is set and where it’s verified. We don’t need to navigate to where the dummy was created to see what values it was initialized with.

Finally, note that Department and Position use the same pattern, and it’s builders all the way down. This allows consumers to be explicit about sub-components if they need to, e.g.

TypeScript
xxxxxxxxxx
1
15
 
1
it("should build employee summary", () => {
2
  const dummyEmployee = buildEmployee({
3
    name: "Jane",
4
    department: buildDepartment({
5
      name: "R&D",
6
      division: "Skunkworks",
7
    }),
8
    position: buildPosition({
9
      name: "Software Engineer",
10
    }),
11
  });
12
  
13
  expect(target.buildSummary(dummyEmployee))
14
  .toEqual("Jane (Software Engineer): R&D - Skunkworks");
15
});

 

Best Practice for This Pattern

Before you copy, paste, and get on your way, it’s worth a quick think on how we use these dummy values. Tests should not rely on default dummy values but, instead, should explicitly define any properties relevant to them. So a good rule to keep in mind is:

Changing a default scalar in any of your dummy builders shouldn’t cause any tests to fail.

This requires some discipline on the part of developers and, if you wish, you can remove that burden in one of the following ways:

  • Don’t provide defaults for scalars in the dummy object builder function, but instead let them be undefined and cast your object to the return type before it’s returned. This will force consumers to be explicit about properties they need but it can also make tests a bit more noisy as they may need to define properties that are required by a method but not relevant to a test, e.g. logging an Employee’s id and name while building their payslip. It also means your dummies are invalid objects.
  • Include builders for your scalers, e.g. buildString, buildNumber, etc. This means all your default strings will be the same, so you can’t think of the employee’s name as “Jim” anymore.

There’s room for debate here, and it could be worth having the development team explore what will work best for them.

Beyond Interfaces

While interfaces are the simplest case it’s possible to use this pattern with classes too. But this typically requires a few more moving parts involving a mocking framework, and that’s what we’ll be getting to next time.

TypeScript unit test Object (computer science) Property (programming)

Published at DZone with permission of Eoin Mullan. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • TDD Typescript NestJS API Layers with Jest Part 1: Controller Unit Test
  • Shallow and Deep Copies in JavaScript: What’s the Difference?
  • Two Cool Java Frameworks You Probably Don’t Need
  • Fighting Fragility With Property-Based Testing

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

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

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!