Created
September 11, 2018 15:03
-
-
Save nebez/2b5e6fcc34c5197f6197ddcd7afbbb32 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { expect } from 'chai'; | |
import * as Str from 'src/Support/String'; | |
const app: any = {}; | |
const otherMumboJumbo: any = {}; | |
const validStatusCode = 200; | |
const invalidStatusCode = 500; | |
describe('String utility', () => { | |
describe('slugify', () => { | |
// This test fails too early and the output is not informative enough. | |
// If there are tons of test cases, let's refactor this into a "data | |
// provider"-esque test so we can see the pass/fail of each. | |
it('should work on all string inputs', () => { | |
// Let's test some ascii as well as some non-ascii encodings. | |
expect(Str.slugify('this is a test input')).to.equal('this-is-a-test-input'); | |
expect(Str.slugify('thiś iś à teśt input')).to.equal('this-is-a-test-input'); | |
expect(Str.slugify('Ťhîś îś even more')).to.equal('this-is-even-more'); | |
expect(Str.slugify('ŠoḾe MØRE')).to.equal('some-more'); | |
}); | |
// This test has a garbage failure message. | |
it('should coalesce consecutive hyphens', () => { | |
expect(Str.slugify('coalesce me') === 'coalesce-me').to.equal(true); | |
}); | |
}); | |
describe('prepareForQuery', () => { | |
// This test does not let the writer of the test enforce the output. | |
// It is a fragile test that, when it fails, provides no confidence or | |
// hints at what the real biz-requirement-determined output should be. | |
it('should encode special characters after slugification', () => { | |
// According to my biz requirements, an input can only be | |
// "prepped" for a query parameter after it has been both | |
// slugified and percent-encoded (in that order). | |
const input = 'Contact us at support@ssense.com'; | |
const expectedCleanedInput = encodeURIComponent(Str.slugify(input)); | |
expect(Str.prepareForQuery(input)).to.equal(expectedCleanedInput); | |
}); | |
}); | |
describe('validateUrl', () => { | |
it('should validate both URL schemas', () => { | |
expect(() => Str.validateUrl('http://example.com')).to.not.throw(); | |
expect(() => Str.validateUrl('https://example.com')).to.not.throw(); | |
}); | |
// This is a bad example of a test that only checks if the method | |
// throws, but not -what- it throws. The issue here is that the | |
// function takes an array of strings too but will throw on the first | |
// string occurrence. Display the problems here with first changing | |
// the input to an array and showing that the function throws. Add | |
// strict error message checking but then change the message to show | |
// that's not enough because it's too coupled to the implementation. | |
// Then finally use a combination of the Error constructor check + a | |
// regex. | |
it('should throw errors for invalid URLs', () => { | |
expect(() => Str.validateUrl('foo://example.com')).to.throw(); | |
}); | |
}); | |
}); | |
describe.skip('My web app', () => { | |
// The problem with this test is multi tiered. | |
// Firstly, the use of constants declared outside of the scope of the | |
// tests is problematic. This is one (of many) ways to introduce global | |
// state and leaky tests. You may have heard of the "runInBand" option | |
// provided by Jest which lets tests run serially in the same node | |
// process. While running in separate processes avoids test suites leaking | |
// into eachother, this is a bad idea to rely on the implementation | |
// details of your test runner to prevent you from writing leaky state. | |
// Secondly, using a named constant at the top-level to define something | |
// easily inferrable at the test level is bad (validStatusCode and | |
// invalidStatusCode). We know what good at bad HTTP status codes are. | |
// Inline them. | |
// Thirdly, using constants within a test to de-duplicate an identifier is | |
// unnecessary for these kinds of small examples. Having the magic number | |
// 99 within the test is very obvious that it's purposeful. The exception | |
// to this rule is when the magic number is something common like 0 or 1. | |
// This can make use of a good variable named "BrandIdentifier" or | |
// something. | |
// And finally, the test is all over the place. Tests should be focused. | |
// We're bootstrapping the application and have lost some contextual space | |
// here to that. We're running an assertion on a useless property, aka the | |
// length of the products array returned. This is not the point of the | |
// test according to the name, we care about HTTP status codes. Then the | |
// test proceeds to hit two different endpoints. | |
it('should return appropriate status codes for valid requests', async () => { | |
const goodBrandId = 99; | |
otherMumboJumbo.setupRoutes(require('routes.json')); | |
otherMumboJumbo.setupMiddleware(require('middleware.js')); | |
app.addMumbuJumbo(otherMumboJumbo); | |
await app.initialize(); | |
const res = await app.get(`/products?brand_id=${goodBrandId}`); | |
expect(res.status).to.equal(validStatusCode); | |
expect(res.body.meta.brand).to.equal(goodBrandId); | |
expect(res.body.products.length).to.be.greaterThan(1); | |
const res2 = await app.get(`/products?brand_id=`); | |
expect(res2.status).to.equal(invalidStatusCode); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment