Writing and Testing a stdin script with TypeScript

Hello there, folks, how are you?

One of my motivations for writing texts here in dev.to is to compile information which took me a lot of effort to find in one place.

So, one of these days I was writing a small application in TypeScript that used st…


This content originally appeared on DEV Community and was authored by Laura Viglioni

Hello there, folks, how are you?

One of my motivations for writing texts here in dev.to is to compile information which took me a lot of effort to find in one place.

So, one of these days I was writing a small application in TypeScript that used stdin from the terminal

ts-node main.ts < input.txt

Everything went well until I had to test it. I spent more time than I'd like trying to find out how I would test this input. I tried a lot of different stuff that I saw on the internet but only one of them worked and this is the solution I'll present in this text.

Code Example

First of all, an example of a script in typescript that receives stdin line by line and terminates when an empty line is entered:

// main.ts
import * as readline from 'node:readline'
import { stdin as input, stdout as output } from 'node:process'

type RL = readline.Interface
type SomeFunction = (rl: RL) => (line: string) => void

const someFunction : SomeFunction = rl => line => {
  if (line === '') {
    rl.close()
  }

 /*
  * Do something with `line`
  */
  const result = // doSomething(line)

  console.log(result)
}


export const main = (): void => {
  const rl = readline.createInterface({ input, output })

  console.log("Please insert the data.")

  // reads line by line and calls someFunction(rl)(line)
  rl.on('line', someFunction(rl))
}

You can check out the readline docs

Preparing the project for testing

The question is: how would we test our main function that only calls our someFunction?

If we mock readline, we wouldn't be testing our app, we need to mock the stdin to get a realistic simulation of what our program is doing.

For that, we will use jest. In this particular project, these are my dependencies inside package.json:

  "devDependencies": {
    "@babel/core": "^7.16.0",
    "@babel/preset-env": "^7.16.4",
    "@babel/preset-typescript": "^7.16.0",
    "@types/jest": "^27.0.3",
    "@types/node": "^16.3.1",
    "babel-jest": "^27.4.2",
    "jest": "^27.4.3",
    "mock-stdin": "^1.0.0",
    "typescript": "^4.3.5",
    // ...
  },

The other config files:

// jest.config.ts
export default {
  clearMocks: true,
  testMatch: ['**/test/**/*.spec.ts'],
}


// babel.config.js
module.exports = {
  presets: [
    ['@babel/preset-env', {targets: {node: 'current'}}],
    '@babel/preset-typescript',
  ],
};

// tsconfig.json
{
  "compilerOptions": {
    "module": "commonjs",
    "moduleResolution": "node",
    "declaration": true,
    "removeComments": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "allowSyntheticDefaultImports": true,
    "target": "ES2019",
    "lib": ["ES2019"],
    "sourceMap": true,
    "outDir": "./dist",
    "incremental": true,
    "esModuleInterop": true,
    "strict": true
  },
  "include": ["./src/**/*", "./test/**/*"],
  "exclude": ["node_modules"]
}

Testing

The package that does the trick is mock-stdin and is very simple to use:

// 1. import the lib
import mockStdin from 'mock-stdin'

// 2. start it 
const stdin = mockStdin.stdin()

// 3. use it
stdin.send("some input")

// 4. end it
stdin.end()

Here is an example of a test for our main function:

import mockStdin from 'mock-stdin'
import { main } from '../../src/main'

// mock console.log
console.log = jest.fn()

describe('main.ts', () => {
  let stdin: ReturnType<typeof mockStdin.stdin>

  // just a helper function to start the application
  // and mock the input
  const execute = (input: string): void => {
    main()
    stdin.send(input)
    stdin.end()
  }

  beforeEach(() => {
    stdin = mockStdin.stdin()
  })

  describe('when input is valid', () => {
    const input = // something
    const expectedResult = // another thing

    beforeEach(() => {
      execute(input)
    })

    it('should print the correct output', () => {
      expect(console.log).toBeCalledWith(expectedResult)
    })
  })

  // another describe blocks

 }

That's it, folks, I hope this text helps you somehow!
Bye bye

Use masks (yes!) and use emacs
xoxo

covidVaccines.forEach(takeYourShot)

Cover photo: Photo by Sigmund


This content originally appeared on DEV Community and was authored by Laura Viglioni


Print Share Comment Cite Upload Translate Updates
APA

Laura Viglioni | Sciencx (2022-05-31T02:59:39+00:00) Writing and Testing a stdin script with TypeScript. Retrieved from https://www.scien.cx/2022/05/31/writing-and-testing-a-stdin-script-with-typescript/

MLA
" » Writing and Testing a stdin script with TypeScript." Laura Viglioni | Sciencx - Tuesday May 31, 2022, https://www.scien.cx/2022/05/31/writing-and-testing-a-stdin-script-with-typescript/
HARVARD
Laura Viglioni | Sciencx Tuesday May 31, 2022 » Writing and Testing a stdin script with TypeScript., viewed ,<https://www.scien.cx/2022/05/31/writing-and-testing-a-stdin-script-with-typescript/>
VANCOUVER
Laura Viglioni | Sciencx - » Writing and Testing a stdin script with TypeScript. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2022/05/31/writing-and-testing-a-stdin-script-with-typescript/
CHICAGO
" » Writing and Testing a stdin script with TypeScript." Laura Viglioni | Sciencx - Accessed . https://www.scien.cx/2022/05/31/writing-and-testing-a-stdin-script-with-typescript/
IEEE
" » Writing and Testing a stdin script with TypeScript." Laura Viglioni | Sciencx [Online]. Available: https://www.scien.cx/2022/05/31/writing-and-testing-a-stdin-script-with-typescript/. [Accessed: ]
rf:citation
» Writing and Testing a stdin script with TypeScript | Laura Viglioni | Sciencx | https://www.scien.cx/2022/05/31/writing-and-testing-a-stdin-script-with-typescript/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.