THE FALSE COMFORT OF MISCONCEPTIONS

Hi, there! Got curious about the title, did you? Let’s see if I can clarify it a bit. 

In this article, I will try to use my own experience as part of the LGBTQIAPN+ community (maaan, is it me or does this acronym keep on getting bigger?!) and my not-so-clever humor, to let you in on a little secret. Hiding who we truly are behind what is apparently a more comfortable experience of life and avoiding the conflict is, besides a tremendous lack of respect towards ourselves, a way to perpetuate prejudice. Don’t know if you are aware of that, but I surely wasn’t for quite some time… 

While this is my own process it can be a common experience. This is meant to be an honest sharing, hopefully, useful not just for the mentioned community but for everyone who reads it since this is about self-awareness and self-knowledge. 

Filipa Pestana – Product Designer

Where to start 

When you are born, you are this immense whiteboard, full of possibilities. Eventually, you will start getting some drafts and paints, reflecting your context and the mainstream society you belong to, as expected. As a child, one of my most vivid memories is the passion and enthusiasm I felt watching Disney movies. It was so delightful how every story ended up just right, as long as the princess found her prince charming. For a major part of my existence up until that point, this was all I needed to believe in: as long as I’m the princess, my one goal should be to find my prince and live happily ever after. 

As I grew older, my purpose became more and more obvious. Led by the hand of the family’s matriarchy, I learned that to attract the utopic prince I would need to know how to sew, how to cook, and how to iron his precious suit. It made sense, I mean, the guy is out there fighting dragons, and riding horses… his shirt and pants are doomed to failure unless his graceful princess takes care of it. And of course, me being the obedient little angel a good girl should be, I accepted my fate without questioning it and topped it off with the joyful ignorance only children can endure – lucky little bastards. 

I made it my life’s goal to be a nice, sweet, obedient princess so that I could win over the heart of my perfect prince. Seemed easy enough. But somehow, no matter how hard I tried to fulfill others and my expectations at that time, something just seemed off. 

The thing is, the more you grow and allow yourself to open your mind to live outside your little dome, the more you start to be aware of other realities and possibilities. During college, my once-perfect board, carefully drawn and painted inside the lines, could now gain the most vibrant colors and splashes of sparkles! This led me to question what before seemed so obvious and trustworthy since this was coming from my first leaders in life – my family members (and, let’s face it, Disney). But more important, at this time, instead of only pointing the finger at others’ beliefs – that I had so willingly taken as my own – I started questioning myself and my existence. I wasn’t sure about how I felt about boys and if they were actually all that charming. I was very confused about what gender identity and sexual preferences translated to (which led to a drastic haircut and some questionable fashion choices – but let’s not elaborate on that. All proof has been deleted, in case you are wondering). Above all, I was very lost in the whole concept of being myself and what that meant… Of course, in such a tormented moment I turned to those I trusted the most my whole life. They for sure would be able to help me untie this knot in my soul. But their own experience of the world – and this topic in particular – only allowed them to do the best they knew. To fill me up with a fear of the world and the beliefs of others, in such a matter that I and my aching soul were not able to dig ourselves out of that whole of constant self-judgment and inadequacy for a long time. 

The art of misconception 

Knowing that soon enough I would have to face the professional world, full of people and their own beliefs, ideologies, and questionable truths, there was only one possible thing to do: practice the art of misconception. A vague answer here, an inconclusive act there, and I would easily dodge the bullet of actually being myself. I was quite feminine and bubbly, I would indulge in some innocent flirting, or make some very heterosexual comment (to note: comments don’t actually have sexual preferences, this is only for dramatic effect, ok?) and for some time I kept up the facade. 

In all the offices I set foot in I was what people would expect from a heterosexual woman and I obliged with all the stereotypes that even I believed to have to live by. I would feel this inexplicable need to look like my feminine peers around the office: hair and makeup on the flick, excruciating noticeable heels, uncomfortably trendy clothes,… Going deeper into any personal conversations my official statement was that I had a boyfriend, we were happy and that should suffice when it comes to you knowing anything about my romantic life. 

The suffocating need to meet these imagined expectations got me so blinded, that in the presence of my male superiors, I would shoot myself in the foot and gladly shut up in the light of any arguments they would so eloquently spread upon my far less superior mind. Unknowingly I kept perpetuating the objectification of womanhood and spreading the gender norm misinformation. Not that my male colleagues – and some female colleagues, to be honest – needed any help with that.  

Being oneself would simply have to wait, given the urgency I felt to fit in all these little boxes – the ones where everyone fits others to make sense of the world. It took me a while before I felt like a character at my workplace and faced the fact that I was extremely unhappy living that way. After all, as I would discover later in life, all these boxes are nothing but invented concepts from the still-evolving minds of one’s species. But I would eventually find out there is only so much you can take when you are tricking yourself.  

In this game of misconception, it’s relevant to point out the importance of the companion you have on your side, if you have one. Yes, I’m talking about that “other half”. I wanted to be loved, just like anyone else, independent of gender or sexual preferences. Let me tell you that when your significant other is quite comfortable living in this facade, deliberately avoiding any complications derived from actually living a truthful life, and mastering the art of misconception, it becomes easier for you to keep feeding the fear and depression inside you. And yes, you got it right, that’s a red flag. But can one really identify a big-flashy-pompous red flag when one is simply trying to survive their misplaced fears and find love? 

Unpleasant (but necessary) awareness 

When I joined MB.io, I was still deeply buried in confusion. Thick darkness would fall over me after a long day of pretending. I would look in the mirror, face washed out, drowned in my tears, completely oblivious to the cause of my pain. Living a double life can be quite exhausting. You need to carefully curate the content of the life you want to project on all fronts. You need to prepare your speech when inquired about your “other half” during lunchtime gossip – because God forbid you could suffice for yourself. And there’s, of course, the continuous perfecting of the always popular excuse for distant relatives that had known no boyfriend of yours (tip: always point the finger at your career and long working hours. That’s always a big hit).  

Somehow, something started to feel different around this new group of people called MB.ioneers. These were people led not by their irreproachable truths, nor fed by their insecurities, but they thrived in this unconventionally heterogeneous environment, full of potential and risks. They actively looked for failure so they could iterate, try again, and evolve in the process. And they did it all with welcoming open arms to difference – in points of view, in experiences, in nationalities, in religions… – disarming any trail of conformed thinking. I wanted so badly to be a part of this new plot and leave my character in the past where it belonged.

 

The immense power of choice 

The good news is that you can break this cycle at any time, because none of it depends on your lover, on the overly male chauvinist company you work for, or the family that raised you. 

Facing the risk of sounding too much like one of those self-help books on the shelf of a convenience store, your well-being and psychological safety depend on you. You have more control over your own life than you care to imagine. Of course, it would be great to have the advantage of being super well received as you are, wherever you go. Of course, it would be amazing to not have to deal with homophobic comments because everyone is engaging in the motto “live and let live”. It can’t hurt to have the right work environment or the support of your loved ones. Sometimes even professional help is needed (therapy is not just for crazy people in case you didn’t know).  

YOU are the one controlling your actions when that great-great-great-aunt asks you for the millionth time about that non-existent boyfriend of yours. You are in control of your narrative when a colleague makes a gay joke – which is an insult in a very cheap disguise, by the way. You are in charge of being your own person whoever you choose to be. Remember yourself and others that you are more than your gender, your age, your religion, or your sexual preferences. You are all that and so much more that makes you unique. That is so worth fighting for, even when you need to fight against your own discouraging beliefs. You can’t control the actions and minds of others, but you can choose to act differently when facing injustice and prejudice. 

It will hurt, it will be hard to manage and at times can have bigger consequences than what you anticipated, but that’s also part of the change. In you, and others. 

Picture by James A. Molnar

This article has been written by Filipa Pestana

Unleashing Potential – The Upside of being a Working-Student At Mercedes-Benz.io

Moritz Bleckert – Communications Intern

Only a select few students in Berlin will ever have the comfort of limiting their daily life to their degree only. As a student juggling the demands of academia and simply growing up, I embarked on my journey as a working student at Mercedes-Benz.io. I had the great pleasure of studying Media- and Business Psychology, being fully aware of the Damocles-Sword* hanging over my head at all times. At least that’s what I thought. Knowing about stress hormones (adrenaline & cortisol f.e.**) and the somatic damage they can induce, sadly doesn’t protect you from them. Being a psychologist has the interesting side effect of being aware of your own mental health, while also assuming that you would be less affected by it than others (Third-Person Effect***). Consequently, choosing the right place to work was incredibly important to me.

A Well-Rounded Experience

The notion of work-life balance often seems unattainable for many students, at least in my personal bubble. However, Mercedes-Benz.io provided me with the tools and support to strike a harmonious equilibrium between my studies and work commitments. Emphasizing the importance of effective time management early on, enabled me to successfully navigate a demanding schedule of 20 hours of work alongside 25 hours of university commitments each week. The flexibility in scheduling allowed me to accommodate last-minute changes in my academic timetable without any complications. This level of adaptability proved to be especially valuable during exam periods, as I could just adjust my workload accordingly or during semester breaks, where I could upgrade to 40 hours of work each week.

Exploring Different Roles and Skills

One aspect that truly stood out during my stay at Mercedes-Benz.io was the diverse range of responsibilities I was entrusted with. As part of a product team, I was enabled to change between CX and Comms topics easily. Covering experience in support, automatization and data management as well as marketing conception. Realization of marketing measures included production, copywriting, analysis, and campaign management – just to name a few. The multifaceted nature of my assignments nurtured my growth as a professional and allowed me to develop a versatile skill set that spans multiple domains. Psychology is inherently something, that isn’t palpable. Having the opportunity to work with data and statistics gave me a lovely transition from theory to praxis.

A Culture of Learning and Flourishing

Mercedes-Benz.io seems to have understood something, many companies advocate for, but don’t actually act on: An environment, where personal growth and professional development are paramount is an environment, where talent tends to reside. The culture thrives on the active participation of its employees in events, initiatives, and activities. By immersing myself in this vibrant community, I not only expanded my network but also gained invaluable insights from colleagues with diverse backgrounds and perspectives. Collaborating with individuals from various roles and departments broadened my understanding of the business landscape and taught me the importance of effective teamwork and communication.

Conclusion

Being a working student at Mercedes-Benz.io was a transformative experience that allowed me to balance the demands of my studies while gaining practical, hands-on experience in a dynamic organization. The company’s emphasis on work-life balance, the diverse range of responsibilities, and the culture of learning and growth created an environment that nurtured my personal and professional development. If you’re seeking a place where you can not only learn and flourish but also make a lasting impact, I encourage you to explore the opportunities available here.

* An allusion to the imminent and ever-present peril
** American Psychological Association Definition of Stress Hormones
*** American Psychological Association Definition of Third Person Effect

ASKING MB.IONEERS

