Writing Stupid Simple Unit Tests

In this post, we'll cover the basics of unit tests and walk through the process determining what types of tests you should write for your code. We'll also write a few tests to show just how simple most unit tests are.



The aim here is to make tests a bit more familiar and less daunting and mysterious. Unit tests are good. I'm by no means an expert on software testing. I'm just someone that's discovered the joy and freedom that writing tests provides.



If you are an expert on software testing and notice I'm wrong, please tell me!

Onward!

What are unit tests?

If you’re not familiar with unit tests, they’re essentially software written to test discrete units of functionality in a system.

Unit tests test the expected behavior of your software as it exists (or existed) at a specific point in time. They make sure that everything works as expected based on the rules established for the software at the time it was written.

If those rules change -- and your software along with them -- your tests will fail. When they fail, you'll gain insight into how the change affects the rest of the system.

For example, if a change is made in code for your site’s user profile management, you'll want to make sure that the change does not affect the behavior of your site’s payment processing code.

Tests can save time and money by acting as the first line of defense in catching problems before they result in a late night emergency or catastrophic failure. When things go wrong, they can also serve as the first response team. You can run your unit tests to see if any rules/expectations have been broken by some recent change, either in your code or some external API dependency, and fix the bug introduced by the change.

Sometimes the tests become outdated. Again, the tests will fail, letting you know that you’re no longer testing for the right thing. When this happens, you should update your tests to make sure they are in line with the new rules for your software.

Getting started

Unit tests are rarely difficult to write or run if you’re using a testing framework (e.g., phpunit).

You can write your tests before you write your business code if Test-driven Development (TDD) is your thing or you can write them after.

It doesn’t matter much when you write them as long as you have them.

The brilliant thing is that almost all tests you write will be stupid simple.

Tests usually assert some condition is true (or false, depending on what outcome you expect).

On top of that, most testing frameworks have convenience functions built in for performing these types of tests and several others, including testing the expected output of a function (i.e., what the function prints to the screen).

The three test functions we've used most frequently when writing tests for Largo are assertEquals, assertTrue and expectOutputString.

Our stupid simple example plugin

So, let’s say that we want to write tests for a WordPress plugin. Our plugin simply prints the message “Hello world!” in the top corner of the WordPress dashboard (sound familiar?).

This is the extent of the code:

<?php
/**
 * @package Hello_World
 * @version 0.1
 *
 * Plugin Name: Hello World!
 * Plugin URI: https://gist.github.com/rnagle/d561bd58504a644e9657
 * Description: Just a simple WordPress plugin that prints "Hello world!" in the top corner of the WordPress dashboard.
 * Author: Ryan Nagle
 * Version: 0.1
 **/

function hello_world() {
  return "Hello world!";
}

function hello_world_markup() {
	echo "<p id='hello-world'>" . hello_world() . "</p>";
}
add_action('admin_notices', 'hello_world_markup');

function hello_world_css() {
	$x = is_rtl() ? 'left' : 'right';

	echo "
	<style type='text/css'>
	#hello-world {
		float: $x;
		padding-$x: 15px;
		padding-top: 5px;
		margin: 0;
		font-size: 11px;
	}
	</style>
	";
}
add_action('admin_head', 'hello_world_css');

Couple of things to note:

  1. We have one function -- hello_world -- that returns a string.
  2. Two other functions -- hello_world_markup and hello_world_css -- that echo strings but have no return statements. This means we'll have to check the output of these functions rather than the return value to properly test them.
  3. Note that hello_world_markup relies on hello_world to provide the the “Hello world!” message.

So, how do we test this plugin? Well, if you don’t have your test framework installed and configured, you’ll want to get that ready and raring. The details are beyond the scope of what we'll cover here, but for an overview of setting up unit tests for WordPress plugins and themes, see Will Haynes’ Unit Testing Themes and Plugins in WordPress.

The INN Nerds also have a collection of deploy tools to make developing WordPress sites and writing tests much easier. You can read about using the deploy tools for testing themes and plugins here: Updates to INN’s Deploy Tools.

Disclaimer: getting unit tests working WordPress can be daunting at first. It’s a lot to digest and may take some time to set up if you’re coming to this cold, so be patient. It’ll be worth it.

If you’re really dedicated to the cause and need a hand, reach out to us.

How do I write a unit test?

With that out of the way, let’s design some unit tests for our “Hello World!” plugin.

The first thing we’ll want to do is enumerate the features of the code which we need to test.

There are many ways you can approach this. My preference is to create a single test file per file in my source code and have my tests directory mirror the structure of my source directory.

Each test file has tests for each function (or member functions of classes) in the corresponding source file.

So, the directory structure for our plugin would look like:

hello-world/
   hello-world.php
   phpunit.xml
   tests/
       bootstrap.php
       test-hello-world.php

We’ll be doing our work in test-hello-world.php where we’ll set up the skeleton of our test case, which is as simple as extending WP_UnitTestCase:

<?php

class HelloWorldTest extends WP_UnitTestCase {}

We can then stub out test functions for each function:

 class HelloWorldTest extends WP_UnitTestCase {
  function test_hello_world() {
    // Test hello_world()
  }

  function test_hello_world_markup() {
    // Test hello_world_markup()
  }

  function test_hello_world_css() {
    // Test hello_world_css()
  }
} 

Now, let's look at each function and consider what we're testing:

1. For hello_world, we want to verify that the string returned is "Hello World!":

function test_hello_world() {
  $this->assertEquals("Hello World!" == hello_world());
}

Easy enough. Now if for some reason someone changes the return value to "Hello world!" -- capitalization be damned -- the test will fail.

I can hear you now, "This is stupid, no one writes code like this." Yes, the example is stupid and that's the point. Don't get caught up focusing on the wrong thing.

It makes no difference how complex the function is, the only concern is verifying that the return value or the outcome is what is expected.

So, if instead you're testing some_made_up_function which returns an object, you may want to verify that it is actually a PHP Object:

$this->assertEquals(gettype(some_made_up_function()), "object");

Or that the object has a specific member attribute:

$test_obj = some_made_up_function();
$this->assertTrue(property_exists($test_obj, 'name_of_attribute_goes_here'));

2. For hello_world_markup, we want to verify that the function prints "<p id='hello-world'>Hello World!</p>":

function test_hello_world_markup() {
  $this->expectOutputString("<p id='hello-world'>Hello World!</p>");
  hello_world_markup();
}

Notice that we're expecting "Hello World!" to be part of the output. This might not be a good thing. If the return value of hello_world changes, this test will fail, too.

For the sake of example, let's say we only care about testing the markup and not the message. We can take this test a step further and decouple the two so that we're only testing the markup by changing it to read:

function test_hello_world_markup() {
  ob_start();
  hello_world_markup();
  $result = ob_get_contents();
  ob_end_clean();

  $this->assertTrue(!empty(preg_match("/^<p\s+id='hello-world'>.*<\/p>$/", $result)));
}

Essentially what we're saying is, we don't care what the message is as long as it is wrapped in <p id="hello-world" /> tag.

Simple, right?

3. For hello_world_css, we want to verify that the function prints the CSS rules for our "Hello World!" markup:

function test_hello_world_css() {
  ob_start();
  hello_world_css();
  $result = ob_get_contents();
  ob_end_clean();

  // Make sure there are style tags being printed. Duh.
  $this->assertTrue(!empty(preg_match("/.*?.*<\/style>/s", $result)));

  // Make sure we're using the right selector
  $this->assertTrue((bool) strpos('#hello-world', $result)));
}

And with that, we're done! You can see the entirety of test-hello-world.php here.

When to write tests

As mentioned earlier, you may want to write your tests first. This is called Test-driven Development.

Writing your tests first has lots of awesome benefits. When you write tests first you are forced to think about the design of your system and how best to structure the software so that it is actually testable. It’s a good practice and will help increase the orthogonality of your code.

However, it's never too late to start writing tests. When you write tests for existing code, you'll find places where things need to be refactored (sometimes completely rewritten or redesigned) to clean up the spaghetti code.

If you really can't afford the time it takes to write tests for all your code (or aren't being allotted the time to do so), you might still be able to lobby for time.

It's extremely important that you can verify your code works, especially in cases where you're using code to make an assertion about the world. For example, if you're writing code that processes a data set which will later be used in a story or investigation, it's crucial that your code produces results that are precise and correct.

The other case where you should absolutely be writing tests is for critical customer interactions. Saving sensitive information or processing payment information are things that you don't want to get wrong.

