Search code examples
javascriptjasmine

Jasmine/JavaScript: Test that a new object was created


My goal it to assert that new Bassist() was called by the start() method and create a mock object, instead.

This the second test is currently failing with the error "Expected spy constructor to have been called."

class Bassist {
  constructor() {
    console.log('bassist joined')
  }
}

class Drummer {
  constructor() {
    console.log('drummer joined')
  }
}

class Band {
  constructor(name) {
    this.name = name
    this.bassist = null
    this.drummer = null
  }

  start() {
    this.bassist = new Bassist()
    this.drummer = new Drummer()
  }
}

describe('Band', () => {
  describe('start()', () => {
    it('sets the bassist property', () => {
      let band = new Band('The Foobars')
      band.start()
      expect(band.bassist).not.toBeNull()
    })

    it('creates a new bassist', () => {
      spy = spyOn(Bassist.prototype, 'constructor')
      band = new Band('The Foobars')
      band.start()
      expect(spy).toHaveBeenCalled()
      console.log('expect was called')
    })
  })
})
<!doctype html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>Jasmine Specs</title>

  <!-- Jasmine v2.5.2 -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.5.2/jasmine.css"></link>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.5.2/jasmine.js"></script>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.5.2/jasmine-html.js"></script>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.5.2/boot.js"></script>

</head>

<body>
</body>

</html>


Solution

  • After a lot of trial and error (and searching), I wasn't able to override the call to new Bassist() and replace it with a spy object. Instead, I created a separate method within the Band class to handle Bassist object creation, and then put a spy on it.

    Who knows if this is good practice, but it works for me.

    class Bassist {
      constructor() {
        console.log('bassist joined')
      }
    }
    
    class Band {
      constructor(name) {
        this.name = name
        this.bassist = null
      }
    
      start() {
        this.bassist = this.createBassist()
      }
    
      createBassist() {
        return new Bassist()
      }
    }
    
    describe('Band', () => {
      let band, createBassistSpy, bassistSpy
    
      beforeEach(() => {
        // setup spies
        bassistSpy = jasmine.createSpyObj('bassist', ['shred', 'quit', 'otherSickMethod'])
        createBassistSpy = spyOn(Band.prototype, 'createBassist')
         .and.returnValue(bassistSpy)
    
        // init subject being tested
        band = new Band('The Foobars')
      })
    
      describe('new', () => {
        it('initializes bassist to null', () => {
          expect(band.bassist).toBe(null)
        })
      })
    
      describe('start()', () => {
        it('calls the createBassist() method', () => {
          band.start()
          expect(createBassistSpy).toHaveBeenCalled()
    
          // Not a great test, but ensures we are working with the
          // bassistSpy object, and not something else.
          expect(band.bassist).toEqual(bassistSpy)
        })
    
        it('sets the bassist property', () => {
          band.start()
          expect(band.bassist).not.toBeNull()
        })
      })
    })
    <!doctype html>
    <html lang="en">
    
    <head>
      <meta charset="utf-8">
      <title>Jasmine Specs</title>
    
      <!-- Jasmine v2.5.2 -->
      <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.5.2/jasmine.css"></link>
      <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.5.2/jasmine.js"></script>
      <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.5.2/jasmine-html.js"></script>
      <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.5.2/boot.js"></script>
    
    </head>
    
    <body>
    </body>
    
    </html>