Kategorier
Code testing

Tips and tricks for testing with Jest

Writing tests can be daunting when starting out, it’s hard to know exactly what to test and then learning the API for your test tool.

I wanted to share some small tips that can be useful when starting out.

expect.objectContaining()

In some cases you are only interested in the value of one or just a few properties in an object. To check for a specific property you can use expect.objectContaining to check if the object contains a property with the expected value.

In the code below we’re checking if a report dialog function has been called with the users name and email.
The actual object is much larger but we don’t really care about the other properties, in this case the user information is the moving parts in the object.

expect(showReportDialog).toHaveBeenCalledWith(
  expect.objectContaining({
    user: {
      name,
      email,
    }
  })
);

expect.anything()

Callback functions or randomly generated values can sometimes be a hassle to handle in tests since they might change, but it is possible to ignore specific properties or arguments using expect.anything.

function loadScript(scriptUrl:string, callback:() => unknown) { ... }

When testing the above function we ’re not interested in the callback function and only wants to check if loadScript have been called with the correct script.

it("should call loadScript", () => {
  someFunctionUsingLoadScript();

  expect(loadScript).toHaveBeenCalledWith(
    "script.js",
    expect.anything()
  );
}

expect.anything does not match null or undefined values

expect.any()

Another way to match more broadly is expect.any(constructor) where you can accept any match based on the constructor being passed to it.

expect(someFn).toHaveBeenCalledWith({
  someNumber: expect.any(Number),
  someBoolean: expect.any(Boolean),
  someString: expect.any(String)
});

expect.assertions()

When doing asynchronous tests it can be helpful to make sure that all assertions have been run when the test ends.
The expect.assertions(Number) ensures that the correct number of assertions have been made.

test('prepareState prepares a valid state', () => {
  expect.assertions(1);
  prepareState((state) => {
    expect(validateState(state)).toBeTruthy();
  })
  return waitOnState();
})

test.each

For some unit tests you may want run the same test code with multiple values. A great way to do this is using the test.each function to avoid duplicating code.

Inside a template string we define all values, separated by line breaks, we want to use in the test. The first line is used as the variable name in the test code.

test.each`
  someId
  ${undefined}
  ${null}
  ${""}
`("$someId should reject promise", async ({ someId}) => {
  expect.assertions(1);
  await expect(someFn(someId))
    .rejects.toEqual(errorMessage);
});

Multiple input variables can be added separated by the pipe (|) character.

test.each`
  someId       | anotherValue
  ${undefined} | ${a}
  ${null}      | ${b}
  ${""}        | ${c}
`("$someId with $anotherValue should reject promise", async ({ someId, anotherValue }) => {
  expect.assertions(1);
  await expect(someFn(someId, anotherValue))
    .rejects.toEqual(errorMessage);
});

Note: it is also possible to define the values as arrays, read more in the official documentation.

jest.requireActual

This is just a reminder never to forget adding jest.requireActual when mocking libraries. If you do forget, it can lead to weirdness that may take several hours to solve (talking from personal experience here 😁).

So what does it do?

When mocking a library you may only want to mock a specific function of the library and keep the rest of the library intact.

jest.mock("@material-ui/core", () => ({
  ...jest.requireActual("@material-ui/core"),
  useMediaQuery: jest.fn()
}));

So in the code above we create a new mock object, using jest.requireActual to spread all the functions of the library and only mock useMediaQuery in this case.

Kategorier
Frontend

How to add an RSS-feed to your Next.js site

I love RSS-feeds (and still curse Google for cancelling Google Reader) and use them as my main news source for things I find interesting so with this article I would like to help people to add RSS-feeds to their blogs.

If you read my article about how to add a sitemap.xml to your next.js site you will recognise much of the code, it’s pretty much the same base but with slightly different XML-markup.

Creating the page

First off we need a page that can return the XML. I suggest you name it ”rss”, ”feed” or something similar.

In getInitialProps we get our blog posts and set the ”Content-Type”-header to ”text/xml” so the browser knows that should expect XML instead of HTML.
We then generate the XML and passes it on to the browser using res.write.

export default class Rss extends React.Component {
  static async getInitialProps({ res }: NextPageContext) {
    if (!res) {
      return;
    }
    const blogPosts = getRssBlogPosts();
    res.setHeader("Content-Type", "text/xml");
    res.write(getRssXml(blogPosts));
    res.end();
  }
}

Generating the base XML for the feed

For the base XML document you will need to add some basic information like the title of the log, a short description, link to your website and the language of the content.
Title, link and description are mandatory elements in the RSS specification but add as many optional elements as you find useful.

const getRssXml = (blogPosts: IBlogPost[]) => {
  const { rssItemsXml, latestPostDate } = blogPostsRssXml(blogPosts);
  return `<?xml version="1.0" ?>
  <rss version="2.0">
    <channel>
        <title>Blog by Fredrik Bergqvist</title>
        <link>https://www.bergqvist.it</link>
        <description>${shortSiteDescription}</description>
        <language>en</language>
        <lastBuildDate>${latestPostDate}</lastBuildDate>
        ${rssItemsXml}
    </channel>
  </rss>`;
};

Adding XML for the blog items

With the basic stuff out of the way all we need to go is generate some XML for the blog posts and figure out when the blog was updated.

The item element should at least contain a link to the blog post, the date when it was published and the text. You can either opt to put a short description and force the user to come visit your page or put the whole post in the XML.

If you have HTML-markup in your text you need to enclose it within a <![CDATA[${post.text}]]>-tag or HTML encode the text.

const blogPostsRssXml = (blogPosts: IBlogPost[]) => {
  let latestPostDate: string = "";
  let rssItemsXml = "";
  blogPosts.forEach(post => {
    const postDate = Date.parse(post.createdAt);
    if (!latestPostDate || postDate > Date.parse(latestPostDate)) {
      latestPostDate = post.createdAt;
    }
    rssItemsXml += `
      <item>
        <title>${post.title}</title>
        <link>
          ${post.href}
        </link>

        <pubDate>${post.createdAt}</pubDate>
        <description>
        <![CDATA[${post.text}]] >
        </description>
    </item>`;
  });
  return {
    rssItemsXml,
    latestPostDate
  };
};

Finishing up

The last thing you need to do is to add a link to your feed somewhere in the head of your document.

<link 
  rel="alternate" 
  type="application/rss+xml" 
  title="RSS for blog posts" 
  href="https://www.bergqvist.it/rss" />

This will make it easier for browsers and plugins to auto discover your feed.

The code is available as a gist on github and please leave a comment with any feedback.

Kategorier
conference

SmashingConf New York 2019

A few weeks ago I had the privilege of attending SmashingConf in New York, travelling from a chilly Swedish autumn to a quite pleasant New York end of summer. I spent the weekend fighting jet lag by taking long walks and touristing so I could be fit for the first workshop I had signed up for.

TL;DR

Great conference but not technical enough for my taste.

Watch Wes Bos’s talk (slides) for good javascript insights and all talks can be found here: SmashingConf NY 2019 videos.

Lightning fast performance with Scott Jehl.

Spending a whole day digging deep into performance sounded like a great way to spend a Monday so I had booked.
The venue for the workshop was located at Microsoft, just a short walk from my hotel so I had plenty of time to get a good nights sleep, eat a steady breakfast and still make the 9 o’clock start without any problems.

Scott Jehl was really easy going and the workshop covered tools to measure performance and how to mitigate issues.
I would however have liked more nitty gritty details and less time spent on using online tools.

The Move Fast & Don’t Break Things-talk that Scott held on the second day of the conference was a distilled version of the workshop so check it out if you’re interested.

In the evening I attended a Shopify-hosted jam session with lightning talks from Shopify, Microsoft, Forestry (about TinaCMS!) and a few others talkers.

Conference day one

The conference took place at the New world stages close to time square.
The main stage was a musical theatre which worked well but the rest of the venue was quite cramped.

Many of the talks were more of the UX/inspirational kind which can be nice, but I’m more of a hands on person so my favourites were the following.

Marcy Sutton on Garbage Pail Components is a talk about accessibility with an 80’s twist (and not even the first 80’s flashback this conference).

Wes Bos on Slam Dunk Your JavaScript Fundamentals is jam packed with new APIs that you might not have heard of yet, especially the Intl-object.

In the evening the SmashingConf party took place at Waylon bar where speakers and participants got to mingle. I met lots of friendly people from both sides of the pond and had a great time.

Conference day two

With a slight hangover and not enough sleep I went to see the mystery speaker dina Amin who makes wonderful stop-motion shorts with old discarded electrical devices.

Another fun talk was the Using a Modern Web to Recreate 1980s Horribly Slow & Loud Loading Screen-talk where Remy Sharp recreated the loading screens of his old 80’s Sinclair Spectrum computer. All ending with a photo of the audience being transformed to the Spectrum image format and shown on the big screen

Next.js with Remy Sharp

With the conference over I still had a workshop day to look forward to so I went back to the Microsoft office to learn some next.js!

The workshop was good with all the basics of next.js covered, I had already tinkered with next.js (my homepage runs for example) so most things were familiar but luckily I had not read up on the changes in 9.1 so I did learn some new tricks.

All in all I had a great week in NYC and I met lots of great people but I would have liked if the conference and workshops were a bit more technical.