Jim's
Tutorials

Fall 2018
course
site

Testing Week 7

Unit testing an Airplane

So now I need to try out unit testing with Rspec. I need to have a spec file for every class file, and a spec file is called a unit test, unit tests are for individual classes. I read about that in this stack overflow conversation and it mentions that unit tests allow for granular-testing. I guess sometimes developers test too large a unit sometimes. They said unit tests should test individual behaviors. I read this wiki about inversion control in regards to that, and making sure your unit tests don't become integration tests. Not sure I entirely understand that but just going to get started. It seems like they are just saying to carefully limit the scope of your unit tests to remove dependancies from you code. This stack overflow conversation helped me understand it. note to self: ask about this

I made this test in "airplane_spec.rb"

require_relative "../../lib/airplane"

describe Airplane do
  describe '#flying?' do
    context 'when the plane is flying' do
      it 'returns true' do
        expect(Airplane.new.flying?).to eq true
      end
    end

    context 'when the plane is not flying' do
      it 'returns false' do
        expect(Airplane.new.flying?).to eq false
      end
    end
  end
end


and in "airplane.rb" I started out with code, even though I knew it wouldn't pass.


class Airplane    
end

OUTPUT:

FF

Failures:

  1) Airplane#flying? when the plane is flying returns true
     Failure/Error: expect(Airplane.new.flying?).to eq true

     NoMethodError:
       undefined method `flying?' for #<Airplane:0x007fdb6b338af8>
     # ./spec/lib/airplane_spec.rb:7:in `block (4 levels) in <top (required)>'

  2) Airplane#flying? when the plane is not flying returns false
     Failure/Error: expect(Airplane.new.flying?).to eq false

     NoMethodError:
       undefined method `flying?' for #<Airplane:0x007fdb6a08c800>
     # ./spec/lib/airplane_spec.rb:13:in `block (4 levels) in <top (required)>'

Finished in 0.00658 seconds (files took 0.40809 seconds to load)
2 examples, 2 failures

Failed examples:

rspec ./spec/lib/airplane_spec.rb:6 # Airplane#flying? when the plane is flying returns true
rspec ./spec/lib/airplane_spec.rb:12 # Airplane#flying? when the plane is not flying returns false

This means, yeah, I have an undefined method flying? and I also need to be able to set two airplanes to either flying or not. Let me go ahead and do that.


class Airplane
    attr_accessor :flying
def initialize(status)
    if
        status == 'flying'
        @flying = true
    else
        @flying = false
    end
end

def flying?
    @flying
end
end


SPEC:

require_relative "../../lib/airplane"

describe Airplane do
  describe '#flying?' do
    context 'when the plane is flying' do
      it 'returns true' do
        expect(Airplane.new('flyin').flying?).to eq true
      end
    end

    context 'when the plane is not flying' do
      it 'returns false' do
        expect(Airplane.new('grounded').flying?).to eq false
      end
    end
  end
end

OUTPUT:

rspec
F.

Failures:

  1) Airplane#flying? when the plane is flying returns true
     Failure/Error: expect(Airplane.new('flyin').flying?).to eq true

       expected: true
            got: false

       (compared using ==)
     # ./spec/lib/airplane_spec.rb:7:in `block (4 levels) in <top (required)>'

Finished in 0.02943 seconds (files took 0.16584 seconds to load)
2 examples, 1 failure

Failed examples:

rspec ./spec/lib/airplane_spec.rb:6 # Airplane#flying? when the plane is flying returns true

..SOOO I have a failure here... what happened... yeah, I spelled flying wrong, ok! so I fixed that and the test is passing 2 examples.

Unit Testing My Girlfriend

Lets try something with a bit more complexity than an airplane- my gf.

SPEC!

require_relative './gf_unit_test/gf.rb'


describe Girlfriend do
    describe '#pissed?' do
        context 'when I say something stupid' do
            it 'returns true' do
                expect(Girlfriend.new('pissed').pissed?).to eq true

               end
        end

        context 'when I dont say something stupid' do
            it 'returns false' do
                expect (Girlfriend.new('happy').pissed?).to eq false
            end
        end
    end
end


CODE!

class Girlfriend
    attr_accessor :pissed
    def initialize(mood)
        if
            mood == 'pissed'
            @pissed = true
        else
            @pissed = false
        end



...and it NOT WORKING. it WON"T READ THE FILE. I need help getting this to work.. not reading file no matter what I do.


ok, I did get it working but it's really stupid. So I'd put my spec and code files in the same folder.. then I tried running "rspec" and it wasn't finding the file. So it took both stating require_relative 'gf.rb' AND saying rspec gf_spec.rb in terminal specifically, for it to find it. I guess the default "rspec" looks for a file in a spec folder... HOW would i ever have known that. I changed my file structure before i figured it out, and put my spec file in a spec folder and it worked so I put two and two together. jeez.

here's how i had to arrange my files to get it to work before figuring out that default thing:

tree
.
├── gf.rb
└── spec
    └── gf_spec.rb

OUTPUT

gf_unit_test git:(master) ✗ rspec
..

Finished in 0.00654 seconds (files took 0.19184 seconds to load)
2 examples, 0 failures

lets make another about the gf

