Alexey Kartsev

Alexey Kartsev

1588315320

Test-driven development in React Applications

In this post, we will talk about the thinking behind the testing driven development and how to apply this knowledge to simple functions, web accessibility, and React components, mostly with Jest and React Testing Library.

Automated tests are a big part of software development. It gives us, developers, confidence to ship code to be there, but we increase the confidence that the software will be up and running and working appropriately.

I began my software career in the Ruby community writing tests from the first day I learned the language. The Ruby (and Rails) community was always strong in the testing automation area. It helped shape my mindset on how to write good software.

So using Ruby and Rails, I did a lot of backend stuff like background jobs, data structure modeling, API building, and so on. In this scope, the user is always one: the developer user. If building an API, the user would be the developer that’s consuming the API. If building the models, the user would be the developer that will use this model.

Now doing a lof of frontend stuff too, after 1 intense year of building PWAs using mostly React and Redux, at first some thoughts came to my mind:

  • TDD is impossible when building UI stuff. How do I know if it is a div or span?
  • Testing can be “complex”. Should I shallow or should I mount? Test everything? Ensure every div should be the right place?

So I started re-thinking about these testing practices and how to make it productive.

TDD is possible. If I’m wondering if I should expect a div or a span, I’m probably testing the wrong thing. Remember: tests should give us the confidence to ship, not necessarily to cover every bit or implementation details. We will dive into this topic later!

I want to build tests that:

  • Ensure the software works appropriately
  • Give the confidence to ship code to production
  • Make us think about software design

And tests that make software:

  • Easy to maintain
  • Easy to refactor

Testing Driven Development

TDD shouldn’t be complex. It is just a process of 3 steps:

  • Make a test
  • Make it run
  • Make it right

We start writing a simple test to cover how we expect the software works. Then we make the first implementation of the code (class, function, script, etc). Now the software is behaving. It works as expected. Time to make it right. Time to make it better.

The goal is a clean code that works. We solve the “that works” problem first and then make the code clean.

It is pretty simple. And it should be. I didn’t say it is easy. But it is simple, straightforward, just 3 steps. Every time you exercise this process of writing tests first, code after, and then refactoring, you feel more confident.

One good technique when writing your tests first is to think about use cases and simulate how it should be used (as a function, component, or used by a real user).

Functions

Let’s apply this TDD thing into simple functions.

Some time ago I was implementing a draft feature for a real estate registration flow. Part of the feature was to show a modal if the user had a not finished real estate. The function we will implement is the one that answers if the user has at least one real estate draft.

So first step: writing the test! Let’s think of the use cases of this function. It always responds a boolean: true or false.

  • Has no unsaved real estate draft: false
  • Has at least one unsaved real estate draft: true

Let’s write the tests that represent this behavior:

describe('hasRealEstateDraft', () => {
  describe('with real estate drafts', () => {
    it('returns true', () => {
      const realEstateDrafts = [
        {
          address: 'São Paulo',
          status: 'UNSAVED'
        }
      ];

      expect(hasRealEstateDraft(realEstateDrafts)).toBeTruthy();
    });
  });

  describe('with not drafts', () => {
    it('returns false', () => {
      expect(hasRealEstateDraft([])).toBeFalsy();
    });
  });
});

We wrote the tests. But when running it, it shows go red: 2 broken tests because we do not have the function implemented yet.

Second step: make it run! In this case, it is pretty simple. We need to receive this array object and return if it has or hasn’t at least one real estate draft.

const hasRealEstateDraft = (realEstateDrafts) => realEstateDrafts.length > 0;

Great! Simple function. Simple tests. We could go to step 3: make it right! But in this case, our function is really simple and we’ve already got it right.

But now we need the function to get the real estate drafts and pass it to the hasRealEstateDraft.

Which use case we can think of?

  • An empty list of real estates
  • Only saved real estates
  • Only unsaved real estates
  • Mixed: save and unsaved real estates

Let’s write the tests to represent it:

