Felix Kling

Felix Kling

1567754898

How React Storybook can simplify component testing

Originally published by Raphael Ugwu at https://blog.logrocket.com

Introduction

Every developer wants to build software that works. We can make sure our software’s code works flawlessly by isolating it and showing its behavior in a series of tests. The same can’t be said for our components as they are tested within the context of our app.

Storybook lets you view and interact with your components in an isolated manner. It’s just like unit testing but for UI components. In the words of Storybook’s documentation:

Storybook is a user interface development environment and playground for UI components. The tool enables developers to create components independently and showcase components interactively in an isolated development environment.

In this post, we will find out how Storybook can help us create UI components and improve our component testing.

Getting started with Storybook

Let’s start with bootstrapping a new React project and in it, we’ll install Storybook via CLI:

npx create-react-app my-storybook-app
cd my-storybook-app
#install storybook
npx -p @storybook/cli sb init
yarn storybook

When running yarn storybook, you should see Storybook’s testing page on the local address http://localhost:9009/:

For the purpose of testing, we’ll create a basic component – a button called CoffeeButton. It displays the number of cups of coffee to be served.

// /src/stories/CoffeeButton.js
import React, { useState } from 'react';
const ButtonStyle = {
    backgroundColor: 'lightgrey',
    padding: '10px',
    borderRadius: '5px',
    fontSize: '15px',
    border: '#66c2ff 3px solid',
    cursor: 'pointer'
};
const CoffeeButton = ({children}) => {
    const [count, setCount] = useState(1);
    return (
        <button style = {ButtonStyle} onClick = {() => setCount(count + 1)} >
        {new Array(count).fill(children)}
        {(count < 2)
        ? <div> Please serve 1 cup of coffee </div>
        : <div> Please serve {count} cups of coffee </div>
        }
        </button>
    );
};
export default CoffeeButton;

Storybook works by using “stories”. A story is a function that contains the single state of one component and renders that component to the screen for testing purposes. Let’s write a story for our CoffeeButton component. In src/stories create a file and name it CoffeeButtonStory.js:

import React from 'react';
import { storiesOf } from '@storybook/react';
import CoffeeButton from './CoffeeButton';
storiesOf('CoffeeButton', module)
  .add('Black', () => (
    <CoffeeButton>
      <span role="img" aria-label="without-milk">
  &lt;/span&gt;
&lt;/CoffeeButton&gt;

))
.add(‘White’, () => (
<CoffeeButton>
<span role=“img” aria-label=“with-milk”>

  &lt;/span&gt;
&lt;/CoffeeButton&gt;

));

UI testing

Storybook offers different techniques for testing UI components. Components need to undergo tests for a variety of reasons some of which are:

  • Detection of bugs
  • Tests can be documented to serve as guidelines for other developers who will work on the project
  • To prevent stuff from breaking during new commits

Let’s go on to examine some of the ways Storybook can make component testing seamless.

Structural testing

Structural testing involves the testing of a component based on the knowledge of its internal implementation. Storybook implements structural testing through storyshots – an add-on that works by comparing snapshots of code. To install storyshots run:

npm i -D @storybook/addon-storyshots react-test-renderer

react-test-renderer renders React components to pure JavaScript objects without depending on the DOM. This makes it possible to grab the screenshot of the DOM tree rendered by a React DOM.

After installing, let’s create a test file storyshots.test.js, in it we’ll initialize storyshots:

// src/storyshots.test.js
import initStoryshots from ‘@storybook/addon-storyshots’;
initStoryshots({ /* configuration options */ });

To test a component we run npm test. What this does is generate a snapshot where you can inspect the component’s output. Every time you run a test, a snapshot is automatically generated and compared with snapshots generated from previous tests. If storyshots spots any differences, the test will fail. Below is a snapshot generated on testing for the first time:

Our tests were successful, now let’s try to change something in our CoffeeButton component. Change line 16 of CoffeeButton.js to:

? <div> Please DO NOT serve 1 cup of coffee </div>

On running tests we get the following errors:

A more detailed view:

Automated visual testing

Automated visual testing involves automatically verifying that our UI visually appears as intended. This is useful in cross-browser testing as it can detect lapses that escaped the observations of developers. Storybook tests UI visually via an add-on called storyshot-puppeteer . Same as storyshots, this add-on works by comparing screenshots – only this time it takes screenshots of the browser and not code. To install storyshot-puppeteer run:

npm i -D @storybook/addon-storyshots-puppeteer

Once installed, to make it compare UI and not code we’ll need to override the test comparison with imageSnapshot from the puppeteer add-on. We can do this by editing the initStoryshots function we created when carrying our structural testing. We’ll also need to specify the URL where our storybook will be running:

// src/storyshots.test.js
import initStoryshots from ‘@storybook/addon-storyshots’;
import {imageSnapshot} from ‘@storybook/addon-storyshots-puppeteer’;
initStoryshots({
test: imageSnapshot({storybookUrl: ‘http://localhost:9009/’}),
});

Below is a snapshot generated when we test our images for the first time:

Should we change any UI property in our component, our tests will fail and puppeteer will return the difference in the form of snapshots. Let’s change a part of our UI. In line 3 of CoffeeButton.js, change the background color from lightgrey to lightblue:

backgroundColor: ‘lightblue’,

Now when we run the tests:

Below is a generated snapshot of the difference noticed by puppeteer in our UI:

In the diffing above, the original image is on the left, the modified image is on the right and the difference between both of them is in the middle.

Interaction testing

With Interaction testing, Storybook lets you display tests and their results alongside your stories in the DOM. It does this via an addon – react-storybook-specifications. To install this addon, run:

npm install -D storybook-addon-specifications

Then add this line to your addons.js file:

import ‘storybook-addon-specifications/register’;

react-storybook-specifications does not work alone, we still need to install the following:

enzyme: JavaScript’s testing utility for React.

enzyme-adapter-react-16: Enzyme’s adapter corresponding to the version of React you’re using.

expect: Jest’s inbuilt method used for checking that values meet certain conditions when writing tests.

To install these add-ons, run:

npm install -D enzyme expect enzyme-adapter-react-16

In our config.js file, we’ll import configure and Adapter from enzyme and enzyme-adapter-react-16. Notice that we’ll be having two instances of configure now so we’ll need to specify both like this:

import { configure as configure1 } from ‘@storybook/react’;
import {configure as configure2} from ‘enzyme’;
import Adapter from ‘enzyme-adapter-react-16’;
function loadStories() {
require(‘…/src/stories’);
}
configure1(loadStories, module);
configure2({ adapter: new Adapter() });

Let’s see how this works by testing part of CoffeeButton component. In CoffeeButtonStory.js, input the following block of code:

import React from ‘react’;
import { storiesOf } from ‘@storybook/react’;
import { action } from ‘@storybook/addon-actions’;
import { specs, describe, it } from ‘storybook-addon-specifications’;
import {mount} from “enzyme”;
import expect from “expect”;
import CoffeeButton from ‘./CoffeeButton’;
const stories = storiesOf(‘CoffeeButton’, module)
stories
.add(‘Black’, () => {
const story =
<CoffeeButton onMouseOver={action(‘click’)}>
<span role=“img” aria-label=“without-milk”>

    &lt;/span&gt;
  &lt;/CoffeeButton&gt;;
  specs(

() => describe(‘Black’, () => {
it(‘Should have the following text: Please serve 1 cup of coffee’, () => {
let output = mount(story);
expect(output.text()).toContain(
‘Please serve 1 cup of coffee’);
});
}));

return story;
})

As can be seen, changing our expected output will render an error to the DOM. Interaction testing via storybook-addon-specifications enables us to have a living documentation, where we can interact with our components and their test results side by side.

Conclusion

Storybook provides a great way to test our UI components. It may seem like we are doing away with unit testing but that’s not the case. Unit testing aims at finding out what went wrong with code. In this case, we’re testing React components and if something went wrong with our UI we would still ask questions about what markup we have to fix.

This implies that integration and snapshot tests are as good as unit tests – in this context. Should you want to play around with code, you can always check out the source code here on Github.

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow us on Facebook | Twitter

Further reading

An Introduction To Storybook

Building a Component Library with Angular and Storybook

Creat Custom Decorators with Storybook & Vue



#reactjs #testing #web-development

What is GEEK

Buddha Community

How React Storybook can simplify component testing
Autumn  Blick

Autumn Blick

1598839687

How native is React Native? | React Native vs Native App Development

If you are undertaking a mobile app development for your start-up or enterprise, you are likely wondering whether to use React Native. As a popular development framework, React Native helps you to develop near-native mobile apps. However, you are probably also wondering how close you can get to a native app by using React Native. How native is React Native?

In the article, we discuss the similarities between native mobile development and development using React Native. We also touch upon where they differ and how to bridge the gaps. Read on.

A brief introduction to React Native

Let’s briefly set the context first. We will briefly touch upon what React Native is and how it differs from earlier hybrid frameworks.

React Native is a popular JavaScript framework that Facebook has created. You can use this open-source framework to code natively rendering Android and iOS mobile apps. You can use it to develop web apps too.

Facebook has developed React Native based on React, its JavaScript library. The first release of React Native came in March 2015. At the time of writing this article, the latest stable release of React Native is 0.62.0, and it was released in March 2020.

Although relatively new, React Native has acquired a high degree of popularity. The “Stack Overflow Developer Survey 2019” report identifies it as the 8th most loved framework. Facebook, Walmart, and Bloomberg are some of the top companies that use React Native.

The popularity of React Native comes from its advantages. Some of its advantages are as follows:

  • Performance: It delivers optimal performance.
  • Cross-platform development: You can develop both Android and iOS apps with it. The reuse of code expedites development and reduces costs.
  • UI design: React Native enables you to design simple and responsive UI for your mobile app.
  • 3rd party plugins: This framework supports 3rd party plugins.
  • Developer community: A vibrant community of developers support React Native.

Why React Native is fundamentally different from earlier hybrid frameworks

Are you wondering whether React Native is just another of those hybrid frameworks like Ionic or Cordova? It’s not! React Native is fundamentally different from these earlier hybrid frameworks.

React Native is very close to native. Consider the following aspects as described on the React Native website:

  • Access to many native platforms features: The primitives of React Native render to native platform UI. This means that your React Native app will use many native platform APIs as native apps would do.
  • Near-native user experience: React Native provides several native components, and these are platform agnostic.
  • The ease of accessing native APIs: React Native uses a declarative UI paradigm. This enables React Native to interact easily with native platform APIs since React Native wraps existing native code.

Due to these factors, React Native offers many more advantages compared to those earlier hybrid frameworks. We now review them.

#android app #frontend #ios app #mobile app development #benefits of react native #is react native good for mobile app development #native vs #pros and cons of react native #react mobile development #react native development #react native experience #react native framework #react native ios vs android #react native pros and cons #react native vs android #react native vs native #react native vs native performance #react vs native #why react native #why use react native

Einar  Hintz

Einar Hintz

1599055326

Testing Microservices Applications

The shift towards microservices and modular applications makes testing more important and more challenging at the same time. You have to make sure that the microservices running in containers perform well and as intended, but you can no longer rely on conventional testing strategies to get the job done.

This is where new testing approaches are needed. Testing your microservices applications require the right approach, a suitable set of tools, and immense attention to details. This article will guide you through the process of testing your microservices and talk about the challenges you will have to overcome along the way. Let’s get started, shall we?

A Brave New World

Traditionally, testing a monolith application meant configuring a test environment and setting up all of the application components in a way that matched the production environment. It took time to set up the testing environment, and there were a lot of complexities around the process.

Testing also requires the application to run in full. It is not possible to test monolith apps on a per-component basis, mainly because there is usually a base code that ties everything together, and the app is designed to run as a complete app to work properly.

Microservices running in containers offer one particular advantage: universal compatibility. You don’t have to match the testing environment with the deployment architecture exactly, and you can get away with testing individual components rather than the full app in some situations.

Of course, you will have to embrace the new cloud-native approach across the pipeline. Rather than creating critical dependencies between microservices, you need to treat each one as a semi-independent module.

The only monolith or centralized portion of the application is the database, but this too is an easy challenge to overcome. As long as you have a persistent database running on your test environment, you can perform tests at any time.

Keep in mind that there are additional things to focus on when testing microservices.

  • Microservices rely on network communications to talk to each other, so network reliability and requirements must be part of the testing.
  • Automation and infrastructure elements are now added as codes, and you have to make sure that they also run properly when microservices are pushed through the pipeline
  • While containerization is universal, you still have to pay attention to specific dependencies and create a testing strategy that allows for those dependencies to be included

Test containers are the method of choice for many developers. Unlike monolith apps, which lets you use stubs and mocks for testing, microservices need to be tested in test containers. Many CI/CD pipelines actually integrate production microservices as part of the testing process.

Contract Testing as an Approach

As mentioned before, there are many ways to test microservices effectively, but the one approach that developers now use reliably is contract testing. Loosely coupled microservices can be tested in an effective and efficient way using contract testing, mainly because this testing approach focuses on contracts; in other words, it focuses on how components or microservices communicate with each other.

Syntax and semantics construct how components communicate with each other. By defining syntax and semantics in a standardized way and testing microservices based on their ability to generate the right message formats and meet behavioral expectations, you can rest assured knowing that the microservices will behave as intended when deployed.

#testing #software testing #test automation #microservice architecture #microservice #test #software test automation #microservice best practices #microservice deployment #microservice components

Chumarat Pat

Chumarat Pat

1599639298

Interaction Testing with React Testing Library

Testing is complicated. I’ve certainly never been good at it. For the longest time, I’ve only been focused on basic function input-output unit tests. Why? Because they were easy — you didn’t need to render HTML, you didn’t need to query DOM elements, you didn’t need to interact with said DOM elements. But of course, React component testing is a necessity for any mature codebase. And it finally came time for me to sit down and figure it out.

That’s when I discovered React Testing Library. And suddenly, everything seemingly became much simpler. All the complexities that I’ve encountered, but not understood, that made me put off React component testing disappeared. Hopefully, the same will happen for you.

#react-testing-library #unit-testing #react #jest #interaction-testing

Tamia  Walter

Tamia Walter

1596754901

Testing Microservices Applications

The shift towards microservices and modular applications makes testing more important and more challenging at the same time. You have to make sure that the microservices running in containers perform well and as intended, but you can no longer rely on conventional testing strategies to get the job done.

This is where new testing approaches are needed. Testing your microservices applications require the right approach, a suitable set of tools, and immense attention to details. This article will guide you through the process of testing your microservices and talk about the challenges you will have to overcome along the way. Let’s get started, shall we?

A Brave New World

Traditionally, testing a monolith application meant configuring a test environment and setting up all of the application components in a way that matched the production environment. It took time to set up the testing environment, and there were a lot of complexities around the process.

Testing also requires the application to run in full. It is not possible to test monolith apps on a per-component basis, mainly because there is usually a base code that ties everything together, and the app is designed to run as a complete app to work properly.

Microservices running in containers offer one particular advantage: universal compatibility. You don’t have to match the testing environment with the deployment architecture exactly, and you can get away with testing individual components rather than the full app in some situations.

Of course, you will have to embrace the new cloud-native approach across the pipeline. Rather than creating critical dependencies between microservices, you need to treat each one as a semi-independent module.

The only monolith or centralized portion of the application is the database, but this too is an easy challenge to overcome. As long as you have a persistent database running on your test environment, you can perform tests at any time.

Keep in mind that there are additional things to focus on when testing microservices.

  • Microservices rely on network communications to talk to each other, so network reliability and requirements must be part of the testing.
  • Automation and infrastructure elements are now added as codes, and you have to make sure that they also run properly when microservices are pushed through the pipeline
  • While containerization is universal, you still have to pay attention to specific dependencies and create a testing strategy that allows for those dependencies to be included

Test containers are the method of choice for many developers. Unlike monolith apps, which lets you use stubs and mocks for testing, microservices need to be tested in test containers. Many CI/CD pipelines actually integrate production microservices as part of the testing process.

Contract Testing as an Approach

As mentioned before, there are many ways to test microservices effectively, but the one approach that developers now use reliably is contract testing. Loosely coupled microservices can be tested in an effective and efficient way using contract testing, mainly because this testing approach focuses on contracts; in other words, it focuses on how components or microservices communicate with each other.

Syntax and semantics construct how components communicate with each other. By defining syntax and semantics in a standardized way and testing microservices based on their ability to generate the right message formats and meet behavioral expectations, you can rest assured knowing that the microservices will behave as intended when deployed.

Ways to Test Microservices

It is easy to fall into the trap of making testing microservices complicated, but there are ways to avoid this problem. Testing microservices doesn’t have to be complicated at all when you have the right strategy in place.

There are several ways to test microservices too, including:

  • Unit testing: Which allows developers to test microservices in a granular way. It doesn’t limit testing to individual microservices, but rather allows developers to take a more granular approach such as testing individual features or runtimes.
  • Integration testing: Which handles the testing of microservices in an interactive way. Microservices still need to work with each other when they are deployed, and integration testing is a key process in making sure that they do.
  • End-to-end testing: Which⁠—as the name suggests⁠—tests microservices as a complete app. This type of testing enables the testing of features, UI, communications, and other components that construct the app.

What’s important to note is the fact that these testing approaches allow for asynchronous testing. After all, asynchronous development is what makes developing microservices very appealing in the first place. By allowing for asynchronous testing, you can also make sure that components or microservices can be updated independently to one another.

#blog #microservices #testing #caylent #contract testing #end-to-end testing #hoverfly #integration testing #microservices #microservices architecture #pact #testing #unit testing #vagrant #vcr

Alayna  Rippin

Alayna Rippin

1597168800

Test Driven Development (TDD) with React Testing Library & Mock Service Worker

Let’s step up our testing game with two useful libraries that lend themselves excellently to a TDD approach.

Setting up

Whenever I want to try out something React-related, I use the library create-react-app. It gives you a ready-to-work-with basic React application with no configuration needed. Recent versions also come bundled with React Testing Library, so if you use the latest create-react-app, you can start using React Testing Library straight away. If not — install @testing-library/react and @testing-library/jest-dom in your existing React application.

I am going to implement the following functionality: a simple recipe list with a search function. It will look something like this to a user in a mobile browser:

Image for post

Image credits: Burger Photo by Robin Stickel on Unsplash, French Toast Photo by Joseph Gonzalez on Unsplash, Salmon Photo by Casey Lee on Unsplash

Starting with a failing test

I want to use a TDD approach, so let’s start with a failing test. At this point, there is no component yet, so of course whatever test we write is going to fail. But let’s start small; I want a component named ‘Recipes’ that renders the expected heading text. Here is my test for that expectation:

	import React from 'react';
	import { render, screen } from '@testing-library/react';

	test('renders the heading', () => {
	  render(<Recipes />);

	  expect(screen.getByRole('heading')).toHaveTextContent('Recipe Finder');
	});

React testing library exports a render method, which will render a component and all of its child components. It also exports a screen object, holding a number of queries we can use to select different elements in our rendered component (and its child components too). The **getByRole **query lets me select the heading element and make an assertion on its text content.

Why select the heading element by its role and not for example a CSS class? The guiding principle of React Testing Library is “The more your tests resemble the way your software is used, the more confidence they can give you.” Therefore, we want to write our tests as close as possible to how the ultimate tester — the end user — will be using the application. Users don’t see CSS classes or data attributes; they interact with text, label text and semantic elements and roles. Using queries such as getByRole also encourages us to write accessible code, since these selectors are available to everyone, including users of screen readers.

Making the test pass

Our first test fails as expected.** Recipes is not defined.** But this is the first step in TDD — a failing test. Now, let’s make it pass by writing the simplest possible component with the correct heading and then importing it in our test file. Now, let’s re-run the test. It passes!

	import React from 'react';

	const Recipes = () => {
	  return (
	    <div>
	      <h1>Recipe Finder</h1>
	    </div>
	  )
	};

	export default Recipes;

Using further queries, we can make similar expectations for the input element and the “Find” button. The button also has a role, but for the input field, I will use the getByPlaceholderText query, since that is probably the closest query to how the user would find it on the page.

Start with a failing test…

	import React from 'react';
	import { render, screen } from '@testing-library/react';
	import Recipes from './Recipes';

	test('renders the heading, input field and button', () => {
	  render(<Recipes />);

	  expect(screen.getByRole('heading')).toHaveTextContent('Recipe Finder');
	  expect(screen.getByPlaceholderText('Enter an ingredient to find recipes...'))
	    .toBeInTheDocument();
	  expect(screen.getByRole('button')).toHaveTextContent('Find');
	});

… and implement the changes necessary to make it pass:

	import React from 'react';

	const Recipes = () => {
	  return (
	    <div>
	      <h1>Recipe Finder</h1>
	      <form>
	        <input 
	          type="text" 
	          name="ingredient"
	          placeholder="Enter an ingredient to find recipes..." 
	        />
	        <button type="submit">Find</button>
	      </form>
	    </div>
	  )
	};

	export default Recipes;

This way, we know what we expect from our code and more importantly — we will know if we break any functionality if the previously passing tests suddenly fail.

An important step in TDD is the refactor step, where we improve our code to for example make it easier to read, become more efficient and remove any duplication. The test should still pass after we refactor.

Setting up our mocks

When the application first renders, I want to display a list of all my recipes, just like in the visual design above. This requires some kind of communication with an API. We are going to use Mock Service Worker to mock the HTTP-requests, so that we can control the response data. Install Mock Service Worker with npm like this:

npm install msw --save-dev

With Mock Service Worker, we are not mocking a specific module (unlike if we were to use Jest.mock), which means that it makes no difference if I use fetch or a third-party library such as axios to get the data. This makes it incredibly flexible. Let’s add the following imports to our test file:

import { rest } from 'msw';
import { setupServer } from 'msw/node';

Here is how I set up mocking a call to the recipe list endpoint:

	import React from 'react';
	import { render, screen } from '@testing-library/react';
	import Recipes from './Recipes';
	import { rest } from 'msw';
	import { setupServer } from 'msw/node';

	const allRecipes = [
	  { id: 1, title: 'Burger' }, 
	  { id: 2, title: 'French toast' }, 
	  { id: 3, title: 'Salmon' }
	];

	const server = setupServer(
	  rest.get('/api/recipes', (req, res, ctx) => {
	    return res(ctx.json({ recipes: allRecipes }));
	  })
	);

	beforeAll(() => server.listen());

afterAll(() => server.close());

If you have worked with NodeJS and Express, the syntax looks very familiar. GET requests to ‘/api/recipes’ will respond with JSON containing the allRecipes array, just like a real API would. These two lines make sure the server starts listening (intercepting) before the tests run and closes its connection when the tests in this file have finished running:

beforeAll(() => server.listen());
afterAll(() => server.close());

#tdd #react-testing-library #react #test-driven-development #testing