We sat down with people who are or used to be interns and working students at Mercedes-Benz.io to ask them for advice to professionals who just started their career.

WHAT ADVICE WOULD YOU GIVE PROFESSIONALS WHO JUST STARTED AT MERCEDES-BENZ.IO?

“Never be afraid to speak up. It does not matter if you’re an intern or the CEO, your opinion and your ideas are valuable. This does not mean you should always barge in with the first thing that comes to your mind but rather that everyone will appreciate a well-thought-through comment that adds value to a discussion.”

Dennis Bauer – Strategy Business Consultant

“The best advice I can give revolves around two keywords: Curiosity and Creativity. Maintain an unwavering sense of curiosity and ask about what you don’t know yet. That will save you so much time! Also, cultivate your creative flow. I find that there are a lot of opportunities to embrace as we have the space for new visions to be shared and validated. So, go for it!”

Vera Pereira – Communications Intern

“Don’t be afraid to talk with people and make new friends. I always have lunch with my colleagues and stay for a “Feierabendbier” to hear the best stories and get the real insights.”

Valentin Knapp – Digital Practice Intern

Unsplash picture by Annie Spratt

31 DAYS OF EMBRACING EQUITY 

In March, we celebrated International Women’s Day and we used the opportunity to raise awareness and bring its importance to Mercedes-Benz.io. Because the truth is, only with collective action and shared ownership can we drive gender parity and make International Women’s Day impactful.


So, during March, we explored different topics related to gender equality. We’re glad that our MB.ioneers joined us on this journey by reading, sharing their thoughts, and continuing this important conversation.

Women.io Panel Talk

To break down gender barriers and to create and support a tribe of empowered women, we established Women.io – a group of MB.ioneers who organize different initiatives to support this goal. One of these initiatives is a quarterly Panel where we invite inspiring speakers to talk about their journey and focus on areas like empowerment, equality, and diversification.

On March 14th, we welcomed Paula Bellizia, Partner and President of Global Payments at EBANX. She talked about her impressive career journey at companies like Google, Microsoft and Apple and gave us insight into topics such as leadership, diversity, inclusion, and digital transformation. Not only was it a blissful conversation, but also a beautiful reunion with our CEO Silvia Bechmann as both used to work together at Whirlpool Corporation 30 years ago.

CONVERSATION STARTERS

The first action we can ALL take is to educate ourselves and learn more about Equality & Equity. From TED talks, YouTube videos to reports –  we provided educational context for our MB.ioneers to think, reflect and talk to each other. 

A global history of women’s rights, in 3 minutes | UN Women

How much progress have we achieved in the global struggle for equal rights, and how much work remains? From worldwide suffrage campaigns to the rise of #MeToo and digital activism, we have marched slowly forward. But today, the fight for gender equality is far from over. Discover the noteworthy women, grassroots movements and historic milestones that have changed the world for women and girls. 

Women’s History Month | Udemy

Since March marks not only Women’s History Month but hosts International Women’s Day, we brought our MB.ioneers a learning path dedicated to exploring more about how to navigate the workplace as a woman and how to learn more about allyship and ways of supporting your fellow women colleagues.

5 simple steps to becoming an Ally

Being an Ally means being aware of our advantaged status and using it to actively support and include people in less leveraged positions. When it comes to the workplace, an Ally builds partnerships that raise awareness, offer to mentor, and advocate. Shared connections teach about the needs that exist within a group and allies share goals and give access to the resources needed to reach them. Such experiences are the first steps to becoming a trusted ally. Here are some tips to become one: 

1) Listen more than you speak: An effective ally takes notice and listens, seeking out opportunities to learn.

2) Mentor others: Your place of privilege doesn’t mean you didn’t work hard to get there, but it does mean you have the power to coach others. 

3) Share the spotlight: Look around your next meeting or conference and survey who’s not there. Share your access and opportunities.

4) Recognize – and correct – exclusion.

5) Be strategic in your advocacy: Just like you can’t donate to every worthy cause, an ally can’t effectively champion every underrepresented group. 

Women in the Workplace Report 2022 | McKinsey & Company 

Women in the Workplace is the largest study on the state of women in corporate America. In 2015, LeanIn.Org and McKinsey & Company launched the study to give companies insights and tools to advance gender diversity in the workplace. 

The 2022 report focuses on how the pandemic has changed what women want from their companies, including the growing importance of opportunity, flexibility, employee well-being, diversity, equity, and inclusion.

Click HERE to read about the study

Power on: How we can supercharge an equitable digital future | UN WOMEN

Women and girls remain underrepresented across the creation, use, and regulation of technology. They are less likely to use digital services or enter tech-related careers, and significantly more likely to face online harassment and violence. The choices we make today will profoundly impact the path forward. HERE are four steps we can take in the right direction.  

“A seat at the table” isn’t the solution for gender equity

Women and girls are conditioned to believe success is “a seat at the table.” Creator, actress and author Lilly Singh thinks we need to build a better table. In this hilarious, incisive talk, Singh traces the arc of her career from up-and-coming YouTuber to history-making late-night talk show host, offering four ways to build a more inclusive society where girls are encouraged and empowered to do great things. Watch her talk HERE.

How diversity makes teams more innovative

Are diverse companies really more innovative? Rocío Lorenzo and her team surveyed 171 companies to find out – and the answer was a clear yes. In a talk that will help you build a better, more robust company, Lorenzo dives into the data and explains how your company can start producing fresher, more creative ideas by treating diversity as a competitive advantage. Watch her talk HERE.

How to design gender bias out of your workplace

Equity expert Sara Sanford offers a certified playbook that helps companies go beyond good intentions, using a data-driven standard to actively counter unconscious bias and foster gender equity – by changing how workplaces operate, not just how people think. Watch her talk HERE.

Know more about our company or MB.ioneer by following us on LinkedInYouTube, and Instagram!

API Testing with Java and Spring Boot Test – Part 2: Improving the solution

In the last part of this step-by-step, we created the project, set up the test framework, and also did all the configurations needed to run our API tests.

You can see the first part of the series here:

Let’s continue to grow our test framework, but first, we need to do some improvements to the existing code. In this guide, we’ll:

  • Refactor the object mapping (to be easier to handle with the JSON files)
  • Improve the response validations
  • Handle multiple environments inside our tests.

These changes will make our code base cleaner and easier to maintain for us to create a scalable framework of API tests.

Let’s do it.

Refactoring the Object mapping

We’ll take advantage of using the Spring boot Repository to separate the responsibility of mapping the objects (JSON) we’re going to use inside our tests. That way, we can do another step forward in our code cleanup.

So, first of all, we’re going to:

  • Create a new package called repositories
  • Then we create a new Class inside this package called FileUtils.

We’ll also take the opportunity to change the way we map the object to not be hard-coded but be in a proper resource file. That way when we need to change the test data, we don’t have to change the test but only the correspondent resource file.

package org.example.repositories;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Repository;

import java.io.IOException;
import java.net.URL;

@Repository
public class FileUtils {

    /**
     * Read File and return JsonNode
     *
     * @param filePath
     * @return
     * @throws IOException
     */
    public static JsonNode readJsonFromFile(String filePath) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        URL res = FileUtils.class.getClassLoader().getResource(filePath);
        if (res == null) {
            throw new IllegalArgumentException(String.format("File not found! - %s", filePath));
        }
        return mapper.readTree(res);
    }
}

Show code in Github Gist

As you can see in the file above, we created a function to read a JSON file and then return the object already mapped – similar to the approach we had before in the test file.

Now, we’ll structure the resources folder to accommodate the JSON files.

In the resources folder, let’s create a new directory called user and then create a file to store the request body of the operation we’ll do.

{
  "name": "Luiz Eduardo",
  "job": "Senior QA Engineer"
}

Show code in Github Gist

After that, we need to update our test. Now we want to get the file data by using the new function we created for that purpose. The updated test will look like that:

package api.test.java.tests;

import com.fasterxml.jackson.databind.JsonNode;
import io.restassured.response.Response;
import org.example.repositories.FileUtils;
import org.example.services.YourApiService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import java.io.IOException;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;

@ExtendWith(SpringExtension.class)
@SpringBootTest
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class ApiTest {

    private final YourApiService yourApiService;

    public ApiTest(YourApiService yourApiService) {

        this.yourApiService = yourApiService;
    }

    @Test
    public void testCreateUser() throws IOException {

        JsonNode requestBody = FileUtils.readJsonFromFile("user/createUser.json");

        Response res = yourApiService.postRequest("/users", requestBody);
        assertThat(res.statusCode(), is(equalTo(201)));
    }
}

Show code in Github Gist

Much better! By keeping the code cleaner we are helping our future selves with its maintenance – trust me, you’ll be very glad to see this.

Improving the response validation

Great! Now, let’s have a look at the response validation.

In some cases, we want to check the full response body – or at least some parts of it – to fulfill the test requirements.

To do that, we’ll create:

  • A new Repository to abstract the responsibility and help us check the full JSON response body
  • A function to handle the check of the JSON response.

We’ll also add the “jsonassert” dependency to assert the JSON.

The pom.xml file will look like that:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.6</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>api.test.java</groupId>
    <artifactId>apitest</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>api-test-java</name>
    <description>Api Tests</description>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-text</artifactId>
            <version>1.10.0</version>
        </dependency>

        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured</artifactId>
            <version>5.3.0</version>
            <exclusions><!-- https://www.baeldung.com/maven-version-collision -->
                <exclusion>
                    <groupId>org.apache.groovy</groupId>
                    <artifactId>groovy</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.apache.groovy</groupId>
                    <artifactId>groovy-xml</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>json-schema-validator</artifactId>
            <version>5.3.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.skyscreamer</groupId>
            <artifactId>jsonassert</artifactId>
            <version>1.5.1</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.9.1</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-params</artifactId>
            <version>5.9.1</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.9.1</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Show code in Github Gist

The newly created ResponseUtils class will be something like this:

package org.example.repositories;

import com.fasterxml.jackson.databind.JsonNode;
import io.restassured.response.Response;
import org.json.JSONException;
import org.skyscreamer.jsonassert.JSONAssert;
import org.skyscreamer.jsonassert.JSONCompareMode;
import org.springframework.stereotype.Repository;

@Repository
public class ResponseUtils {

    public static void assertJson(String actualJson, String expectedJson, JSONCompareMode mode) throws JSONException {
        JSONAssert.assertEquals(expectedJson, actualJson, mode);
    }

    public static void assertJson(Response response, JsonNode expectedJson) throws JSONException {
        assertJson(response.getBody().asString(), expectedJson.toString(), JSONCompareMode.LENIENT);
    }
}

Show code in Github Gist

The next step should be to use this new function and improve our test. To do that, we’ll configure a GET request on YourApiService and return the full Response object. Then we should be able to check the response body.

public Response getRequest(String endpoint) {

    return RestAssured.given(spec)
        .contentType(ContentType.JSON)
    .when()
        .get(endpoint);
}

