Testing, Integration, and Contracts – Part 2

This post will continue on directly from the first post, where I introduced you to the concepts as well as an application that can serve as a guide to some of the ideas. I highly recommend reading that first post for the context if you haven’t already.

In this post we’ll get into writing some tests for the the Thanos React application that I showed you. As I said at the closing of the first post, my goal is to introduce contract-style thinking along with the expression of that thinking in contract-style tests. I’m saying contract-style tests rather than contract tests directly because here I’m just focusing on the ideas around contract-based thinking, wherein you have something that is a consumer and something that is a provider. Martin Fowler put it this way:

“An integration contract test is a test at the boundary of an external service verifying that it meets the contract expected by a consuming service.”

In fact, I would argue that an “integration contract test” can really be just shortened to “integration test” or “contract test.” Anything that integrates with something else has, even if only implicitly, a contract. Thus I wonder if the “integration/integrated” divide can be handled by referring to “contract tests” and “integrated tests.” Solving the poor naming problem may be a lost cause at this point so I won’t harp on it too much here.

A contract is basically a set of agreements between two or more parts. In our context, a contract defines the expected behavior of a given component or service and what assumptions are reasonable to have about its usage. A few points follow from this:

  • These components can communicate well. Meaning, they send the correct messages, accept the correct messages, do not accept (or gracefully handle) incorrect messages.
  • These components can communicate poorly. Meaning, they send the wrong message, accept the wrong message, don’t send any message at all (when they should), do send a message (when they shouldn’t).

If you add time to this, it can get a bit more complicated. For example, you have to consider if the components send the correct message only at the correct time; accept the correct message only at the correct time; perhaps respond correctly to a series of message within a given time duration, and so on.

So now let’s apply some of this to the application I introduced in the first post.

Getting Set Up

If you want to follow along with me, clone the Thanos React repo. You can write the tests I provide you with here as JavaScript files within the src/components directory for now. If you would prefer not to code along and just get a feel for the tests, that’s perfectly fine too. All of the test code will be shown in this post. But you can also find the tests in the a top-level tests directory. You can simply move those into src/components as well.

Regardless of how you use or build the tests, you can execute the tests by running the following command from the project root directory:

npm test

Coder and Tester Working Together

As I’ve said in another post, I do believe testers are a type of developer. So what I would like to title this section is Developers Working Together. But we still, as an industry, can’t handle some of our own distinctions. So what I’m focusing on here is someone with a speciality in test development and someone with a speciality in code development working together to determine how best to test the Thanos React application.

I can’t stress how important I feel this bit is. Really, the entire first post has been leading up to this: the healthy interaction of test-focused specialists with those who specialize in code development. This is what modern testing looks like in some contexts and this is part — but only part! — of what it means to be a “technical tester.”

For the purposes of keeping us focused I’m only going to settle on two areas here: the rendering of components and the props that are communicated between components. If you read the first post, I gave you a very high-level view of how React works. I’m not even going to attempt to be comprehensive here with the tests. Rather, this is just to give you some ideas about testing at an abstraction layer below the UI but still focused on quality concerns.

Testing at the Edges

Let’s first settle on what the unit tests here would be. I don’t expect you to have all the details but this is something that you, as a test specialist, would likely work with a developer to understand. But, for now, do you have an instinct on this? If you don’t, that’s totally fine. But do know that this instinct is something you’ll want to develop.

Let’s go with this: anything at the “integration/integrated” divide should be something that is exposed such that it can be communicated between components. A way to frame this is as interaction tests or as collaboration tests. And, yes, here we have two more terms that the industry has thrown at us. I’ll come back to all these terms and see if we can’t wrap them up in the context of contract-style thinking and thus contract-style testing.

For now, however, that gives us a particular edge to consider. We’ll say that private methods within any given component are basically unit tests. We can also say that the default state or internal state would likely be where we craft unit tests. That said, this internal state will likely be tested indirectly with interaction tests.

Another thing we can probably agree on: let’s not test style attributes with our contract tests. React has a lot of logic that deals with rendering with styles, which you can see in how my DisplayScreen returns a div with styling.

Finally, let’s also not test our PropTypes. You can see the PropTypes for Thanos React. We’re not really testing those because that would essentially be testing React rather than how our application uses React.

So our unit edges are defined. Our end-to-end is also defined because that would be testing the whole application as it appears in your browser or on your mobile device. But what about those edges that are part of the integration/integrated arena we are talking about? Well, let’s first jump back to basics a bit.

Intersections at the Edges

What is most if not all test execution (as opposed to design) about? It’s basically this:

input --> action --> output

That means, fundamentally, testing as an execution activity breaks down to state and interactions. To have an interaction implies there is some sort of collaboration going on between two distinct things. To have state implies that something can be one way at a given time and another way at a different time. And the interactions will often mediate the state.