describe('getRealEstateDrafts', () => {
  describe('with an empty list', () => {
    it('returns an empty list', () => {
      const realEstates = [];

      expect(getRealEstateDrafts(realEstates)).toMatchObject([]);
    });
  });

  describe('with only unsaved real estates', () => {
    it('returns the drafts', () => {
      const realEstates = [
        {
          address: 'São Paulo',
          status: 'UNSAVED'
        },
        {
          address: 'Tokyo',
          status: 'UNSAVED'
        }
      ];

      expect(getRealEstateDrafts(realEstates)).toMatchObject(realEstates);
    });
  });

  describe('with only saved real estates', () => {
    it('returns an empty list', () => {
      const realEstates = [
        {
          address: 'São Paulo',
          status: 'SAVED'
        },
        {
          address: 'Tokyo',
          status: 'SAVED'
        }
      ];

      expect(getRealEstateDrafts(realEstates)).toMatchObject([]);
    });
  });

  describe('with saved and unsaved real estates', () => {
    it('returns the drafts', () => {
      const realEstates = [
        {
          address: 'São Paulo',
          status: 'SAVED'
        },
        {
          address: 'Tokyo',
          status: 'UNSAVED'
        }
      ];

      expect(getRealEstateDrafts(realEstates)).toMatchObject([{
        address: 'Tokyo',
        status: 'UNSAVED'
      }]);
    });
  });
});

Great! We run the tests. It doesn’t work… yet! Now implement the function.

const getRealEstatesDrafts = (realEstates) => {
  const unsavedRealEstates = realEstates.filter((realEstate) => realEstate.status === 'UNSAVED');
  return unsavedRealEstates;
};

We simply filter by the real estate status and return it. Great, the tests are passing, the bar is green! And the software is behaving, but we can make it better: step 3!

What about extracting the anonymous function within the filter function and make the 'UNSAVED' be represented by an enum?

const STATUS = {
  UNSAVED: 'UNSAVED',
  SAVED: 'SAVED',
};

const byUnsaved = (realEstate) => realEstate.status === STATUS.UNSAVED;

const getRealEstatesDrafts = (realEstates) => realEstates.filter(byUnsaved);

The tests are still passing and we have a better solution.

One thing to have in mind here: I isolated the data source from the logic. What does it mean? We get the data from local storage (data source), but we test only the functions responsible to the logic to get drafts and see if it has at least one draft. The functions with the logic, we ensure that it works and it is clean code.

If we get the localStorage inside our functions, it becomes hard to test. So we separate the responsibility and make the tests easy to write. Pure functions are easier to maintain and simpler to write tests.

React Components

Now let’s talk about React components. Back to the introduction, we talked about writing tests that test implementation details. And now we will see how we can make it better, more sustainable, and have more confidence.

A couple of days ago I was planning to build the new onboarding information for the real estate owner. It is basically a bunch of pages with the same design, but it changes the icon, title, and description of the pages.

I wanted to build just one component: Content and pass the information needed to render the correct icon, title, and description. I would pass businessContext and step as props and it would render the correct content to the onboarding page.

We don’t want to know if we will render a div or paragraph tag. Our test needs to ensure that for a given business context and step, the correct content will be there. So I came with these use cases:

  • The first step of the rental business context
  • Last step of the rental business context
  • The first step of the sales business context
  • Last step of the sales business context

Let’s see the tests:

describe('Content', () => {
  describe('in the rental context', () => {
    const defaultProps = {
      businessContext: BUSINESS_CONTEXT.RENTAL
    };

    it('renders the title and description for the first step', () => {
      const step = 0;
      const { getByText } = render(<Content {...defaultProps} step={step} />);

      expect(getByText('the first step title')).toBeInTheDocument();
      expect(getByText('the first step description')).toBeInTheDocument();
    });

    it('renders the title and description for the forth step', () => {
      const step = 3;
      const { getByText } = render(<Content {...defaultProps} step={step} />);

      expect(getByText('the last step title')).toBeInTheDocument();
      expect(getByText('the last step description')).toBeInTheDocument();
    });
  });

  describe('in the sales context', () => {
    const defaultProps = {
      businessContext: BUSINESS_CONTEXT.SALE
    };

    it('renders the title and description for the first step', () => {
      const step = 0;
      const { getByText } = render(<Content {...defaultProps} step={step} />);

      expect(getByText('the first step title')).toBeInTheDocument();
      expect(getByText('the first step description')).toBeInTheDocument();
    });

    it('renders the title and description for the last step', () => {
      const step = 6;
      const { getByText } = render(<Content {...defaultProps} step={step} />);

      expect(getByText('the last step title')).toBeInTheDocument();
      expect(getByText('the last step description')).toBeInTheDocument();
    });
  });
});