Design Feedback Tools for Remote Teams

Good design doesn't happen in a vaccuum. As a remote team, though, we don't get to sketch around a table together, see each other's whiteboards, or have other daily opportunities for in-person collaboration.

Instead, we mostly share screenshots in group chat and, sadly, a lot of ideas don't even make it that far. It can feel like a high barrier to entry to post something online for the team to review — just by uploading, it becomes *Important* even if it's a simple sketch.

But if we're not able to share work in progress, we miss out on the value and ideas of our teammates and can end up working toward disparate goals. I want to dismantle barriers and make feedback and conversation about design a regular, fun part of our team process. It's essential to share half-baked designs, interface sketches, and unpolished ideas — even more so because we don't inhabit the same physical space.

Everybody agrees our products and apps will be better for it, but like all things with remote work, it takes an intentional commitment. You have to build even casual feedback into your workflow. With that in mind, I've been testing a few design tools meant to help facilitate asynchronous design feedback and communication. Here are my notes and thoughts on the three products we've tried so far.

Red Pen

RedPenCommentsRedPenAddComment

Overall, Red Pen was the fastest and most intuitive tool. This was also the service that everyone on the team actually tried and used successfully — the other two had much lower participation. This, more than anything else, is an indicator of its value. If nobody uses it, it's useless.

Pros:

  • It's easy to share and comment on designs without having to create an account (plus the workflow for account creation is smart).
  • Easy to navigate through designs using keyboard.
  • Simple and fast commenting. All our team members contributed with ease.
  • Tells you who has seen a comment (e.g., "read by meredith") and a few other nice interface features like that.
  • Retains all versions of a design.
  • Browser and email notifications tell you when there are unread comments.

Cons:

  • When we tested it there was no way to customize notification settings — some of us got email updates, some of us didn't, and it wasn't clear why. While the notifications were fairly intuitive, it would be nice to be able to adjust preferences.
  • No "view all comments" option, yet. They say they're working on this feature. Without it, there's no way to get an aggragate view of all feedback for a project.
  • No way to see all versions of a design at once.
  • There doesn't seem to be a way to easily download files (not a huge deal for us).
  • You can only upload png files.

Not seeing all the comments is actually a pretty big deal for me. As the lead designer, I want to be able to take all the feedback, consolidate and translate it into tasks (which live as tickets in GitHub or JIRA). Red Pen would work better for quick feedback on sketches and design ideas, less so for long conversations or contentious feature decisions.

Red Pen is also the most expensive of the tools we tested. I sent them a couple of emails about nonprofit rates and haven't heard back.

InVision

InVisionnewCommentInvisionAllComments

InVision is like the Photoshop of design feedback tools. It can do a lot of different things, and feels a bit bloated as a product (when looking solely for design feedback, at least). But they have put a lot of thought into the design and functionality of their suite of tools, and you can tell that this was created by and for designers.

Pros:

  • You can draw/sketch on designs and toggle comments on and off.
  • Notification options can be set at a user level and changed with each comment.
  • You can build clickable prototypes using wireframe images.
  • Ablility to upload all the file types (or at least a lot of them) and vector handling. There is also a separate repo for assets.
  • There is a conference call feature for live design walkthroughs. We tested this recently with wireframes for a new site and it worked well.
  • The project history page has rich data — I'm not sure how practical any of it is, but it was fun to see.

Cons:

  • Conversations are harder to access (a few clicks to see full thread).
  • Inviting people to comment takes a few more steps, and the sign up process is not intuitive.
  • Navigating between designs within a project, and between different projects, takes quite a bit of menu searching and clicking.

This is not a lightweight product, and while there are a lot of fun features, our team didn't consistently use — or even try — most of them. If we're attempting to cultivate a lower barrier to entry for feedback, this is not the tool I would choose.

InVision does offer nonprofit discounts for the more expensive payment tiers, and has been responsive and helpful when I've reached out.

Conjure

ConjureDrawer

Conjure fell somewhere in between InVision and Red Pen for me. It wasn't as feature heavy as InVision, but wasn't as fast or intuitive as Red Pen. There are a lot of nice elements, but it was the least used by our team during testing.

Pros:

  • A nice way of highlighting particular areas of a design to comment on (drag to select).
  • Pro level is currently free during beta.
  • You are able to approve a project when the feedback period has ended.