So how does this fit in with all those terms we talked about? We have integration tests, collaboration tests, interaction tests, and contract tests. We’re not going to settle any debates in this post and that’s not my goal. So for now take a look at a visual. This is probably one of the better ones I’ve seen out there.

It’s not mine; it comes from a discussion around organizing integration tests. I’m going to repeat salient points from that here and then expand on them a bit. Let’s call this a view of “integration testing.” That’s made up of two parts: collaboration and contract.

The numbered points are broken down like this, with a slight change in terminology.

  1. The consumer (client) asks the provider (supplier) questions.
  2. The provider accepts those questions.
  3. The provider answers those questions.
  4. The consumer can understand the answers.

Now here is where I expand a bit on the material I just referenced. I should note I’m basically repeating a few points I said when talking about conceptualizing test intersections.

The subject of a collaboration test is the client or the consumer. And it’s asking the following:

  1. Do I ask my collaborators the right questions?
  2. Can I handle all their responses?

So the fundamental goal of these tests is figuring out an answer to this question: Does the consumer ask the provider the right questions? Dropping down an abstraction level, we can frame it as: Does the consumer invoke the correct method on the provider in the correct context with the correct parameters?

The subject of a contract test is the supplier or the provider. And it’s asking the following:

  1. Do I even try to answer the question you are asking?
  2. Can I give you the answer you might think I can?

So the fundamental goal of these tests is figuring out an answer to this question: Can the provider handle the question in the first place? Dropping down an abstraction level we can frame it as: Does the provider have a corresponding implementation that the consumer is depending on?

A disconnect between how the consumer thinks the collaboration should go and how the providers themselves view it is a cause of many problems in a programmatic context like this. This manifests in three ways:

  • The consumer asks a question that the provider can’t answer.
  • The consumer misses checking for a response that the supplier might give.
  • The consumer checks for a response that the supplier can’t give.

I’m putting a lot on the consumer with those points and that’s part of what’s called consumer-driven contract testing, which is a term you’ll come across. The meaning is exactly what it sounds like: a focus on the consumer and its interactions above that of the provider. Even with that focus, it helps to look at both sides of the interaction.

Considering things from the provider point of view:

  • For every test of the type “do you even try to answer my question”, there must be a contract test that calls that method with those parameters.
  • For every test of the type “do you return the correct response”, there must be a contract test that expects that response.

Considering things from the consumer point of view:

  • For every test of the type “do I ask this question correctly”, there must be a contract test that calls a method with parameters.
  • For every test of the type “do I handle the response correctly”, there must be a contract test for the implementation that returns that response.

Notice how this helps us start to frame our coverage and we can do this below the level of the UI, thus framing quicker feedback tests. Even more to the point, however, we can do this before coding has even begun. We can use contract-style thinking to put pressure on design, which is treating testing as a design activity.

Stop! And Assess.

How do you feel about what you’ve read so far? Does this make sense? You may disagree about how I’ve formulated some of this and that’s fine. But do you see how this kind of thinking starts to bridge the gap between a test specialist and a code specialist? I tried to make sure that each condition I stated above, which would be covered by a test, was capable of being framed at different abstraction levels.

Think about what this means when we consider “edge-to-edge testing”, which code specialists tend to focus on, and “end-to-end testing”, which test specialists tend to focus on. Even more to the point notice how this takes us to where “edge-to-edge” shades into “end-to-end” and allows to ask how many of the edge-to-edge we need that would allow us to reduce our dependence on the often heavier, often more brittle and often longer running end-to-end tests.

Regardless of our speciality we all want the tests to be something that can run quickly to give us timely feedback but also run in a way that we trust is indicative of the actual user experience. “Run quickly but still provide useful feedback” is the essential mantra and that means considering how much testing “below the UI” we can do. Fair warning: this post is not going to provide you an answer. I’ve already given you the application. I’m going to give you the tests. The conclusions will be up to you.

Writing Contract-Style Tests

You may feel a bit at a disadvantage here if you don’t know React. Don’t worry about that. Just follow along with the ideas. There are a couple of things that immediately jump out as far as testing in this context.

  • We want to test that the main application component renders.
  • We want to test that all the other components that the application component renders.
  • We want to test any elements rendered by the component that are not dependent on the props passed to the component.
  • If props do provide a basis path for how a given component renders or interacts, then we want to pass different props and test the behavior of the component.

So what this means is we want consider, at minimum, some contract style tests for rendering and for props. There are two test files provided in the tests directory: displayScreen-render.test.js and displayScreen-props.test.js.

There’s some boilerplate to set up:

We can also do some setup common to both:

The above function uses the mountedDisplayScreen variable to mount a DisplayScreen with the current props. It can also return the DisplayScreen that has been mounted. This is really important. We’re essentially establishing the basis of our context for running our tests. That will be shown best by how we use these things before each test:

The props and mountedDisplayScreen always start off undefined. The above displayScreen function means that when needed by a test, a DisplayScreen will be mounted with the relevant props. We’re also not assuming any particular props for a given test thus it will be up to each test to establish what it needs.

