DEV Community

loading...
Cover image for The three A’s of Unit Testing

The three A’s of Unit Testing

Jay Cruz
Hey there! I'm Jay. I love to learn and am super passionate about my field, Software Engineering.
・2 min read

Testing is an essential part of building Software. When it comes to production-ready applications we need to have reliable and well-tested code with as few bugs as possible. There are many methods available for testing your code. In this article, I’ll go over one of the most popular methods, Unit-testing. Unit-testing involves the testing of specific modules or pieces of code within your app. When writing tests you probably want to follow some sort of pattern to write well structured, readable tests. This is where the AAA pattern comes in. The AAA stands for Arrange, Act, and Assert. This is a great way to make sure we’re covering all aspects of testing a module of code.

Arrange the state of the data to set it up for testing.

Act on the data through some method that performs an action.

Assert that the result from acting on that data is what we expect it to be.

This is the basic flow for using the AAA pattern with any testing framework. To break each of these down using a code example we’ll be using Javascript's Jasmine testing framework. If you’ve not heard of Jasmine before, it’s similar to other testing frameworks you might be familiar with such as RSpec and JSpec. Now let’s write some tests!

Implementing the AAA pattern

For our example, we’ll be testing a User model in Javascript. Our User class constructor will receive a full name object to set its first name, middle initial, and last name properties.

Our User class contains a method getFullName() that should return the user’s full name. So how do we check that this method does what it says it does? We can write a unit test to make sure we’re getting the correct values. the following code does just that!


So the first part of our test suite is the describe method. The describe just groups together the code that we’re testing. Then the it part of our test is saying what this specific piece of code should actually do. In this case, it should return the full name. Inside of the body of the it is where we implement the arrange, act, and assert, giving each part a specific responsibility. The arrange is creating a new instance of User class while act performs the action with the getFullName() method we’re testing. Assert then assures us if the evaluated result from invoking getFullName() on our user is exactly what we need it to be.

Conclusion

The AAA pattern gives us simple but effective steps for testing our code. Each step to this pattern has its own job to do. The arrange step sets up our data while the act step performs the actions needed to test it, and the assert will determine if the result from acting on that data is what we expected it to be.

Discussion (6)

Collapse
auroratide profile image
Timothy Foster • Edited

I love the AAA pattern! I use it often when teaching to devs unfamiliar with testing.

In your example, there's an argument to be made whether the assertion should be hard-coded as expect(result).toBe('Jay J. Cruz');. As it stands, the test code essentially re-implements the production code, meaning if there's a bug in one then the bug is in the other.

Collapse
fijiwebdesign profile image
Gabirieli Lalasava

Very true. For a trivial example it seems ok but in a real world scenario you're adding uncertainty.

If the test breaks, now you can't tell if the production code or the test has the error.
The test should have concrete values so you don't have to test the test.

If creating the concrete result is too complex, it's better to run the test once and take a snapshot of the result. Inspect that it is correct and use that snapshot against the production code in future runs.

Collapse
coderjay06 profile image
Jay Cruz Author

Thanks Timothy, great advice. I didn't realize this, definitely something to keep in mind

Collapse
lukeshiru profile image
LUKESHIRU

Nice example! I seen some similar posts in which they tested the values of the actual properties instead of the output of methods, which is a very bad practice, because the internal state isn't relevant to unit testing, just the surface of contact with the external world. So:

// Doing it like in your example, which is ideal:

const instance = new Class("value");
const output = instance.method();
expect(output).toBe("expected-value");

// Bad practice I seen:

const instance = new Class("value");
expect(instance.value).toBe("value");
Enter fullscreen mode Exit fullscreen mode

Thanks for sharing! Cheers!

Collapse
coderjay06 profile image
Jay Cruz Author

Thanks for sharing this Luke! Definitely good to know, I'm pretty new to writing my own tests so trying to learn about best practices

Collapse
richedwards26 profile image
RichEdwards26

All testing needs to follow this pattern. Everything from Unit tests though automation and up to manual testing.
I work in the sdet space as a consultant and its scary how common tests are written which have no assertion/validation of results.