Cons: 

  • There's a separate menu you have to click to see the full thread of a comment. You can't see responses to a primary comment on the design itself.
  • Adding collaborators is more complicated than other tools we tried.
  • Navigating between projects and designs is clunky.

Overall it comes down to what our team will actually use. InVision has so many great features, but it also feels needlessly complicated for the purposes of fast feedback. We don't need every single customization option when looking for quick opinions on a design direction. Red Pen, on the other hand, had the most intuitive interface and was the product everyone actually used while testing. It is opinionated in its simplicity and that works to its advantage here.

Despite the higher price and some interface limitations, Red Pen will likely be what we use for sharing sketches and mockups. As with so many things, the right tool is the one that people will use.

For clickable prototypes and more formal design presentations and walkthroughs, I will continue to use InVision. To me it feels more like a protoyping and client-services tool than a home for internal feedback. (For a detailed comparison chart of other prototyping tools, check out http://prototypingtools.co.)

Red Pen Conjure InVision
Pricing $30/month for 10 projects $25/month for unlimited projects (currently free in beta) $22/month for unlimited projects (one designer)

How We Build Community

As a remote team, we pay special attention to how we communicate and build connections within our organization. But the need for community and connection extends beyond just our team — we're keenly aware of the challenges faced by the often lone technologists working at our member organizations. Even if you have supportive colleagues, it's not easy to be the only person at your organization who does what you do.

Organizing opportunities to learn together is a core component of our team’s commitment to “Always Be Learning.” To that end we have recently announced a number of new programs that we hope will help people connect and create affinity groups, ask questions of us and one another, and form a more tight-knit community and support network.

There are a number of existing online communities for news technologists — the NICAR-L mailing list comes to mind, as well as various Twitter chats, Google groups, etc. — but we want to create more opportunities for people to casually connect with each other online and to meet “face-to-face” (even if these face-to-face interactions are mediated by technology). This is why, as a team, we have a daily standup meeting, or scrum, using Google Hangout where we can actually see each other (via video chat) and communicate via always-on chat throughout the day (using HipChat).

Since part of our role is to support a network of organizations, we’re trying a number of things to create similar experiences for the broader community of technologists at INN member organizations and beyond.

Book Club

Our News Nerd Book Club is an idea that sprang from a discussion with Ryan Nagle about how we might replicate a shared team library as a distributed team. Instead of mailing books around to each other we decided to start a book club where we pick a book each month, read it and then get together to talk about it (either via Google Hangout or, occasionally, in person if we happen to be at a conference or other team gathering in the same physical space).

We decided to open the book club to anyone because we view these get togethers as not only a time to gather and discuss the book itself, but also as a scheduled time every month to convene a community that might not otherwise have an opportunity to come together outside of conferences (and, for people at organizations without a significant travel budget, that might not meet at all).

If you’d like to join the club, we have a crowd-sourced list of potential books (we’re trying to keep the selections accessible to a general audience and not overly technical), a Twitter account you can follow and our next hangout is scheduled for December 10.

Office Hours

We now hold monthly open office hours where anyone can sign up for a slot to come talk to our entire team about projects you’re working on, questions you have or really anything you’d like to chat with us about. The aim here, again, is to set aside time each month to get people together to learn from each other and for us as a team to spend time thinking about different problems than we typically work on in our day-to-day work.

We see these office hours as part of our commitment to transparency (all of our office hours are open by default, meaning that anyone can drop by and listen in or join the conversation) and a way to give back to the community. They're also a great way to generate new project ideas and possible leads for our consulting work.

Additionally, we hope that these office hours provide students, recent grads or prospective team members a non-threatening opportunity to get to know us, other members of the news and tech community, and to see first-hand how we work.

Open Chat

Another tool that has become indispensable for our team, as for many distributed teams, is some sort of asynchronous, always-on chat (we use HipChat and Slack is another popular choice).

In addition to our private team chat room where we talk about projects, share interesting links and post the occasional animated GIF, we now have a semi-public HipChat room that is open to any news technologists (particularly from INN member organizations, but we’re not too picky) who want to come hang out with us, ask occasional questions, share projects and interesting things they’re reading, and generally feel like part of our extended team.

If you’d like an invite to this room, we need to add you to our HipChat account (HipChat is kind enough to offer free unlimited accounts to nonprofit organizations), so just send an email to nerds@inn.org and we’ll get you an invitation.

Weekly Newsletter

Finally, just last week we sent out the first edition of our new weekly newsletter, Nerd Alert. This newsletter is a collection of interesting things our team is reading, open source tools, projects we're excited about, some random fun stuff and, perhaps most importantly, an opportunity for us to highlight "guest stars" whose perspectives we value and want to share with our readers.

Our aim with this newsletter is to capture and share some of our team's collective knowledge that might otherwise be trapped within our HipChat room or relegated to Twitter or an inaccessible email thread, while also highlighting voices that are less-often heard (particularly, again, the "lone wolf" technologists at our member organizations).

You can sign up for the newsletter right here and if you believe you or anyone you know would make a good guest star for a future newsletter, shoot us an email and we'll add you to our list.

Those are a few recent things we've been trying to help build strong networks among INN members and the broader journalism and tech community. While they're not a replacement for spending time together in real life (something we're hoping to do more of in the new year) we hope they're at least a start.

If you have any ideas for other things we might want to try, feel free to leave a comment or get in touch. We'd love to hear from you!

How We Make Remote Work Work

The INN nerds are a distributed team, which means we work in different locations and time zones and sometimes in our pajamas. Working from home full time also means we have to think intentionally about communication, structuring our days and setting boundaries around our work.

Here are a few of the tips and resources we've found helpful as we learn how to work effectively as a remote team.

Use Your Words

Remote work requires varsity-level communication. Not only do you need to keep your colleagues informed about what you're working on, but you also need to speak up about challenges and frustrations. Lacking the ambient awareness of a shared physical space, your coworkers can't see if you're struggling or confused, or if you have bruised feelings because of a miscommunication. It's on you to proactively reach out and address things sooner rather than later.

To help facilitate this sort of openness, we start every day with a short standup meeting using Google Hangouts. This allows us to review what we're working on, set priorities and address obstacles. Our weekly team meeting gives us space to discuss the bigger picture stuff, review current projects and set priorities for the following week.

We use HipChat, Google Hangouts and other tools to stay in touch throughout the day. (See the full list of our favorite tools below.)

Boundaries

Remote work offers the flexibility of setting your own schedule, but it also means you can feel like you're working 24/7. We think it's important to set boundaries around our work each day. Work reasonable hours. Don't send or expect responses to non-emergency email after hours. When working in different time zones, don't feel bad about reminding a team member that a late afternoon meeting in their time zone might be well into the evening for you. Taking time to not work makes our working time more productive.

During the day, it's all too easy to get sucked into our screens and not blink for hours on end. To counteract this sort of faux-productivity, we take a lot of walks and other short breaks away from our screens. Snacks and coffee are important, too.

Motivation

Building habits and routines can help prevent feelings of disconnect or isolation — and feelings of wanting to stay in bed and catch up on The Vampire Diaries. The advice is almost rote at this point, but for good reason: Have a dedicated space in your home where you "go to work." Take a shower. Put on pants.

Sometimes a change of scenery can help reset your focus. Take advantage of coworking spaces in your town or the tried-and-true coffee shop work session.

And most importantly: If something isn't working, or you start to struggle with lack of direction or motivation, speak up. You may work by yourself but you're not alone.

Face Time

While we're primarily remote workers, we like to see each other's faces in person a few times a year. There are some things that are just easier to do when we're all in the same room. These IRL meetups are essential for keeping us connected us a team. We tackle long-term planning and major projects, and build camaraderie over good meals and music and conversation.

Resources and Tools

  • There are great remote work tips in this Source article by Christopher Groskopf
  • Helpful tips on remote productivity
  • The books on remote work
  • HipChat: We use this as our group chat tool and always-on back channel
  • GitHub: For versioning and hosting our open source projects
  • Bitbucket: Versioning and hosting for Largo child themes for all the member sites we host. This allows us to add devs at member organizations to a repo just for their child theme so they can commit changes to a theme for us to review and push to production.
  • JIRA: For project management, planning sprints and iteration cycles, time tracking, and service desk features
  • Bee: Combines issues and tickets from JIRA, GitHub and others into a streamlined interface. Also offers time tracking and task prioritization.
  • Screenhero: Remote pair programming software
  • Google Hangouts: For meetings and daily scrum
  • Dropbox: For file sharing
  • 1password: For password management (synced to everyone's computers/devices using Dropbox)

Also, make sure to check out our growing list of tools and services (including many of the above) that offer discounts to nonprofits.

Real Life

We can spout best practices and advice all day, but the real life application can vary drastically depending on barometric pressure, how early you woke up to a crying baby (or puking cat), and countless other variables. One of the joys of remote work is that it makes room for the realities of life. As a team, we're choosing to trust each other and extend enough flexibility that a work day doesn't have to be an immutable cage.

[Full disclosure, I wrote this post in pajamas, curled up on my couch under a blanket.]

To illuminate more of the real-life applications of remote work advice, we'll soon be launching an occasional series of interviews with remote workers — starting with our own team — that will explore how different people make remote work work: what their set-up looks like, how they structure their days, and what they do in the face of frustrations or flagging motivation. We hope to collect honest portrayals of our modern working life and learn from each other in the process. Watch for more in this space soon.

Want to share your remote work experiences? Get in touch at nerds@inn.org.

Showing How We Work

We're excited to announce the release of a new collection of documents that show how our team works.

In putting this collection together, we wanted to go beyond a style guide (also important, and we're working on that, too) to try to explain as much as possible about the process and values that inform our work: What makes our team unique, how we recruit and hire new team members, our internal process, and how outside members or clients interface with our process.

Opening up our process has a number of benefits for us as a team and, we hope, for others, as well.

Codifying existing processes in one place makes them easier to reference and helps keep the team on the same page. It allows new hires to get up to speed faster and gives prospective employees insight into how we work, our mission and values, and whether working with us would be a good fit.

It also helps external partners, like INN members and our consulting clients, learn how to work with us most effectively.

Above all, we hope that collecting this information in one place will be useful to other organizations who are building and managing similar teams.

This is especially important for us because INN's mission includes a strong educational component and we want to do everything we can to help our members make smart technology decisions.

By showing not only our process, mission and values, but also expanding the scope of this collection to include things like job descriptions, how we run meetings and the tools we use to work effectively as a remote team, we are attempting to create a sort of "missing manual" for running a news apps and technology team.

We hope, over time, to make this manual even more comprehensive as we refine our process and our thinking. And we hope that providing this model will make it a little easier on organizations and managers traveling down this road in the future.

We're grateful to the teams that have come before us who have written and released documentation that served as a source of inspiration for various parts of our team docs, particularly:

- ProPublica's News App and Data Style Guides
- The NPR Visuals Team's app template, coding best practices and manifesto
- Guides and process docs from The Chicago Tribune's News Apps Team
- MinnPost's style guide

This is a work in progress and we plan on updating frequently so we'd really value your feedback and contributions. Feel free to contribute to the project on GitHub or send us suggestions to help us improve our existing docs (or to propose new sections you'd like to see us add).

Spaces or tabs: which will YOU choose?

Source: 1985 McCall's Magazine

What follows is a Very Important Blog Post. Please, take this seriously.

It’s a question that every tech team inevitably debates and, as with many topics in software development, people can be downright religious about their stance on spaces versus tabs.

I’m here to (officially >_<) declare such debates a waste of time.

Be flexible enough to use either, depending on the project.

What’s more important than an individual's preference for spaces or tabs is that the team agree on a style of writing code and stick with it.

In our case, since we do quite a bit of work with WordPress and WordPress’ best practices indicate the use of hard tabs, we decided to use them for our WordPress work.

For any code that we write outside the context of a specific framework/project (e.g., WordPress), we use four spaces. This includes HTML, JS, CSS, Python and, occasionally, PHP without WordPress.

Soon, you'll be able to see the details of our coding style guide. I'll update this post once that's available.

If you’re interested, my personal preference is spaces.

Here’s why: hard tab length is treated differently by different text editors and may be adjusted to suit an individual’s preference. This can mean varying degrees of readability for the same piece of code on different machines. It can also wreak havoc on code written in languages like Python or Coffeescript, where whitespace is, in fact, significant.

For more information on why you should never ever use hard tabs (except sometimes), check out:

For a less one-sided look, check out Tabs versus Spaces: An Eternal Holy War.

This concludes my Very Important Blog Post.

Editor's Note: ...but don't even get us started about using two spaces after a period.