This is a critical aspect of contract-style testing: establish a minimum baseline that assumes very little about state or interaction. Leave that information to be supplied by the contract tests.

Assessing What to Test

Let’s step away from code for a second and just consider some contract details that we can distill from the code. Again, we’re only focusing on the DisplayScreen component here. We have some very definite test conditions which I’ll provide along with a link to the code.

  • If a displayImage prop was passed, then a div returned by the component should provide a CSS background-image style for that displayImage. Otherwise, it should be an empty string (which means the style should not be set). Link to code.
  • displayImage is expected to be a string, and is optional. Link to code.

Even if you’re not where you want to be yet at reading code, take some time and checkout out the links. See if the test conditions that I mention are at least recognizable to you in the code. Continuing on …

  • If a displayMessage prop was passed, render that displayMessage within the DisplayContent. Otherwise, don’t render anything here (null). Link to code.
  • displayMessage is expected to be a string, and is optional. Link to code.

Note in the first one there we actually have two test conditions because of the basis path: render DisplayContent or do not render DisplayContent. And please keep in mind that all of this describing a type of contract. This contract is operative at the level below the UI but, of course, it will ultimately be fulfilled with a UI interface.

There are some invariants to be aware of. Invariants are always good test conditions.

  • A div is always rendered, which contains all components. It has a particular set of inline styles. Link to code.
  • A DisplayTimer is always rendered. It does not receive any props. Link to code.
  • A DisplayActivate is always rendered. It receives the value of the passed onActivate prop as its onSlide prop, regardless of if it was defined or not. Link to code.
  • onActivate is expected to be a function, and is optional. Link to code.

Again, this is all about distilling information from the code that exists. But clearly these are test conditions that could have been written out before code was written. So we could have used testing as a pressure on design to suggest the code to be written in the first place by figuring out the contracts. Here we’re using testing as a pressure on design to distill the contracts we put in place.

Writing the Tests

Now let’s take the above and write the tests. Again, you can simply reference the code I provide here. You can also reference the tests that I did put in the tests directory.

Render Contracts

We know that DisplayScreen will be rendered and that it will display a div as part of that process. This is a contract we want to make sure is always enforced.

This is basically equivalent to a tester checking that the display screen actually shows up in an end-to-end test. But notice what we’ve done here: we’ve already done an integration test in the sense that we’ve checked if something that has to be rendered has in fact rendered.

We know that DisplayScreen will render its children components, each of of which displays a div. This is a contract we want to make sure is always enforced.

Notice above that in the test for rendering the DisplayContent component, we pass in a prop for the displayMessage. Right below that we have a test where no prop is being passed in at all. So we handle two basis paths there.

We know that the DisplayTimer receives no props. We can see how DisplayTimer is brought in. We can also see what DisplayTimer renders. That’s important. The DisplayTimer is designed specifically not to be overridden by any property.

Props Contracts

We known that an onSlide action is checked for and if found an onActivate call is made. We can see that with the attributes value. There are thus two contracts operative for onActivate. The behavior is different depending on which situation occurs.

We saw this exact some context play out with our render tests, when we handled whether DisplayContent was passed a prop or was not passed a prop. Here we have a different bit of functionality but essentially the same contract.

So What Have We Learned?

Well, first let me say this. The most direct influence on this post, at least from the React example part, was the article The Right Way to Test React Components, the example of which I repurposed a bit here. There were a few other articles that I found quite valuable and that guided me a bit. These were, in no particular order, Testing React / Redux Apps with Jest & Enzyme, React component contract driven testing and Components testing in React: what and how to test with Jest and Enzyme.

I mention all that because obviously this post was heavily dependent upon React to provide an example. And I read the above article, as well as many others, because I was in a situation where I was working with code specialists to figure out how to perform contract-style testing with a React application.

I provided a lot of links to my particular code here because I wanted to make sure that you could navigate what I was talking about at one abstraction level with the code that implemented what I was talking about at another abstraction level. This is a key skill of test specialists. This is just one concrete example of what I was talking about when I said testers shift between polarities.

What I wanted to show here was an example of how a code specialist and a test specialist could work together at one of the intersections of testing, which is that of contracts. Specifically, I wanted to focus on contract-style thinking and how that helps us navigate some of the semantics we have around test terminology. In a future post, I’ll cover more direct contract-style testing with the Pact tool.

For now happy testing … and coding!

Share

About Jeff Nyman

Anything I put here is an approximation of the truth. You're getting a particular view of myself ... and it's the view I'm choosing to present to you. If you've never met me before in person, please realize I'm not the same in person as I am in writing. That's because I can only put part of myself down into words. If you have met me before in person then I'd ask you to consider that the view you've formed that way and the view you come to by reading what I say here may, in fact, both be true. I'd advise that you not automatically discard either viewpoint when they conflict or accept either as truth when they agree.
This entry was posted in Contracts, Testing. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.