CODE

class Larisa

    def initialize(plan)
        if
            plan == 'travel'
            @plan = true
        else
            @plan = false
    end
end

def plan?
    @plan
end
end

SPEC:

require_relative '../larisa'

describe Larisa do
    describe '#plan?' do
        context 'when larisa is making plans' do
            it 'returns true' do
            expect(Larisa.new('travel').plan?).to eq true
        end
    end

    context 'when larisa makes no plans' do
        it 'returns false' do
        expect(Larisa.new('stay home').plan?).to eq false
    end
end
end
end

OUTPUT:

rspec
.F

Failures:

  1) Larisa#plan? when larisa makes no plans returns false
     Failure/Error: expect(larisa.new('stay home').plan?).to eq false

     NameError:
       undefined local variable or method `larisa' for #<RSpec::ExampleGroups::Larisa::Plan::WhenLarisaMakesNoPlans:0x007f8e319313b0>
     # ./spec/larisa_spec.rb:13:in `block (4 levels) in <top (required)>'

Finished in 0.01059 seconds (files took 0.27478 seconds to load)
2 examples, 1 failure

OK so... about this- I had a few variable and spelling mixups, I initialized plan? and was using plans?, etc. Good practice for repetition. Will do this again.

now trying a different kind of test- to be_a(whatever)

so theres a another kind of test I tried. first I made a card class with a couple variables initialized and passed through:

class Card
    attr_accessor :suit, :number

    def initialize(suit, number)
        @suit = suit
        @number = number

    end

    def facecheck
        if @number == 'jack' || @number == 'king' || @number == 'queen'
            return true
        else
            return false
        end
    end

    def checker
        if @suit == 'ace'
            return true
        else
            return false

        end
    end
end

..Basically just wanted to make something easy-ish to test. Then I made a test that checks to see if the card drawn is a facecard (this file is called dance because I originally was going to make something else so just ignore that:

require_relative '../dance'

describe Card do
    describe 'facecheck' do
        context 'checks to see if the card drawn is a face card' do
            it 'returns true' do
            expect(Card.new('club', 'jack').facecheck).to eq true
            end

            it 'returns false if not a face card' do
            expect(Card.new('diamond', 'sandy').facecheck).to eq false
            end
        end
    end
end

OUTPUT:

.F

Failures:

  1) Card facecheck checks to see if the card drawn is a face card returns false if not a face card
     Failure/Error: expect(Card.new(10, 'sandy').facecheck).to eq false

       expected: false
            got: true

       (compared using ==)
     # ./spec/dance_spec.rb:11:in `block (4 levels) in <top (required)>'

Finished in 0.03972 seconds (files took 0.23347 seconds to load)
2 examples, 1 failure

Failed examples:

rspec ./spec/dance_spec.rb:10 # Card facecheck checks to see if the card drawn is a face card returns false if not a face card

So here's what happened. Initially I had something in my code that looked like this:

def facecheck
        if @number == 'jack' || 'king' || 'queen'
            return true
        else
            return false

but this was making my test always return true because it was not checking to see if EXACTLY king and queen were passed through, it was looking at whether a string was passed through so it was returning true all the time. it should have looked like this:

def facecheck
        if @number == 'jack' || @number == 'king' || @number == 'queen'
            return true
        else
            return false
        end
    end

And so that made my test pass.

Then I worked on a few more to test out my card class.

describe Card do
    let(:blah) {Card.new('club', 'king')}

    describe '.new' do
        it 'takes two arguments as arguments' do
            expect(blah).to be_a(Card)
        end
    end

...This one just tests to see if a card is made when I pass two arguments through, and indeed it is. This is the first time I used to be_a..pretty exciting.

I also made the very similar test:

describe 'club?' do
    it 'will be a suit that is a club' do
    expect(blah.suit).to eq('club')

        end
    end

Here I was just practicing making stacked tests using the same variable :blah. I tried making one more that I didn't get to work:

describe 'facecheck?' do
        it 'will be a facecard' do
            expect(blah.suit).to be_a('club')
        end
    end

OUTPUT

rspec
....F

Failures:

  1) Card facecheck? will be a facecard
     Failure/Error: expect(blah.suit).to be_a('club')

     TypeError:
       class or module required
     # ./spec/dance_spec.rb:48:in `block (3 levels) in <top (required)>'

Finished in 0.00844 seconds (files took 0.18251 seconds to load)
5 examples, 1 failure

Failed examples:

rspec ./spec/dance_spec.rb:47 # Card facecheck? will be a facecard

I know I'm getting my variables mixed up, will speak to nate or something.

Reflection on my unit tests

So it seems like I spent as much time messing with getting rspec to read my stuff as I did actually trying to get the tests to pass. Another question. I'm not sure why I need the accessor AND the def to get this thing to work.. I dunno, I read this about filepaths and it did not answer my question.

...OK so update, I understand why I need the attr_accessor AND the @variable = variable... That's because the attr_accessor makes it so I don't need to RETURN anything while I'm making other methods in my class but I still need to initialize my @variable in the first place. I think that's the answer. (see below for what I'm referring to)

class Card
    attr_accessor :suit, :number

    def initialize(suit, number)
        @suit = suit
        @number = number