We have one describe block for each business context and an it block for each step. I also created an accessibility test to ensure the component we are building is accessible.

it('has not accessibility violations', async () => {
  const props = {
    businessContext: BUSINESS_CONTEXT.SALE,
    step: 0,
  };

  const { container } = render(<Content {...props} />);
  const results = await axe(container);

  expect(results).toHaveNoViolations();
});

Now we need to make it run! Basically, the UI part of this component is just the icon, the title, and the description. Something like:

<Fragment>
  <Icon />
  <h1>{title}</h1>
  <p>{description}</p>
</Fragment>

We just need to build the logic to get all these correct data. As I have the businessContext and the step in this component, I wanted to just do something like

content[businessContext][step]

And it gets the correct content. So I built a data structure to work that way.

const onboardingStepsContent = {
  alugar: {
    0: {
      Icon: Home,
      title: 'first step title',
      description: 'first step description',
    },
    // ...
  },
  vender: {
    0: {
      Icon: Home,
      title: 'first step title',
      description: 'first step description',
    },
    // ...
  },
};

It’s just an object with the first keys as the business context data and for each business context, it has keys that represent each step of the onboarding. And our component would be:

const Content = ({ businessContext, step }) => {
  const onboardingStepsContent = {
    alugar: {
      0: {
        Icon: Home,
        title: 'first step title',
        description: 'first step description',
      },
      // ...
    },
    vender: {
      0: {
        Icon: Home,
        title: 'first step title',
        description: 'first step description',
      },
      // ...
    },
  };

  const { Icon, title, description } = onboardingStepsContent[businessContext][step];

  return (
    <Fragment>
      <Icon />
      <h1>{title}</h1>
      <p>{description}</p>
    </Fragment>
  );
};

It works! Now let’s make it better. I wanted to make the get content more resilient. What if it receives a step that doesn’t exist for example? These are the use cases:

  • The first step of the rental business context
  • Last step of the rental business context
  • The first step of the sales business context
  • Last step of the sales business context
  • Inexistent step of the rental business context
  • Inexistent step of the sales business context

Let’s see the tests:

describe('getOnboardingStepContent', () => {
  describe('when it receives existent businessContext and step', () => {
    it('returns the correct content for the step in "alugar" businessContext', () => {
      const businessContext = 'alugar';
      const step = 0;

      expect(getOnboardingStepContent({ businessContext, step })).toMatchObject({
        Icon: Home,
        title: 'first step title',
        description: 'first step description',
      });
    });

    it('returns the correct content for the step in "vender" businessContext', () => {
      const businessContext = 'vender';
      const step = 5;

      expect(getOnboardingStepContent({ businessContext, step })).toMatchObject({
        Icon: ContractSign,
        title: 'last step title',
        description: 'last step description',
      });
    });
  });

  describe('when it receives inexistent step for a given businessContext', () => {
    it('returns the first step of "alugar" businessContext', () => {
      const businessContext = 'alugar';
      const step = 7;

      expect(getOnboardingStepContent({ businessContext, step })).toMatchObject({
        Icon: Home,
        title: 'first step title',
        description: 'first step description',
      });
    });

    it('returns the first step of "vender" businessContext', () => {
      const businessContext = 'vender';
      const step = 10;

      expect(getOnboardingStepContent({ businessContext, step })).toMatchObject({
        Icon: Home,
        title: 'first step title',
        description: 'first step description',
      });
    });
  });
});

Great! Now let’s build our getOnboardingStepContent function to handle this logic.

const getOnboardingStepContent = ({ businessContext, step }) => {
  const content = onboardingStepsContent[businessContext][step];

  return content
    ? content
    : onboardingStepsContent[businessContext][0];
};

We try to get content. If we have it, just return it. If we don’t have it, return the first step of the onboarding.

Neat! But we can improve it. What about using the || operator? No need to assign to a variable, no need to use a ternary.

const getOnboardingStepContent = ({ businessContext, step }) =>
  onboardingStepsContent[businessContext][step] ||
  onboardingStepsContent[businessContext][0];

If it finds the content, just return it. If it didn’t find, return the first step of the given business context.

Now our component is only UI.

const Content = ({ businessContext, step }) => {
  const {
    Icon,
    title,
    description,
  } = getOnboardingStepContent({ businessContext, step });

  return (
    <Fragment>
      <Icon />
      <h1>{title}</h1>
      <p>{description}</p>
    </Fragment>
  );
};


Final thoughts

I like to think deeply about the tests I’m writing. And I think all developers should too. It does need to give us the confidence to ship more code and have a bigger impact on the market we are working on.

Like all code, when we write smelly and bad tests, it influences other developers to follow the “pattern”. It gets worse in bigger companies. It scales badly. But we are always able to stop, reflect on the status quo, and take action to make it better.

I shared some resources I found interesting reading and learning. If you want to get a great introduction to TDD, I really recommend TDD by example, a book from Kent Beck.

#reactjs #testing #web-development #javascript

What is GEEK

Buddha Community

Test-driven development in React Applications
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

Juned Ghanchi

1621573085

React Native App Developers India, React Native App Development Company

Expand your user base by using react-native apps developed by our expert team for various platforms like Android, Android TV, iOS, macOS, tvOS, the Web, Windows, and UWP.

We help businesses to scale up the process and achieve greater performance by providing the best react native app development services. Our skilled and experienced team’s apps have delivered all the expected results for our clients across the world.

To achieve growth for your business, hire react native app developers in India. You can count on us for all the technical services and support.

#react native app development company india #react native app developers india #hire react native developers india #react native app development company #react native app developers #hire react native developers

sophia tondon

sophia tondon

1621250665

Top React JS Development Company | React JS Development Services

Looking to hire dedicated top Reactjs developers at affordable prices? Our 5+ years of average experienced Reactjs developers comprise proficiency in delivering the most complex and challenging web apps.

Hire ReactJS developers online on a monthly, hourly, or full-time basis who are highly skilled & efficient in implementing new technologies and turn into business-driven applications while saving your cost up to 60%.

Planning to** outsource React web Development services from India** using Reactjs? Or would you like to hire a team of Reactjs developers? Get in touch for a free quote!

#hire react js developer #react.js developer #react.js developers #hire reactjs development company #react js development india #react js developer

Aria Barnes

Aria Barnes

1627031571

React 18: Things You Need To Know About React JS Latest Version

The most awaited version of React 18 is finally out now. Its team has finally revealed the alpha version of React 18 and its plan, though the official launch is still pending. This time the team has tried something and released the plan first to know their user feedback because the last version of React 17 was not that much appreciated among developers.

According to Front-end Frameworks SurveyReact JS has ranked top in the list of most loved frameworks. Thus, the developer communities expect a bit higher from the framework, so they are less appreciative of the previous launch.
ReactJS stats.pngSo, this time React 18 will be a blast. For beginners, the team is working on a new approach. They have called a panel of experts, library authors, educators, and developers to take part in a working group. Initially, it will be a small group.

I am not a part of this release but following the team on their GitHub discussion group. After gathering the information from there, I can say that they have planned much better this time.

React 17 was not able to meet the developer's community. The focus was all primarily centered on making it easier to upgrade React itself. React 18 release will be the opposite. It has a lot of features for react developers.

Read more here: React 18: Things You Need To Know About React JS Latest Version

#hire react js developers #hire react js developers india #react developers india #react js developer #react developer #hire react developers

sophia tondon

sophia tondon

1625803880

Top React JS Development Company | React JS Development Services

Looking to hire top Reactjs developers at affordable prices? Our 5+ years of average experienced Reactjs developers comprise proficiency in delivering the most complex and challenging web apps.

Hire ReactJS developers online on a monthly, hourly, or full-time basis who are highly skilled & efficient in implementing new technologies and turn into business-driven applications while saving your cost up to 60%.

Planning to outsource React Js web Development services using Reactjs? Or would you like to hire a team of Reactjs developers? Get in touch for a free quote!

#hire react js developers #hire reactjs developers #hire react js developer #hire react.js developer #hire react.js developers #hire react developer