Now, it’s just a matter of adding the test case to the ApiTest test class and using the same strategy of letting the JSON response file be in its proper directory. Finally, we’ll have something like this:

@Test
public void testGetUser() throws IOException, JSONException {

    Response res = yourApiService.getRequest("/users/2");

    JsonNode expectedResponse = FileUtils.readJsonFromFile("responses/user/specific.json");
    
    assertThat(res.statusCode(), is(equalTo(200)));
    ResponseUtils.assertJson(res, expectedResponse);
}

Quite easy to understand if you just look at the test case 🙂

Executing the tests over multiple environments

Now we have the tests properly set, and everything is in the right place. One thing that could be in your mind right now is: “Ok, but I have a scenario in my product, in which I need to run my test suit over multiple environments. How do I do that?”.

And the answer is – property files.

The property files are used to store specific data which we can use along our test suit, like the application host, port, and path to the API. You can also store environment variables to use within your test framework. However, be careful, since we don’t want to make this information public. You can see an example in the lines below.

With Spring boot, we take advantage of using the “profiles” to set the specifics of the environments our application has, and make them available as spring boot profiles.

So, let’s do that. Inside the resources folder, we’ll create a new file called application-prod.properties to store the values of the production cluster of the test application. The file will store something like this:

apitest.base.uri=https://reqres.in
apitest.base.path=/api
apitest.token=${TOKEN}

Now, the only thing missing is to change our service to get the values stored in the property file.

To get the values from the property files, we’ll use the annotation @Value. This annotation will provide the values from the properties we set in the application-prod.properties file.

**Bear in mind: ** You’ll need to set the environment variable before using it here. The @Value annotation will grab this value from the environment variables you have set.

The updated version of YourApiService class will look like this:

package org.example.services;

import com.fasterxml.jackson.databind.JsonNode;
import io.restassured.RestAssured;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.http.ContentType;
import io.restassured.response.Response;
import io.restassured.specification.RequestSpecification;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;

@Slf4j
@Service
public class YourApiService {

    @Value("${apitest.base.uri}")
    private String baseURI;

    @Value("${apitest.base.path}")
    private String basePath;

    @Value("${apitest.token}")
    private String myToken;

    private RequestSpecification spec;

    @PostConstruct
    protected void init() {

        RestAssured.useRelaxedHTTPSValidation();

        spec = new RequestSpecBuilder().setBaseUri(baseURI).setBasePath(basePath).build();
    }

    public Response postRequest(String endpoint, JsonNode requestBody) {

        return RestAssured.given(spec)
            .contentType(ContentType.JSON)
            .body(requestBody)
        .when()
            .post(endpoint);
    }

    public Response getRequest(String endpoint) {

        return RestAssured.given(spec)
            // In our case, we won't use the "token" variable, as the API doesn't require so.
            // But if your API require, here you can use the token like this:
            // .auth().basic("token", myToken)
            .contentType(ContentType.JSON)
        .when()
            .get(endpoint);
    }
}

Show code in Github Gist

That’s a great step up. This way, if you have multiple environments in your setup, you just need to create another application-YOUR_PROFILE_NAME.properties.

Executing the test suit

You must be wondering: How do I run the test suit with this newly created profile?

The answer is simple, just execute mvn clean test -Dspring.profiles.active=prod.

By default, if you just run the mvn clean test command, Spring Boot will try to find a file called application.properties and automatically activate it.

Now we have significantly improved the test setup of our application by:

  • The refactoring of the Object mapping to clean up our code and apply some best practices
  • Improving the response validation by adding a new dependency and using it to simplify the check
  • Learning how to handle multiple test environments. This should be useful when it comes to companies that have layers of environments before the code reach production

Are you curious about the article? Building a Java API test framework part 3 will further improve our application. We will then go deeper into the following topics:

  • Test reporting with Allure reports
  • Configure a CI pipeline with GitHub actions
  • Publish the test report on GitHub pages

(Image by Mohammad Rahmani on Unsplash).

API TESTING WITH JAVA AND SPRING BOOT TEST – PART 1: THE BASIC SETUP

Here at Mercedes-benz.io (MB.io), we collaborate as multiple multi-disciplinary teams (nothing new to a Scrum-based organization).

I’m part of one of those teams, responsible for a Java-based microservice. Since this microservice sends data to a back-office application, we need to test the APIs provided by it.

With that said, we had the challenge to build a new API test framework from scratch.

In this series of articles we’ll show:

  • How we choose the tools
  • The process of creating and improving the test framework
  • Pipeline configuration
  • Test report

Choosing the language and framework

The main reason why we went for a Java-based framework is that the background of our team is Java and the microservice itself is written in this language. Our team is composed of Java developers, so they can contribute to building the right solution for our team and also maintain the code base of the test repository in case it’s needed.

The test framework we’ve chosen to be the base of our solution was Rest Assured.io. The reason behind it is that rest assured is already used in several projects within our tribe at MB.io and is also widely used and maintained in the community.

We also added Spring Boot to organize, structure, and be the foundation of the project.

Setting up the project

Step 1: Create the project

We choose Maven as our dependencies manager. Now, the first thing to do is to add the dependencies we need in our project.

Tip: You can use the spring boot initializer to get the basic pom.xml file with the spring initial setup.

After the initial setup, we need to add the dependencies for the rest-assured test framework and other things we’ll use to make our lives easier.

The pom.xml file should be something like that:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>api.test.java</groupId>
    <artifactId>apitest</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>api-test-java</name>
    <description>Api Tests</description>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-text</artifactId>
            <version>1.9</version>
        </dependency>

        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured</artifactId>
            <version>5.1.1</version>
            <exclusions><!-- https://www.baeldung.com/maven-version-collision -->
                <exclusion>
                    <groupId>org.apache.groovy</groupId>
                    <artifactId>groovy</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.apache.groovy</groupId>
                    <artifactId>groovy-xml</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>json-schema-validator</artifactId>
            <version>5.1.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Show code in Github Gist

With this, we should be able to start organizing our project.

Step 2: Changing the Main class

The Main class should be changed to a SpringBootApplication. And the main method must be configured to run as a SpringApplication.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Main {

    public static void main(String[] args) {

        SpringApplication.run(Main.class, args);
    }
}

Show code in Github Gist

Step 3: Create a Service to manage your API

To abstract access and configure the requests in one single place, we can create a new Service and take advantage of it.

Here is the place to set the proper configuration of the requests.

Let’s create a new method here to abstract the use of a post request. In this post request, we’ll provide the URL and the JSON body as parameters, so the file will be something like this:

package org.example.services;

import com.fasterxml.jackson.databind.JsonNode;
import io.restassured.RestAssured;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.http.ContentType;
import io.restassured.response.Response;
import io.restassured.specification.RequestSpecification;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;

@Slf4j
@Service
public class YourApiService {

    private RequestSpecification spec;

    @PostConstruct
    protected void init() {

        // On init you can set some global properties of RestAssured
        RestAssured.useRelaxedHTTPSValidation();

        spec = new RequestSpecBuilder().setBaseUri("https://reqres.in").setBasePath("/api").build();
    }

    public Response postRequest(String endpoint, JsonNode requestBody) {

        return RestAssured.given(spec)
            .contentType(ContentType.JSON)
            .body(requestBody)
        .when()
            .post(endpoint);
    }
}

Show code in Github Gist

Note: We’ll return the full response to be able to validate what we want within the test itself.

As you can see in the file above, we also take advantage of the built-in RequestSpecification that Rest-assured has to set the baseURI and basePath for this service. This is a smart way to configure your service because if you have more than one service in our test framework, each of them can have its setup and host.

Step 4: Add a test case

First things first, let’s add the proper annotations to be a spring boot JUnit 5 test class.

@ExtendWith(SpringExtension.class)
@SpringBootTest
@TestInstance(TestInstance.Lifecycle.PER_CLASS)

After that, let’s add a constructor method and assign the Service to be used in our test as a class variable.

private final YourApiService yourApiService;

public ApiTest(YourApiService yourApiService) {

    this.yourApiService = yourApiService;
}

Now we are good to start adding the test cases here. Let’s do that.

The postRequest method expects two parameters:

  • the endpoint we want to send the data as a String;
  • the request body as a JsonNode.

The first thing we want to do is create an object to send in the request body of our request. We’ll take advantage of the jackson-databind library to help us with the object mapping.

@Test
public void testCreateUser() throws JsonProcessingException {

    ObjectMapper mapper = new ObjectMapper();

    String body = "{\"name\": \"Luiz Eduardo\", \"job\": \"Senior QA Engineer\"}";
    JsonNode requestBody = mapper.readTree(body);
}

Now, we need to make the request and validate what we want. Let’s add that to our test case. The final results should be something like that:

package api.test.java.tests;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.restassured.response.Response;
import org.example.services.YourApiService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;

@ExtendWith(SpringExtension.class)
@SpringBootTest
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class ApiTest {

    private final YourApiService yourApiService;

    public ApiTest(YourApiService yourApiService) {

        this.yourApiService = yourApiService;
    }

    @Test
    public void testCreateUser() throws JsonProcessingException {

        ObjectMapper mapper = new ObjectMapper();

        String body = "{\"name\": \"Luiz Eduardo\", \"job\": \"Senior QA Engineer\"}";
        JsonNode requestBody = mapper.readTree(body);

        Response res = yourApiService.postRequest("/users", requestBody);
        assertThat(res.statusCode(), is(equalTo(201)));
    }
}

Show code in Github Gist

Note: Bear in mind that this is just the first iteration, we’ll improve the code to keep the responsibilities within the respective classes.

What we’ve seen so far:

  • The project’s basic setup
  • How to structure the project
  • How to abstract the requests in the service layer
  • The first functional test case

This is the end of part 1 of this series of articles. The next part will cover:

  • A short refactor on the object mapping
  • Improve the response validation
  • Property files

See you soon!

Photo by Nubelson Fernandes on Unsplash

Andreas Rau – How pilots communicate

… and what we as Software Engineers/ Designers and Businessmen can learn from it

TLDR

In this article you will learn about miscommunication pitfalls in aviation and that the same pitfalls occur in software development, design, and business. We will dive deeper into topics like the aviation decision-making (ADM) process. Have a glance at intercultural communication. How it dictates the way we speak and understand our world. Further an introduction to my own personal experiences and how you can sabotage the productivity of your organization effectively.

In the end, we will apply the ADM to foster and improve your communication.

How miscommunication can trigger disasters

On 25th January 1995, the Avianca flight from Bogota, Colombia, to JFK Airport in New York, was running out of fuel. Air traffic control (ATC) at JFK kept the airplane in a holding position until their plane’s fuel tank was running dangerously low. When revisiting the conversation between the airplane and the ATC at no point in time the word “emergency” or “mayday” was communicated by the pilots. The captain reportedly told the first officer to “tell them we are in an emergency”. instead of letting ATC know that the airplane is in a serious situation, the word “emergency” was not communicated. Instead, the first officer told ATC “We’re running out of fuel” “With air traffic control unaware of the gravity of the problem, the plane crashed just nine minutes later. Out of the 158 people on board, 73 died, including the pilot and the co-pilot.”

