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
Building Scalable Real-Time Apps with AstraDB and Vaadin
Register Now

Trending

  • 4 Expert Tips for High Availability and Disaster Recovery of Your Cloud Deployment
  • Database Integration Tests With Spring Boot and Testcontainers
  • The Role of AI and Programming in the Gaming Industry: A Look Beyond the Tables
  • What Is Envoy Proxy?

Trending

  • 4 Expert Tips for High Availability and Disaster Recovery of Your Cloud Deployment
  • Database Integration Tests With Spring Boot and Testcontainers
  • The Role of AI and Programming in the Gaming Industry: A Look Beyond the Tables
  • What Is Envoy Proxy?
  1. DZone
  2. Data Engineering
  3. Data
  4. Test Behavior, Not State

Test Behavior, Not State

Jared Richardson user avatar by
Jared Richardson
·
Aug. 25, 10 · Interview
Like (0)
Save
Tweet
Share
19.70K Views

Join the DZone community and get the full member experience.

Join For Free
Many developers and testers write automated tests. Sometimes they're unit tests, other times package level, and occasionally integration. There are many different types of tests, but there are a few characteristics of great tests. Today let's look at one specific characteristic: behavior versus state.

A common trap that many test crafters fall into is letting their test see too far into the class. Delving into the code inside the method, checking data structures, seeing what routines were called, and looking at variables are all signs that your test has gone too deep. By coupling the test so completely with the exact implementation of the method, you've made it impossible to refactor the code inside the method without changing the test as well. This creates more work for you (generally a bad thing), but also forces you to update the test to match the changes you've made. Adjusting the test gives you a change to change what's being tested, which allows bugs to creep in.

For example, say I've got a routine that grabs a list of students in a class, calculates each student's grade, then returns an average for the entire list. The method signature might look like this:

public Integer calculate_classes_average_grade( String class_name)

You pass in a class name and get a number in return. There are several good ways to test a routine like this. One way would be to pass in a class name, have it pull in known data from a database, let it calculate the average GPA and return it to your test harness. You'd then verify the return value. (You'd probably want to use Dave Hussman's "Nuke and pave" idea on the database to ensure a clean, known data set.)

Another way to test it would be to mock the database connection, have the mock return a known data set, then again, test the return value.

A third way would be to drive the code that uses this routine, testing it from a higher level. Perhaps a package level test or even a GUI test would work for this scenario.

In each case, we're using the code and checking the results. The test doesn't know anything about how the grades are retrieved (apart from the mocking). It only knows what the code should return. This frees up the development team to change the code inside the routine. Perhaps you want to make the code more efficient, change the data structures being used, or completely rewrite the internals of the class. If you've fallen into the trap of overly coupling your tests to your code, these changes will fail your tests, and you'll have two classes to rewrite.

Sometimes a developer will write a test that runs this routine, then checks the internal list of students to see if the correct number of students is in the list, then checks if the right students are in the list, and so on. For the moment let's ignore that that bit of code should be broken out into a subroutine so that you can do that check properly. Instead, let's look at what happens if you write a code that peeks into the class, grabs that data structure, and verifies the structure's internal state. When you give the test that level of insight into the class being tested, what happens when the data structure changes from an array to a hash table? What happens when the order of the students in the list changes? What happens when you want to run the test against a different data set?

In each of these scenarios, the test fails, so you have to update the code. This creates unnecessary work, wastes your time, and introduces the potential of "fixing" the test in a way that lets errors creep in.

So take a step back. Never let your automated tests, no matter their type or flavor, see inside the code they're testing. Use the class, the package, or the routine. Pass in the data values that make it run, and then check the results. This is one of the best ways to write solid, reusable tests and ensure you can refactor your code without having to update your tests at the same time.
Testing Data (computing) Database

Opinions expressed by DZone contributors are their own.

Trending

  • 4 Expert Tips for High Availability and Disaster Recovery of Your Cloud Deployment
  • Database Integration Tests With Spring Boot and Testcontainers
  • The Role of AI and Programming in the Gaming Industry: A Look Beyond the Tables
  • What Is Envoy Proxy?

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

Let's be friends: