Skip to content

Modus-Logo-Long-BlackCreated with Sketch.

  • Services
  • Work
  • Blog
  • Resources

    OUR RESOURCES

    Innovation Podcast

    Explore transformative innovation with industry leaders.

    Guides & Playbooks

    Implement leading digital innovation with our strategic guides.

    Practical guide to building an effective AI strategy
  • Who we are

    Our story

    Learn about our values, vision, and commitment to client success.

    Open Source

    Discover how we contribute to and benefit from the global open source ecosystem.

    Careers

    Join our dynamic team and shape the future of digital transformation.

    How we built our unique culture
  • Let's talk
  • EN
  • FR

An Overview of Unit, Integration, and E2E Testing

Published on October 12, 2021
Last Updated on February 27, 2023
Quality Assurance

Manual testing is simply navigating through your product and seeing if it will behave as expected or not. To some extent, this is a good thing to do. It’s always helpful to use your own application.  


NEW RESEARCH: LEARN HOW DECISION-MAKERS ARE PRIORITIZING DIGITAL INITIATIVES IN 2024.

Get Report

However, automated tests speed up your development flow and give you a quick way to identify issues, break changes and side effects.


Types of tests

Why Automate Tests?

  • Get an error if the code is broken (ex: Tests that automatically trigger a new releaseAt on git)
  • Save time
  • Think about possible issues and bugs
  • Integrate into the build workflow
  • Break complex dependencies
  • Improve your code

We’ll explore all the above points in detail in our upcoming posts. 

Different Types of Tests

Unit Tests: Unit tests are the easiest tests to write because you can expect specific results for your input. There are no dependencies or complex interactions.

Integration Tests: Integration tests are more complex than unit tests because you have to deal with dependencies.

End-To-End: End-to-end tests simulate a specific user interaction flow with your app. For example, clicking or entering text.

Complexity and Frequency - testing

From the above figure, we can conclude:

  • Unit tests are the least complex and E2E tests are the most complicated.
  • We tend to write more tests with less complexity. It’s preferable to write more unit tests than E2E tests.
  • The integration tests fit in the middle. This implies that we write them more frequently than the E2E tests but less than unit tests.

Now let’s dive deep into each of the three tests.

Unit Tests

Consider this function which we use in our app. It will take name and age as inputs and return a greeting with these two parameters.

const greeting = (firstName, lastName) => {
 return `Hello Mr./Mrs. ${firstName} ${lastName}`
}

A valuable unit test could be:

test('should output firstName and lastName', () => {
 const text = greeting('Moataz', 'Mahmoud')
 expect(text).toBe('Hello Mr./Mrs. Moataz Mahmoud')
})

This test will check whether the greeting function returns the expected text. If we now change the greeting function, let’s say like this:

const greeting = (firstName, lastName) => {
 return `Hello Mr./Mrs. ${firstName} ${firstName}`
}

Then our test will fail since it will return Hello Mr./Mrs. Moataz Moataz instead of Hello Mr./Mrs. Moataz Mahmoud.

expect and toBe are predefined keywords in chai assertion library and will be addressed in detail in future posts. So don’t worry about them for now.

From a software engineering perspective, it’s better to put your app into as small units as possible so that you can write unit tests for them. And that’s why we said at the beginning of this article that unit tests are the most frequent tests in a project. 

Integration Tests

Integration tests possess the next level of complexity in the testing pyramid. They are more complex than the unit tests because you need to handle code block dependency. You are testing how a code snippet (the method most of the time) depends on another method to run and pass some value to it.

Because of this high dependency between the tests, we highly recommend covering all the single components in the unit testing’s scope. 

Since you are covering every single unit independently, you may think that your system is working perfectly. But that’s seldom the case. Two components working independently doesn’t mean that they will work together as expected.

Here is an example:

exports.checkBeforeGreeting = (firstName, lastName) => {
  if (validateInput(firstName, false) || validateInput(lastName, false)) {
    return false
  }
  return greetThem(firstName, lastName)
}

An integration test can be like this:

test('should generate a valid text output', () => {
  const text = checkBeforeGreeting('Moataz', 'Mahmoud')
  expect(text).toBe('Hello Mr./Mrs. Moataz Mahmoud')
})

You can see that there is no special syntax to do it. It’s the same syntax used in unit tests. It’s just called integration since it’s testing code that depends on another. In this case, it’s greetThem depending on validateInput. And checkBeforeGreeting is called a wrapper method.

At first look, you may think that this will only fail when either validateInput or greetThem has a problem – which would be detected by a unit test. So, why should we test the checkBeforeGreeting function?

Here is the answer: Suppose that the condition in the checkBeforeGreeting got misimplemented:

if (validateInput(firstName, false) && validateInput(lastName, false))

That will now break the logic of this function as we now incorrectly handle the result of validateInput. So even though validateInput or greetThem are not broken, checkBeforeGreeting would still yield an invalid result.

That’s why you need integration tests!

End to End Tests

End-to-end tests simulate a specific user interaction flow with your app, including UI or API tests.

The UI tests (clicking, entering text, etc.) run in the browser, but they’ll not load your app. They just need a JavaScript environment (i.e., an empty browser window that’s loaded behind the scenes).

However, for end-to-end/ UI testing, we need a browser that loads our app. And we need to be able to control that browser via code (so that we can program certain user interactions and simulate them).

The syntax of an end-to-end test depends highly on the used framework. Here is a kind of pseudocode for one:

test('should create an element with text and correct class', () => {
  const page = new Page()
  page.visit(baseURL)
  page.click('input#firstName')
  page.type('input#firstName', 'Moataz')
  page.click('input#lastName')
  page.type('input#lastName', 'Mahmoud')
  page.click('#btnGreetUser')
  expect('.greeting-user').toBe('Hello Mr./Mrs. Moataz Mahmoud')
}, 10000)

It’s all about telling the browser what to do -by traversing the DOM- and expecting that the exit criteria of the test scenario are succeeding.

API tests don’t need a browser. For example, an API end-to-end scenario can be simply like this:

test('should create a user and then greet him/her',
  async (Moataz, Mahmoud) => {
    const user = request('/createUser', 'POST', 'Authentication',
      { firstName, lastName })
    const greetingMessage = request('/greetThem', 'POST', user)
    expect(greetingMessage).toBe('Hello Mr./Mrs. Moataz Mahmoud')
}, 10000)

You can see that these tests are not too challenging (neither API nor UI). However, they can be a little tricky to debug if broken. 

There is one tool to do both the UI and API tests — Cypress. We’ll be sharing in-depth Cypress tutorials in the coming weeks. Subscribe to our blog if you’d like to get notified. 

This post was published under the Quality Assurance Community of Experts. Communities of Experts are specialized groups at Modus that consolidate knowledge, document standards, reduce delivery times for clients, and open up growth opportunities for team members. Learn more about the Modus Community of Experts here. 

Ensure your software is secure, reliable, and ready to scale—explore our expert testing and QA services today!

Posted in Quality Assurance
Share this

Moataz Mahmoud

Moataz is a Senior Test Automation Engineer at Modus Create with over seven years of experience in automation. He specializes in writing tests in JavaScript (TypeScript) and writing unit tests in pytest for python backend projects.

Related Posts

  • Here's Why You Should Write Unit Tests
    Here's Why You Should Write Unit Tests

    Software engineers have been testing ever since they could write code. However, the ability to…

  • Unit Testing w/ AngularJS
    Unit Testing w/ AngularJS

    Oh how far we've come in the web development world. Cross-browser support and performance, once…

Want more insights to fuel your innovation efforts?

Sign up to receive our monthly newsletter and exclusive content about digital transformation and product development.

What we do

Our services
AI and data
Product development
Design and UX
IT modernization
Platform and MLOps
Developer experience
Security

Our partners
Atlassian
AWS
GitHub
Other partners

Who we are

Our story
Careers
Open source

Our work

Our case studies

Our resources

Blog
Innovation podcast
Guides & playbooks

Connect with us

Get monthly insights on AI adoption

© 2025 Modus Create, LLC

Privacy PolicySitemap
Scroll To Top
  • Services
  • Work
  • Blog
  • Resources
    • Innovation Podcast
    • Guides & Playbooks
  • Who we are
    • Our story
    • Careers
  • Let’s talk
  • EN
  • FR