The complexity of reality

The Austrian philosopher, Ludwig Wittgenstein already shared with the world in 1921 in his famous book “Tractatus logico-philosophicus”.

“Ordinary language is imperfect and cannot capture the full complexity of reality.”

From this, we can derive that even under calm conditions the human language fails. In the before shown example, it gets even worse under stressful conditions.

From Aviation to Software Development

First of all a disclaimer. I am a software developer and paragliding pilot. I am well aware that in airline aviation serious mistakes can endanger passenger lives and software development in most cases does not. What I am interested in, are the circumstances of mistakes and the diversity of people involved as well as their communication. When I was revealed how many aviation accidents happened because of miscommunication I started to question if the same situations occur in my day-to-day job and even in my private life. In both worlds, we face time-critical decisions. We both need to react to events that happen while we are executing tasks. I see only a few differences. One of them is that there are more decisions and events to consider in aviation. This means that there are more of them in a shorter amount of time. Both worlds make decisions. In software development, those decisions and their effects as well as their execution by 3rd parties take more time than in aviation. Think about this statement for a moment. Compare a 2h flight with all the decisions that you might need to take as a pilot to a software development sprint of two weeks.

I am a paragliding pilot, so nothing close to a real pilot, but in my experience, the amount of decisions I have to take in a 2h flight compared to a two-week sprint is about the same. In the next part, we will dive deeper into how the aviation industry takes decisions.

The aeronautical decision-making (ADM) process

The airline industry has identified a process that every aircraft pilot has to obey. Aeronautical decision-making (ADM) is a five-step process that a pilot needs to conduct when facing an unexpected or critical event. Adhering to this process helps the pilot to maximize his success chance.

  1. Start to identify your situation, this is the most important step. Accurately detecting it enables you to make correct decisions and raise the probability of success.
  2. Evaluate your options and in my experience, there are often more than I expected to be in the beginning.
  3. Choose from your generated options while accessing the risks and viability.
  4. Act according to your plan.
  5. Evaluate if your action was successful and prepare for further decisions. You will always have further decision points where you need to start the process of ADM again.

This process is only one of many more in aviation.

Let’s apply this to a software bug.

  1. Identify your situation
    • What is the real cause for the bug?
    • Is it reproducable, part of my product scope or not?
    • Did this bug occur because of our code changes or of dependency updates?
    • Is this bug on live systems?
    • Can I resolve the bug?
    • Do I need help?
    • Can I get more information?
  2. Evaluate your options
    • Patch the bug with a new version.
    • Ask for help.
    • Investigate further
    • Decline because it’s a feature and the user is using it incorrectly.
  3. Choose
    • Let’s assume the bug is on a live system and needs to be fixed asap -> Patch the bug with a new version.
  4. Act
    • Please enter your routine for fixing a bug here
  5. Evaluate if your action was successful
    • Is the live system running as expected and was the bug resolved?
    • Should we establish a standardized process to fix bugs?
    • Did I resolve the bug in time? If not practice time management.
    • Is there anything we can do to mature the product?
    • Feedback to QA.
    • Share your insights.
    • Improve test procedures.

This is an easy example to illustrate how the ADM process can be applied to software development. A lesson I learned from paragliding and software development is to always finish your plan even if the circumstances change during your action. Trust in your abilities and execute your plan. If you followed the previous steps correctly your actions cannot be severely wrong. Given that the information you based your analysis on was correct.

Which language we use is an important part of correctly communicating with each other, let’s have a look at aviation English next.

Aviation English

Pilots and crew, regardless of their own native language or any other languages they speak, travel across the world. They have to be able to communicate with every airport and every ATC they face, on a daily basis. This was a challenge to solve, which occurred with the rise of civil aviation in the mid-20th century. There was already an unspoken agreement in place. The language of the sky at that time was Aviation English. Now Aviation English is, as misleading as it sounds not the English language that we know. In fact, it is a separate language compared to what is spoken on the ground. Even native English speakers have quite a long road to learn it ahead of them. It uses standardized phraseology in radio communications to ensure aviation safety. Since the manufacturing, as well as the operation of air crafts, was dominated by English-speaking countries. The International Civil Aviation Organization (ICAO) slowly but steadily understood one thing.

Good processes and procedures themselves will not solve the issue.

In 1951 they suggested that English should be the de facto international language of civil aviation. Let me emphasize that ICAO in 1951 only suggested, that English should be the language of the sky. It took them 50 more years, in 2001, to actually determine English as the standardized language of air transport. With said standardization, they published a directive. It stated that all aviation personnel, including pilots, flight attendants, and aircraft controllers must pass an English proficiency test and meet the requirements. Before that, language skills were not checked in any standard way.

Now let that sink in for a moment.

Tech/Design/Business English

In Tech, Design and Business we do have our own set of languages. I am not talking about programming languages. Try to explain to your grandma/grandpa what exactly was decided in the last SAFE PI planning. You can substitute PI planning with almost every other meeting we have in our company. Now ask for honest feedback: “Can you summarize what I just told you?” Be prepared, it might be the case, that your elderly relatives are very kind to you and try to avoid the task you just gave them. But they will most likely not be able to summarize what you have explained to them. I already have issues trying to explain such things to my parents. I can already sense while speaking, that my parents won’t understand a word.

Although you and I were using English as a language. Our terminology, acronyms, processes, and neologism makes Tech/Design/Business English a very complex language.

¿Habla español?

I had the luck to work with many great people in my career so far and I am more than grateful for every one of them. Nevertheless, I discovered a couple of things for myself over the years. We are all working in the field of Information Technology, Design, and Business. We are all speaking the same language and share the same enthusiasm and skills. And still, we are different. We are all shaped and formed during our private and professional lives in ways one can only imagine. We all have a wide range of different religious, social, ethnic, and educational backgrounds. Living close to our families or far away from them. These differences became more and more visible to me with the amount of time I have spent with them. Differences in how colleagues perceive what you are trying to tell them. Differences in how people value their pursuits and sometimes sacrifice their benefits for the sake of the group. Differences in how authorities communicate with subordinates and vice versa. And these are only the people that I had the chance to work with. You have made your own experiences and shared time with so many more great souls.

What we are now tapping into is the field of intercultural communication. Gert (Gerard Hendrik) Hofstede (Dutchman, born on October 3, 1928, Haarlem, Netherlands) is a Dutch sociologist who proposed an indicators set that determines the various people’s cultural characteristics based on research conducted in the 1960-70s. The subject was part of my studies for one semester. At that time my brain did not understand the extent of the topic and how important it will be for my future life. Intercultural communication describes the discipline that studies communication across different cultures and social groups. In other words, how culture affects communication. There is an impressive amount of research done in the field of intercultural communication which investigates topics like:

  • Collectivist versus Individualistic
  • High Context versus Low Context
  • Power Distance
  • Feminity versus Masculinity
  • Uncertainty Avoidance
  • Long-term Orientation versus Short-term Orientation
  • Indulgence versus Restraint

All of them are worth investigating and I encourage you to do so. I have gathered further readings which should get you started.

Personal

On top of every culture, there is you, you how you perceive the world around you, and you, how you make sense of everything which is shaping you. Your personal touch might very well steer you into a counter course of what your culture tried to induce into you for the entirety of your childhood and more. I hereby am not suggesting that you are all rebels. I want to highlight that the personal level of communication can be far off from how the folks back in your hometown used to talk. If you haven’t already met a vast variety of people during your school time you will definitely do so in your professional life. In Software Development I have had the chance to work with many great people from all over the world. Although at some point already familiar with the concept of intercultural communication I often unknowingly said or did something I thought would be appropriate at this exact moment in time… it wasn’t. Having the basics of intercultural communication in mind is necessary but not sufficient. Get to know the person you are talking to and discover a new level of communication.

Interim

We have learned a couple of things about communication, let’s take a second look at the introductory example. The first officer, in disregard of what the pilot told him, made a severe mistake in not properly communicating the extent of the situation. Maybe, in her/his culture, it is common to understate issues and it can be rude to talk about severe issues or problems directly. Nevertheless, the situation required clear and fact-based information. Correct identification of the situation was therefore not possible. All further steps in the aeronautical decision-making process of the ATC were from then on based on false information and we all know the outcome. I often find myself in meetings where colleagues or superiors introduce me to a brand new process that will revolutionize how our company works, solve all the problems and issues at once and make us all happier. Thanks to good marketing everybody is excited and eager to implement the new processes with huge costs in time, money, and motivation only to find out that in the end, it didn’t work — again. I do not want to sound pessimistic, I want to tell you my perspective. Looking into software development and all the processes we have, I want to learn from aviation and invest more time and effort into educating our colleagues on how to properly and fruitfully communicate in a standardized and organized way.

Important factors for this, in my opinion, are honest, transparent, and truthful communication where hidden agendas or intercultural communication pitfalls are avoided. And to make one point clear, more communication is not equal to better communication. I think based on this foundation, processes can be fruitful.

Lost in translation

An enterprise company operating in multiple countries spread over multiple continents. What is the first thing that comes into your mind? For me, it is a rich and diverse project team over as many time zones as possible. During my early career, I was exposed to a trend in IT where teams could not be diverse enough. I know many companies which still steer 100% in this direction, but I also know many who are not. What I am trying to do here is to make you as a reader think. Think about your current circumstances, where are you working? Who are your colleagues? Do you work effectively together? Is communication easy for you or is it a burden? Do you have the feeling that meetings actually create useful artifacts or are they mostly a waste of time? Ask yourselves these questions and assess. I can only speak for myself and I have observed both. In my work, a rich and diverse team can accelerate me, but there have also been times when it slowed me down. When looking at the example I showed you earlier. Aviation English is a language that was an agreement on communication but it was not part of any training or checks. It is important to say, that this has nothing to do with the people per se. When I came to college, in Germany, I had the opportunity to choose if I wanted my studies and lectures in German or English. I was motivated and although my mother tongue is German, I wanted my studies to be in English. This was exciting to me and silly young me thought it would be good to already study in English since the language of IT was English. Now I really regret my decision. During all of my studies, I was confronted with a lot of bad English. Many interesting topics got lost in translation, simply because the lecturer was not proficient enough in her/his language skills. Please do not get me wrong here, my English at that time was not better either. I am just trying to make the point that the content of a message can be severely harmed when not communicated properly. During my career, I often did applicant interviews and had to clearly state my veto. The applicant might have had the best CV, and great experience, but bad English skills. In the IT industry, we have the great opportunity to have rich and diverse teams. This is a circumstance not exclusive to IT but it is still in my opinion a gift we should be thankful for. To make sure we respect and maintain said privilege I have a suggestion. I am suggesting that if we put so much emphasis on what technologies an applicant knows, for how many years she/he has worked with tech stack a, b or c. We should also check thoroughly if her/his English skills are proficient enough to communicate properly in a big and diverse company. We should offer and also expect and check from new hires to improve their language skills, if necessary. Language should never be a limiting factor. Coming from IT and especially web development, I have to deal with accessibility day in and day out. For me, it is easy to understand that digital tools and devices are about inclusion. In my opinion, it is the same with language.

Let’s take this a step further. A good colleague of mine introduced me to an amazing article on some second world war CIA practices.

How to effectively sabotage your organization’s productivity

The CIA created the “Simple Sabotage Field Manual” in 1944 on how everyday people could help the allies weaken their country by reducing production in factories, offices, and transportation lines. There is a wonderful article from the business insider which highlights that. Despite being written in 1944 these instructions are timeless. I am sharing with you the selected list of instructions from the business insider article, filtered for communication. See if any of those listed below remind you of our organization, your colleagues, or even yourself.

Organizations and Conferences

  • Insist on doing everything through “channels.” Never permit shortcuts to be taken in order to expedite decisions.
  • Make “speeches.” Talk as frequently as possible and at great length. Illustrate your “points” by long anecdotes and accounts of personal experiences.
  • When possible, refer all matters to committees, for “further study and consideration.” Attempt to make the committee as large as possible — never less than five.
  • Bring up irrelevant issues as frequently as possible.
  • Haggle over precise wordings of communications, minutes, and resolutions.
  • Refer back to matters decided upon at the last meeting and attempt to re-open the question of the advisability of that decision.

Managers

  • Hold conferences when there is more critical work to be done.
  • Multiply the procedures and clearances involved in issuing instructions, paychecks, and so on. See that three people have to approve everything where one would do.

Employees

  • Work slowly.
  • Contrive as many interruptions to your work as you can.
  • Do your work poorly and blame it on bad tools, machinery, or equipment. Complain that these things are preventing you from doing your job right.
  • Never pass on your skill and experience to a new or less skillful worker.

I highly encourage you to read the full business insider article for some more bitter-sweet laughs.

Foster your communication

Although being funny to read, the sad truth is that some of these instructions are common practice in our organization. I advise you to take this list into your notes, bookmark the article, read through it frequently, and ask yourself: Do my communication behaviors fall into the same categories? If yes, no worries! Everybody has to start from somewhere. Remember the ADM process:

  • Identify your situation and ask yourself: Am I sabotaging my company? It’s important, to be honest here! (Remember: Accurately detecting it enables you to make correct decisions and raise the probability of success)
  • Evaluate your options, there are plenty! Seek feedback from people you trust and ask them for honest feedback on your ways of communication, do an Udemy course. Heck, maybe even join a debating club!
  • Choose from your generated options while accessing the risks and viability.
  • Act according to your plan.
  • Evaluate if your action was successful and prepare for further decisions.

This is not easy – but it can be done. You can do it!

Now to bring this article to an end let’s look at the last dimension of communication.

Are you talking to me?

I often find myself in situations where I talk about colleagues and superiors rather than talking with them. Talking about colleagues is easy, but with most things in life, the outcome of easy is not great. It takes courage to work on yourself and even more to reach out for help. There is no effortless solution, be honest and ask yourself if you really want to change something about how you communicate and how you are perceived while communicating. This article is at most only a catalyst that will hopefully ignite your own journey.

Recap

We have learned how two, at first sight, completely different professions share many fundamental communication skills. We have had a look at the aeronautical decision-making process, what intercultural communication is, that processes are only as good as the material we put into them to be processed, how to effectively sabotage your company and what you can do to foster and improve your communication.

We touched on many topics today and I hope this article touched you personally in at least one of them. If you now think about what you have read in the last couple of minutes I feel already successful in my educational mission and if you remember only one thing then something along the lines of this:

‘Don’t worry the worst mistake you can make, is to not communicate at all.’

Further readings

As promised here you have a small collection to further educate yourself about the topics in this article.

Full blown ADM

Aviation English

Understanding Intercultural Communication

Gert Hofstede – Intercultural Communication

Business insider – How to sabotage your organizations productivity

Workplace Communication


Photo by Avel Chuklanov on Unsplash

Use Tailwind without Tailwind

Tailwind is one of those very controversial things, some people argue that it is the best since sliced bread, others it is a tool sent by the devil itself. Nonetheless, this is not an article about if you should use it or not, there are already plenty of those articles about the pros and cons on the internet.

What makes Tailwind good is the documentation, you may not agree on the “naming” but for every class that you can use there is an equivalent showing how to use it with CSS, for that, the team has done an outstanding job.

Tailwind is super fun to work with after you pass the initial learning threshold, but as a Developer, you still need to understand CSS to use it correctly.

CSS is not hard nor is broken.

In the end, it is just a tool to help you to write CSS, and as a tool, it limits the options of what you could do, for example, you would never be able to use a custom grid layout with Tailwind only, as the one below:

MobileDesktop
Grid Layout MobileGrid Layout Desktop
Source: https://webkit.org/demos/css-grid/

In this article, you will learn how to use Tailwind documentation to write your CSS styles. The following topics will be discussed:

The idea is to have the full power to write your CSS together with the cool ideas and patterns created by the Tailwind team. So let’s get started.

Preflight

Preflight is a set of base styles for Tailwind projects that are designed to smooth over cross-browser inconsistencies and make it easier for you to work within the constraints of your design system.

Here you have basically to copy the preflight.css created by them.

/* preflight.css */
*,
::before,
::after {
  box-sizing: border-box;
  border-width: 0;
  border-style: solid;
  border-color: currentcolor;
}

html {
  line-height: 1.5;
  text-size-adjust: 100%;
  tab-size: 4;
  font-family: system-ui;
}

body {
  margin: 0;
  line-height: inherit;
}

hr {
  height: 0;
  color: inherit;
  border-top-width: 1px;
}

abbr:where([title]) {
  text-decoration: underline dotted;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  font-size: inherit;
  font-weight: inherit;
}

a {
  color: inherit;
  text-decoration: inherit;
}

b,
strong {
  font-weight: bolder;
}

code,
kbd,
samp,
pre {
  font-family:
    "fontFamily.mono",
    ui-monospace,
    SFMono-Regular,
    Menlo,
    Monaco,
    Consolas,
    "Liberation Mono",
    "Courier New",
    monospace;
  font-size: 1em;
}

small {
  font-size: 80%;
}

sub,
sup {
  font-size: 75%;
  line-height: 0;
  position: relative;
  vertical-align: baseline;
}

sub {
  bottom: -0.25em;
}

sup {
  top: -0.5em;
}

table {
  text-indent: 0;
  border-color: inherit;
  border-collapse: collapse;
}

button,
input,
optgroup,
select,
textarea {
  font-family: inherit;
  font-size: 100%;
  font-weight: inherit;
  line-height: inherit;
  color: inherit;
  margin: 0;
  padding: 0;
}

button,
select {
  text-transform: none;
}

button,
[type="button"],
[type="reset"],
[type="submit"] {
  appearance: button;
  background-color: transparent;
  background-image: none;
}

:-moz-focusring {
  outline: auto;
}

:-moz-ui-invalid {
  box-shadow: none;
}

progress {
  vertical-align: baseline;
}

::-webkit-inner-spin-button,
::-webkit-outer-spin-button {
  height: auto;
}

[type="search"] {
  appearance: textfield;
  outline-offset: -2px;
}

::-webkit-search-decoration {
  appearance: none;
}

::-webkit-file-upload-button {
  appearance: button;
  font: inherit;
}

summary {
  display: list-item;
}

blockquote,
dl,
dd,
h1,
h2,
h3,
h4,
h5,
h6,
hr,
figure,
p,
pre {
  margin: 0;
}

fieldset {
  margin: 0;
  padding: 0;
}

legend {
  padding: 0;
}

ol,
ul,
menu {
  list-style: none;
  margin: 0;
  padding: 0;
}

textarea {
  resize: vertical;
}

input::placeholder,
textarea::placeholder {
  opacity: 1;
  color: #9ca3af;
}

button,
[role="button"] {
  cursor: pointer;
}

:disabled {
  cursor: default;
}

img,
svg,
video,
canvas,
audio,
iframe,
embed,
object {
  display: block;
  vertical-align: middle;
}

img,
video {
  max-width: 100%;
  height: auto;
}

That’s it, just add this CSS to your project.

Theme

The theme file is where you define your project’s color palette, type scale, fonts, breakpoints, border radius values, and more.

This is the most important part, getting the same idea of the tailwind.config.js file where you can customize your theme, create a theme file following the Tailwind definitions:

/* theme.css */
:root {
  --size-0-5: 0.125rem; /* spacing:0.5 */
  --size-1: 0.25rem; /* spacing:1 */
  --size-1-5: 0.375rem; /* spacing:1.5 */
  --size-2: 0.5rem; /* spacing:2 */
  --size-2-5: 0.625rem;
  --size-3: 0.75rem;
  --size-3-5: 0.875rem;
  --size-4: 1rem;
  --size-5: 1.25rem;
  --size-6: 1.5rem;
  --size-7: 1.75rem;
  --size-8: 2rem;
  --size-9: 2.25rem;
  --size-10: 2.5rem;
  --size-11: 2.75rem;
  --size-12: 3rem;
  --size-14: 3.5rem;
  --size-16: 4rem;
  --size-20: 5rem;
  --size-24: 6rem;
  --size-28: 7rem;
  --size-32: 8rem;
  --size-36: 9rem;
  --size-40: 10rem;
  --size-44: 11rem;
  --size-48: 12rem;
  --size-52: 13rem;
  --size-56: 14rem;
  --size-60: 15rem;
  --size-64: 16rem;
  --size-72: 18rem;
  --size-80: 20rem;
  --size-96: 24rem;
  --size-xs: 20rem; /* 320px */
  --size-sm: 24rem; /* 384px */
  --size-md: 28rem; /* 448px */
  --size-lg: 32rem; /* 512px */
  --size-xl: 36rem; /* 576px */
  --size-2xl: 42rem; /* 672px */
  --size-3xl: 48rem; /* 768px */
  --size-4xl: 56rem; /* 896px */
  --size-5xl: 64rem; /* 1024px */
  --size-6xl: 72rem; /* 1152px */
  --size-7xl: 80rem; /* 1280px */
  --size-full: 100%;
  --size-fit: fit-content;
  --size-min: min-content;
  --size-max: max-content;
  --size-auto: auto;
  --size-none: none;
  --size-prose: 65ch;
  --size-screen-width: 100vw;
  --size-screen-height: 100vh;
  --size-screen-xs: 480px;
  --size-screen-sm: 640px;
  --size-screen-md: 768px;
  --size-screen-lg: 1024px;
  --size-screen-xl: 1280px;
  --size-screen-2xl: 1536px;
  --grid-1: repeat(1, minmax(0, 1fr));
  --grid-2: repeat(2, minmax(0, 1fr));
  --grid-3: repeat(3, minmax(0, 1fr));
  --grid-4: repeat(4, minmax(0, 1fr));
  --grid-5: repeat(5, minmax(0, 1fr));
  --grid-6: repeat(6, minmax(0, 1fr));
  --grid-7: repeat(7, minmax(0, 1fr));
  --grid-8: repeat(8, minmax(0, 1fr));
  --grid-9: repeat(9, minmax(0, 1fr));
  --grid-10: repeat(10, minmax(0, 1fr));
  --grid-11: repeat(11, minmax(0, 1fr));
  --grid-12: repeat(12, minmax(0, 1fr));
  --border: 1px;
  --border-0: 0;
  --border-2: 2px;
  --border-4: 4px;
  --border-8: 8px;
  --ring: 0 0 0 var(--border);
  --ring-2: 0 0 0 var(--border-2);
  --ring-4: 0 0 0 var(--border-4);
  --ring-8: 0 0 0 var(--border-8);
  --rounded: 0.25rem;
  --rounded-sm: 0.125rem;
  --rounded-md: 0.375rem;
  --rounded-lg: 0.5rem;
  --rounded-xl: 0.75rem;
  --rounded-2xl: 1rem;
  --rounded-3xl: 1.5rem;
  --rounded-full: 9999px;
  --shadow: 0 1px 3px 0 rgb(0 0 0 / 10%), 0 1px 2px -1px rgb(0 0 0 / 10%);
  --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 5%);
  --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 10%), 0 2px 4px -2px rgb(0 0 0 / 10%);
  --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 10%), 0 4px 6px -4px rgb(0 0 0 / 10%);
  --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 10%), 0 8px 10px -6px rgb(0 0 0 / 10%);
  --shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 25%);
  --shadow-inner: inset 0 2px 4px 0 rgb(0 0 0 / 5%);
  --font-weight-thin: 100;
  --font-weight-extralight: 200;
  --font-weight-light: 300;
  --font-weight-normal: 400;
  --font-weight-medium: 500;
  --font-weight-semibold: 600;
  --font-weight-bold: 700;
  --font-weight-extrabold: 800;
  --font-weight-black: 900;
  --line-spacing-xs: 1rem;
  --line-spacing-sm: 1.25rem;
  --line-spacing-md: 1.5rem;
  --line-spacing-lg: 1.75rem;
  --line-spacing-xl: 1.75rem;
  --line-spacing-2xl: 2rem;
  --line-spacing-3xl: 2.25rem;
  --line-spacing-4xl: 2.5rem;
  --line-spacing-5xl: 1;
  --line-spacing-6xl: 1;
  --line-spacing-7xl: 1;
  --line-spacing-8xl: 1;
  --line-spacing-9xl: 1;
  --text-xs: 0.75rem;
  --text-sm: 0.875rem;
  --text-md: 1rem;
  --text-lg: 1.125rem;
  --text-xl: 1.25rem;
  --text-2xl: 1.5rem;
  --text-3xl: 1.875rem;
  --text-4xl: 2.25rem;
  --text-5xl: 3rem;
  --text-6xl: 3.75rem;
  --text-7xl: 4.5rem;
  --text-8xl: 6rem;
  --text-9xl: 8rem;
  --color-canvas: #f9fafb; /* gray:50 */
  --color-contrast: #27272a; /* zinc:800 */
  --color-contrast-50: #fafafa; /* zinc:50 */
  --color-contrast-100: #f4f4f5; /* zinc:100 */
  --color-contrast-200: #e4e4e7; /* zinc:200 */
  --color-contrast-300: #d4d4d8; /* zinc:300 */
  --color-contrast-400: #a1a1aa; /* zinc:400 */
  --color-contrast-500: #71717a; /* zinc:500 */
  --color-contrast-600: #52525b; /* zinc:600 */
  --color-contrast-700: #3f3f46; /* zinc:700 */
  --color-contrast-800: #27272a; /* zinc:800 */
  --color-contrast-900: #18181b; /* zinc:900 */
  --color-primary-backdrop: #bfdbfe; /* blue:200 */
  --color-primary-focus: #93c5fd; /* blue:300 */
  --color-primary: #2563eb; /* blue:600 */
  --color-primary-content: #1e40af; /* blue:800 */
  --color-primary-contrast: #fff;
  --color-error-backdrop: #fecdd3; /* rose:200 */
  --color-error-focus: #fda4af; /* rose:300 */
  --color-error: #f43f5e; /* rose:600 */
  --color-error-content: #9f1239; /* rose:800 */
  --color-error-contrast: #fff;
  --color-success-backdrop: #bbf7d0; /* green:200 */
  --color-success-focus: #86efac; /* green:300 */
  --color-success: #22c55e; /* green:600 */
  --color-success-content: #166534; /* green:800 */
  --color-success-contrast: #fff;
  --color-content-heading: #111827; /* gray:900 */
  --color-content-body: #1f2937; /* gray:800 */
  --color-content-secondary: #374151; /* gray:700 */
  --color-content-tertiary: #6b7280; /* gray:500 */
  --color-content-disabled: #9ca3af; /* gray:400 */
  --color-content-contrast: #d1d5db; /* gray:300 */
}

@media (prefers-color-scheme: dark) {
  :root {
    --color-canvas: #27272a; /* zinc:800 */
    --color-contrast: #fff;
    --color-contrast-50: #18181b; /* zinc:900 */
    --color-contrast-100: #27272a; /* zinc:800 */
    --color-contrast-200: #3f3f46; /* zinc:700 */
    --color-contrast-300: #52525b; /* zinc:600 */
    --color-contrast-400: #71717a; /* zinc:500 */
    --color-contrast-500: #a1a1aa; /* zinc:400 */
    --color-contrast-600: #d4d4d8; /* zinc:300 */
    --color-contrast-700: #e4e4e7; /* zinc:200 */
    --color-contrast-800: #f4f4f5; /* zinc:100 */
    --color-contrast-900: #fafafa; /* zinc:50 */
    --color-primary-backdrop: #93c5fd; /* blue:300 */
    --color-primary-focus: #3b82f6; /* blue:500 */
    --color-primary: #2563eb; /* blue:600 */
    --color-primary-content: #1e40af; /* blue:800 */
    --color-primary-contrast: #fff;
    --color-error-backdrop: #fda4af; /* rose:300 */
    --color-error-focus: #f43f5e; /* rose:500 */
    --color-error: #e11d48; /* rose:600 */
    --color-error-content: #9f1239; /* rose:800 */
    --color-error-contrast: #fff;
    --color-success-backdrop: #86efac; /* green:300 */
    --color-success-focus: #22c55e; /* green:500 */
    --color-success: #16a34a; /* green:500 */
    --color-success-content: #166534; /* green:800 */
    --color-success-contrast: #fff;
    --color-content-heading: #f9fafb; /* gray:50 */
    --color-content-body: #e5e7eb; /* gray:200 */
    --color-content-secondary: #d1d5db; /* gray:300 */
    --color-content-tertiary: #6b7280; /* gray:500 */
    --color-content-disabled: #4b5563; /* gray:600 */
    --color-content-contrast: #374151; /* gray:700 */
  }
}

@custom-media --xs (min-width: var(--size-screen-xs));
@custom-media --sm (min-width: var(--size-screen-sm));
@custom-media --md (min-width: var(--size-screen-md));
@custom-media --lg (min-width: var(--size-screen-lg));
@custom-media --xl (min-width: var(--size-screen-xl));
@custom-media --xxl (min-width: var(--size-screen-2xl));

To use those variables, you can declare as follows:

.example {
  padding: var(--size-3); /* p-3 */
  border-radius: var(--rounded-lg); /* rounded-lg */
  box-shadow: var(--shadow-lg); /* shadow-lg */
  font-size: var(--text-sm); /* text-sm */
  line-height: var(--line-spacing-sm); /* text-sm */
}

You can define your CSS variables to have the same values defined in the Tailwind, so you can get through their docs and use their style. But you wouldn’t need to define a variable for every single class, some classes like inline-flex can be directly translated to display: inline-flex.

The good thing is that is one dependency less in your project, no matter how fast the tailwind (re)builds the CSS it will never the faster than having it directly declared, and the main point is that gives the developer full control. The downside is that you lose their syntax sugar.

Styles

Taking what you learned and applying it to a real-world scenario: Imagine you are building a UI Library. You start to build a badge component. This component should have options to change the size, color, and format, so you can create the following style:

/* badge.css */
.badge {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  white-space: nowrap;
  padding: var(--size-1) var(--size-2);
  text-align: center;
  vertical-align: middle;
  font-size: var(--badge-font-size, var(--text-md));
  line-height: var(--badge-line-height, var(--line-spacing-md));
  font-weight: var(--font-weight-semibold);
  border: var(--badge-border, var(--border) solid var(--color-contrast-200));
  border-radius: var(--badge-border-radius, var(--rounded-lg));
  background-color: var(--badge-background-color, var(--color-contrast-50));
  color: var(--badge-color, var(--color-content-body));

  &.is-pill {
    --badge-border-radius: var(--rounded-full);
  }

  &.is-xs {
    --badge-font-size: var(--text-xs);
    --badge-line-height: var(--line-spacing-xs);
  }

  &.is-sm {
    --badge-font-size: var(--text-sm);
    --badge-line-height: var(--line-spacing-sm);
  }

  &.is-lg {
    --badge-font-size: var(--text-lg);
    --badge-line-height: var(--line-spacing-lg);
  }

  &.is-xl {
    --badge-font-size: var(--text-xl);
    --badge-line-height: var(--line-spacing-xl);
  }

  &.is-info {
    --badge-border: var(--border) solid var(--color-primary-focus);
    --badge-background-color: var(--color-primary);
    --badge-color: var(--color-primary-contrast);
  }

  &.is-error {
    --badge-border: var(--border) solid var(--color-error-focus);
    --badge-background-color: var(--color-error);
    --badge-color: var(--color-error-contrast);
  }

  &.is-success {
    --badge-border: var(--border) solid var(--color-success-focus);
    --badge-background-color: var(--color-success);
    --badge-color: var(--color-success-contrast);
  }

  &.is-contrast {
    --badge-border: var(--border) solid var(--color-contrast-800);
    --badge-background-color: var(--color-contrast-700);
    --badge-color: var(--color-content-contrast);
  }
}

Show code in action

Using CSS variables can give you more freedom to work with while having a consistent style.

An equivalent solution using Tailwind with @apply, even if this is not the recommended way to use the tailwind, but you can get an idea if placed inside the HTML.

/* badge.css */
.badge {
  @apply inline-flex flex-wrap items-center justify-center whitespace-nowrap py-1 px-2 text-center align-middle font-semibold;

  &:not(.is-pill) {
    @apply rounded-lg;
  }

  &.is-pill {
    @apply rounded-full;
  }

  &.is-xs {
    @apply text-xs;
  }

  &.is-sm {
    @apply text-sm;
  }

  &.is-base {
    @apply text-base;
  }

  &.is-lg {
    @apply text-lg;
  }

  &.is-xl {
    @apply text-xl;
  }

  &:not(.is-info, .is-error, .is-success, .is-contrast) {
    @apply border-contrast-200 bg-contrast-50 text-content;
  }

  &.is-info {
    @apply border-primary-focus bg-primary text-primary-contrast;
  }

  &.is-error {
    @apply border-error-focus bg-error text-error-contrast;
  }

  &.is-success {
    @apply border-success-focus bg-success text-success-contrast;
  }

  &.is-contrast {
    @apply border-contrast-800 bg-contrast-700 text-content-contrast;
  }
}

If you notice they are quite similar, but with Tailwind, you declare the properties horizontally and with CSS vertically, which gives the other a more clean aspect.

Now, checking a real-world example, like the one in the Netlify’s Page, things don’t look that simple anymore.

So, using the @apply mixing, you could have the best of both worlds, right? But, in this case, what would be the real benefit of writing classes using Tailwind instead of pure CSS?

Conclusion

This is just an example to give you an idea. Maybe you don’t need that flexibility of using CSS and Tailwind helps to build what you need, then go for it, or perhaps your team dislikes Tailwind and this could give them a taste. Whatever your situation is, it is good to have options. Photo by Kelly Sikkema on Unsplash.

LEARN MORE ABOUT CSS IN OUR TRIBE OF DIGITAL ENTHUSIASTS

View job openings

Spaceships and testing in Javascript

What do a Spaceship and JavaScript have in common? Both already reached space.

The Crew Dragon Spaceship from SpaceX uses JavaScript in the main cockpit panels[1]. It’s super cool to see where the language has come from and what can be achieved with it.

Just like rockets, spaceships, and many others, critical and non-critical projects, require a lot of testing before production launch. Otherwise, a “KaBuM! Effect” could happen, and unless it is a firework, it won’t make anyone happy.

In any case, testing is not complicated, and even if most of us are not building things that can explode, treat them as if they are of equal importance. Testing makes error detection easier and can also save a lot of time. It can be tricky at first, but with practice and experience, it becomes an ally, you just need to make it part of your daily work.

Before getting started, we need to take a look and understand how things work under the hood. This article will cover the basics of testing using JavaScript, including:

  1. Testing Fundamentals
  2. Testing with Jest
  3. Mocking Fundamentals
  4. Static Code Analysis

Testing Fundamentals

One of the most common phrases in software development is: “whattaf*ck…”, some say that the quality of the code can be measured by the FPS (f*cks per second) heard during the development process. $h!t happens, and fixing it can be simple, but if it is a little more complicated, it can take days, weeks, and even months to solve it. Thus, the idea of creating an automated test is to try to catch as many errors as possible in our code before they happened.

Imagine that you are building a spaceship, and this spaceship requires a calculator module and if it fails it can explode. You aim to make sure the results are always correct. Then, you start creating the first method of this module.

export const sum = (a, b) => a + b;

Show code in action

To test this code, you have to check the result of your function to validate your assumption.

import { sum } from './calculator.js';

const expected = 4;
const result = sum(2, 2);

if (result !== expected) {
  throw new Error(`KaBuM! It Exploded!`, { cause: `${result} is not equal to ${expected}` });

Show code in action

In the example, you run and test to check if the result is what you expected. Although this implementation works, it cannot be reused. To simplify the testing process, extract the logic into a new method, that way it can now be used for more cases.

export const expect = value => ({
  toEqual(expected) {
    if (value !== expected) {
      throw new Error(`KaBuM! It Exploded!`, { cause: `${value} is not equal to ${expected}` });
    }
  }
});

Show code in action

Now, update our previous code.

import { sum } from './calculator.js';
import { expect } from './testing.js';

expect(sum(2, 2)).toEqual(4);
expect(sum(2, 'a')).toEqual(NaN); // Error

Show code in action

Much better! However, there is no description showing what is being tested, and if you start adding more tests and one fail, the remaining tests will not run, so let’s fix it by encapsulating this code inside a try/catch:

export const test = (description, fn) => {
  try {
    fn();
    console.log(`✓ ${description}`);
  } catch (error) {
    console.error(`✕ ${description}`);
    console.error(error);
  }
};
// ...

Show code in action

Now, use our new function inside our test code.

import { sum } from './calculator.js';
import { expect, test } from './testing.js';

test('sum numbers', () => expect(sum(2, 2)).toEqual(4));

Show code in action

Let’s open the terminal and run our test:

$ npx babel-node calculator.test.js

In case of an error in the code, you will see the following error message.

...
test('sum numbers', () => expect(sum(1, 2)).toEqual(4));

Show code in action

Congratulations! You have now created a simple JavaScript Testing Framework. The good news is that there are already some great tools for testing automation. The most famous is Jest, and you can make your test compatible with it by just removing one line of code and running it:

import { sum } from './calculator.js';
test('sum numbers', () => expect(sum(2, 2)).toEqual(4));

Show code in action

In the terminal, run the command:

$ npx jest calculator.test.js

There is more than just that. Let’s move on and learn more about how to test an application, even without running the code (Yep, this is possible in JavaScript).

Testing with Jest

Jest is a delightful JavaScript Testing Framework with a focus on simplicity. It has already built in most of the features you expect from a testing framework: A great set of exceptions, code coverage, mocking, runs fast, good documentation, and an incredible community around it.

First things first, you need to install Jest

$ npm install --save-dev jest

Thereafter, update the package.json file to run it:

...
"scripts": {
  "test": "jest"
},
...

Show code in action

npm run test or also manually: npx jest

If you like, you can also run jest --init to create a configuration file

Comparing Values

Let’s go ahead, now you are going to create a weapon module for your spaceship. Start creating a simple test example:

describe('Weapon Module', () => {
  test('a simple test', () => {
    expect(2 + 2).toBe(4);
  });
  // ...
});

Show code in action

After running Jest, this is the result:

The primary comparison methods are toBe and toEqualtoBe uses === to check strict equality, while toEqual makes a deep comparison of the properties of the values using Object.is.

// ...
const weapon = { type: 'laser' };
test('check object with toEqual', () => {
  expect(weapon).toEqual({ type: 'laser' });
});
test('check object with toBe', () => {
  expect(weapon).toBe({ type: 'laser' });
});
// ...

Show code in action

Running this test, you will get the following result:

It is up to you to decide which one fits better in your test case, but if you are starting with testing, using the toEqual the method will probably be the best alternative.

Comparing Strings

Regular Expression

Jest does have support for comparing strings. Besides, the regular toEqual regex can also be used for comparison. All you need is to call the toMatch method and pass in the regex string.

const text = 'hello world';
test('string comparison', () => {
  expect(text).toMatch(/hello/);
});

Show code in action

Length

It’s also possible to compare the length between two strings using toHaveLength.

expect('abc').toHaveLength(3);

Show code in action

It works with an array as well.

expect([1, 2, 3]).toHaveLength(3);

Show code in action

Comparing Numbers

Besides the basic comparison methods, you can easily compare numbers in your tests by utilizing the following methods:

  • toBeGreaterThanOrEqual
  • toBeGreaterThan
  • toBeLessThanOrEqual
  • toBeLessThan

In the following example, you can use a loop to check if the result is less than 10.

test('loop less than', () => {
  for (let i = 1; i < 10; i++) {
    expect(i).toBeLessThan(10);
  }
});

Show code in action

When changing the value from 10 to 5, you will receive the following error message.

Comparing Arrays

The toContain method is used for array comparison, which checks if the values are included in the list.

test('check an array', () => {
  const weapons = ['phaser', 'laser', 'plasma cannon', 'photon torpedo'];
  expect(weapons).toContain('laser');
});

Show code in action

Comparing Dynamic Values

In a situation where you don’t have an exact value, but you know the type of the object, so you can use the expect.any method.

Primitive Values

For primitive values like string, number, and booleans, you can use:

  • expect.any(String)
  • expect.any(Number)
  • expect.any(Boolean)
test('check dynamic string', () => {
  expect('disruptor').toEqual(expect.any(String));
  expect(1).toEqual(expect.any(Number));
  expect(false).toEqual(expect.any(Boolean));
});

Show code in action

Objects

You can check an Object with objectContaining to see if an object contains some properties inside. In this case, you don’t need to match the same properties from the object you want to evaluate.

test('check dynamic object', () => {
  const weapon = { type: 'laser', damage: 100, range: 10, available: false };
  expect(weapon).toEqual(
    expect.objectContaining({
      damage: expect.any(Number),
      type: expect.any(String),
      available: expect.any(Boolean),
    })
  );
});

Show code in action

Arrays

It’s also possible to use arrayContaining to check the values, and you can even combine them with all previous checks.

test('check dynamic array', () => {
  const weapons = [
    { type: 'phaser', damage: 150, range: 15, speed: 'fast' },
    { type: 'photon cannon', damage: 10000, range: 100, speed: 'slow' },
  ];
  expect(weapons).toEqual(
    expect.arrayContaining([
      expect.objectContaining({
        type: expect.any(String),
        damage: expect.any(Number),
        range: expect.any(Number),
        speed: expect.any(String),
      }),
    ])
  );
});

Show code in action

Asynchronous Code

There are multiple ways to handle asynchronous code, depending on your needs.

Callback

The easiest way to handle callback is to use a single done argument when calling the callback function. For example,

test('test callback', done => {
  initBattleMode((data) => {
    try {
      expect(data).toEqual({ ready: true });
      done();
    } catch (error) {
      done(error);
    }
  });
});

Show code in action

Promise

Asynchronous code with a promise is a lot easier, as all you need to do is to return the promise. Have a look at the modified example:

test('test promise', () => {
  return initBattleMode().then((data) => {
    expect(data).toEqual({ ready: true });
  });
});

Show code in action

async/await

On the other hand, using async/await is a lot more straightforward. So let’s reuse the previous example and modify it to use async/await instead.

test('test async', async () => {
  const data = await initBattleMode();
  expect(data).toEqual({ ready: true });
});

Show code in action

This is just a taste. For a complete list of matches, take a look at the reference docs.

Mocking Fundamentals

Occasionally, when doing our tests, you can’t rely on real data because it’s slow, private, or for other reasons.

Mocking allows you to intercept or erase the actual implementation of a function, capture calls (and the parameters passed in those calls), and enable test-time configuration of returned values.

One way to deal with this situation is to mock (faking) your data. Jest has already built-in some great tools with data mocking. It uses a custom resolver for imports in your tests, making it simple to mock any object outside your test’s scope. In addition, you can use mocked imports with the rich Mock Functions API to spy on function calls with readable test syntax.

Let’s focus on two types of mocks using Jest, the mock function, and the mock module.

Mock Functions

To mock a function, you just need to declare the method as a jest function: jest.fn(), with that, you can start our evaluation. Here is a quick example:

// ...
describe('Rocket Engine', () => {
  const cb = jest.fn();
  beforeEach(() => {
    cb.mockReset();
  });
  test('check callback response', () => {
    cb.mockImplementationOnce(() => 2).mockImplementation(() => 1);
    expect([1, 2, 3].map(cb)).toEqual([2, 1, 1]);
    expect(cb).toHaveBeenCalledTimes(3);
  });
  // ...
});

Show code in action

First, declare your mock method, and then you defined the first and the default outputs. Next, check if the output matches our expected result. Thereafter, check if the method was called the amount of time expected and if the parameters were correct.

If you want to learn more, refer to the reference docs

Mock Modules

Mocking a module works similarly to mocking a function, but instead of applying it to a function, you have to intercept a module import.

Back to the spaceship idea, create a startEngine method that receives a callback function as a parameter and does an HTTP call to an API server. In this case, you have to mock the unfetch module.

import fetch from 'unfetch';

export const startEngine = async (callback) => {
  const res = await fetch('https://api.space.com/rocket/engine/start');
  const json = await res?.json();
  if (json) {
    if (callback) {
      callback(json);
    }
    return json;
  }
  return undefined;
};

Show code in action

Now, declare the values you want to the mock module.

// ...
jest.mock('unfetch', () => () => ({
  json: () =>
    Promise.resolve({
      status: 'ready',
      fuel: '100%',
      power: 100,
      sensors: [{ type: 'temp', value: 50, active: true }],
    }),
}));
// ...

Show code in action

There is also an alternative way to declare your module as an esModule.

// ...
jest.mock('unfetch', () => ({
  __esModule: true,
  default: () => ({
    json: () =>
      Promise.resolve({
        status: 'ready',
        fuel: '100%',
        power: 100,
        sensors: [{ type: 'temp', value: 50, active: true }],
      }),
  }),
}));
// ...

Show code in action

The first parameter is the modules name, and the second one is the factory method. You now have configured it to make the output values always be the same.

describe('Rocket Engine', () => {
  const cb = jest.fn();

  beforeEach(() => {
    cb.mockReset();
  });

  test('check engine response', async () => {
    const data = await startEngine();

    expect(data).toMatchObject({
      power: 100,
      fuel: '100%',
      status: 'ready',
      sensors: [{ type: 'temp', value: 50, active: true }],
    });
  });
});

Show code in action

Using the previous mock, you can check the results. When executing, the output should be the same as declared before.

$ npx jest mock.test.js

For a complete list of mock functions, see the reference docs.

Static Code Analysis in JavaScript

There are a ton of ways your program can break. JavaScript is a loosely typed language. The most common bugs are typos and incorrect types, like the wrong variable name or the sum operation of two strings instead of integers.

What is “Static Analysis”?

So, what does mean “static analysis” of code? The answer is:

Predicting defects in code without running it.

Since JavaScript is a scripting language, instead of the compiler running the code analysis, you need to use formatters and linters to get the job done.

Formatters

Formatters are tools that can fix any style inconsistencies it finds automatically. For this purpose, tools like Prettier or StandardJS can do the job. There are a couple of options to configure it to best match your criteria, and it can be integrated with the most popular editors and IDEs.

To show you how does it work, here is an example of an unformatted code:

function HelloWorld({greeting = "hello", greeted = '"World"', silent = false, onMouseOver,}) {

  if(!greeting){return null};

  // TODO: Don't use random in render
  let num = Math.floor (Math.random() * 1E+7).toString().replace(/\.\d+/ig, "")

  return <div className='HelloWorld' title={`You are visitor number ${ num }`} onMouseOver={onMouseOver}>

    <strong>{ greeting.slice( 0, 1 ).toUpperCase() + greeting.slice(1).toLowerCase() }</strong>
    {greeting.endsWith(",") ? " " : <span style={{color: '\grey'}}>", "</span> }
    <em>
      { greeted }
    </em>
    { (silent)
      ? "."
      : "!"}

  </div>;

}

After using prettier, here is the result:

$ npx prettier --write unformatted_code.jsx
function HelloWorld({ greeting = 'hello', greeted = '"World"', silent = false, onMouseOver }) {
  if (!greeting) {
    return null;
  }

  // TODO: Don't use random in render
  let num = Math.floor(Math.random() * 1e7)
    .toString()
    .replace(/\.\d+/gi, '');

  return (
    <div className="HelloWorld" title={`You are visitor number ${num}`} onMouseOver={onMouseOver}>
      <strong>{greeting.slice(0, 1).toUpperCase() + greeting.slice(1).toLowerCase()}</strong>
      {greeting.endsWith(',') ? ' ' : <span style={{ color: 'grey' }}>", "</span>}
      <em>{greeted}</em>
      {silent ? '.' : '!'}
    </div>
  );
}

As you can see, the main benefit is that you don’t need to worry about these minor inconsistencies anymore. It does that for you automatically.

Remember that you write code for the machine to interpret, but for humans to read.

The clearer and more consistent your code is, the easier it is to understand what is happening.

Linters

Code linting is a way to increase code quality. It analyzes the code and reports a list of potential code quality concerns. Currently, the most used tool for that is ESLint.

ESLint is a tool for identifying and reporting on patterns found in ECMAScript/JavaScript code

Let’s check our example:

function sayHello(name) {
  alert('Hello ' + name);
}

name = 'John Doe';
sayHello(name)

Show code in action

To use ESLint, you need to install it first. Then, open the terminal and type on your project folder.

$ npx eslint --init

Now, you can run ESLint on any file or directory, like in this example.

$ npx eslint unconsistent_code.js

The linter shows where are the errors in our code, based on a set of rules in the eslinrc.{js,json,yaml} file. You can also add, remove, or change any rules. For example, let’s add a rule to check if we are missing a semicolon.

...
rules: {
  semi: ['error', 'always']
},
...

When executed again, the result will show you an error with the new rule.

This was a simple example, but the bigger the project, the more it makes sense to use it and catch many trivial errors that could take some time if done manually.

There are some sets of rules that can be extended, so you won’t need to set them one-by-one like the recommended rules (" extends": "eslint:recommended"), and others made by the community like the Airbnb or Standard that you can include into your project.

For a complete list of rules, refer to the reference docs.

Conclusion

In this article, you’ve learned more about how to start adding tests to your program and understanding the foundations of testing in JavaScript, Jest, Mocking, and Static Code Analysis and that’s only the beginning. But don’t worry about that, the most important thing is to add your tests while you are coding, saving you from numerous problems in the future.

Become part of our own moon mission

View job openings

agility: is failing an option?

October 20th, 1995, the premiere of Apollo 13. After discussing several options, Ed Harris launches one of the most iconic sentences in the movie: “Failure is not an option!”. In fact, considering the possible outcome, failing was not an option. But what about real life? Our day-to-day. Should we always consider failing as not an option? Or should we embrace it as part of our lives? (Photo by Damir Babacic on Unsplash).

what is a failure?

Let’s start by defining failure. The dictionary states failure as:

  • lack of success;
  • a state of inability to perform a normal function;
  • omission of performing a duty or expected action.

In a way, we can consider failing everything that falls into the spectre of the unexpected. Something that deviates from preconceived plans.

So, why do we fear failing? Why fear the unexpected? Psychology can help with the answer. For instance, in the book Drive, the author states that we have intrinsic motivators. One of them is having a purpose, which can be translated to having objectives or goals. Achieving them can give us a sense of self-realisation. We are happy when we succeed. Regardless of individual definitions, we, as human beings, tend to thrive for success.


But failure leads to the exact opposite. It makes us feel as we did something wrong and need an explanation for it. A failure is proof that our perfect plans have flaws, that we are not as good as we thought. It makes us feel fragile.
Despite this, there are some trends encouraging failure. Why? If failure makes us feel bad, why push for it? Don’t we all want to achieve success? Seems counter-intuitive, but there is a good explanation for it.

why failing?

Failing may not seem the most exciting thing, but not accepting it can be worse. Failure tends to just happen. However, every failure brings something new: a new learning. And learning is what makes us evolve.


Failing exists so that we can learn more and evolve faster. Accepting failure takes out the fear of experimenting and fosters innovation. If Thomas Edison was afraid of failing, he wouldn’t have invented the light bulb. And according to the myth, he failed more than 10.000 times. Can you imagine a world where no electricity exists? Moreover, can you imagine yourself failing 10.000 times?


But we shouldn’t just accept failure. If we fail but disregard the learning process, we won’t evolve. Edison didn’t just fail, he “found 10,000 ways that won’t work”. Our ability to realize what is wrong is what makes us great.

The key is to understand the level of failure we can sustain to not despair and keep the motivation to try again.

dealing with failure

Looking into it, we fail so that we don’t fail again. Or at least to not repeat the same failure. The more we fail, the more we learn and the better we become.
Learning fast is key to becoming better. The way to learn fast is to fail faster. So, increasing the number of failures can be the right approach to learning more. The main question that arises is “how to fail more?”. The answer is, in theory, easy: reduce the size of failure. Putting that into practice is much harder. Let’s divide this into steps:

1

If you have an idea or a theory, think about the smallest thing you can do to prove it.

2

Always keep in mind the chances of failure are bigger than the chances of succeeding. It’s rare to succeed on the first try.

3

For each failure, have some key takeaways.

4

Repeat! Take into consideration the learnings, and do not make the same mistakes.

Keep in mind this is an oversimplification of the process. Without the correct mindset, it will be hard to reach success.

You’ll need to work on your resilience. Assure yourself that you will fail many times, expect it more often than achieving success. Never lose hope and celebrate the smallest achievements. Keep building your knowledge and keep pushing.
You’ll also need to think in simpler ways. Breaking down your experiments isn’t easy, but it’s the only way to better cope with the disappointment of a failure. Remember, creating high expectations, big plans, and developing big solutions increases the risk of a bigger disappointment. And bigger the pain, the less willing to try again. So, simplify everything as much as possible.


If you work on these concepts, your chances to become better may increase. There is no guaranteed success, but it’s a step closer.

failing is not an option

So, failing is really not an option, it’s mostly a certainty of life. Everyone, even the more experienced, is prone to fail. The only option you have is how to deal with it.
Failing makes us evolve, despite not being pleasant. There is a thin line that will help you keep your mental sanity and still learn and innovate.

If you want to know more about the art of failing, I recommend “Fail Fast, Fail Often: How Losing Can Help You Win“ by John D. Krumboltz and Ryan Babineaux. There you can find more tips on how to deal with failure and how you should embrace it. It’s written by professionals in psychology, so they’ll probably know more about it than I do.

WANT TO FAIL & EVOLE WITH US?

View job openings