Ruby Minitest'之前'出人意料地表演

时间:2014-08-02 17:29:07

标签: ruby unit-testing minitest

我正在为Ruby中的文本冒险编写一些测试。他们一切都很好,除了一半失败的人。这些是我的测试:

load "./conquest"

$options[:save_file] = File.expand_path("~/.conquest_tests")
$options[:reset] = true
$options[:no_save] = true

require "stringio"
require "minitest/autorun"

def prompt(*a) "name" end

describe Delegate do

    before do
        @out = StringIO.new
        $stdout = @out
        @delegate = Delegate.new
        @player = @delegate.player
        @out.truncate(0)
    end

    after do
        $stdout = STDOUT
    end

    describe "invalid command is entered" do
        it "must warn user" do
            @delegate.parse("not valid")
            @out.string.wont_equal ""
        end
    end

    describe "command is empty" do
        it "must do nothing" do
            @delegate.parse("")
            @out.string.must_equal ""
        end
    end

    describe "it keeps time" do
        it "must increase the time" do
            start = @player.total_seconds
            sleep 3
            done = @player.total_seconds
            done.must_be :>, start
        end

        it "must give an achievement after 10 minutes" do
            @player.total_seconds = 599
            refute(@player.achievements[:ten_minutes].unlocked)
            @player.total_seconds = 601
            @delegate.check_time
            assert(@player.achievements[:ten_minutes].unlocked)
        end
    end

    describe "quests command" do
        it "must list quests" do
            @player.quests[:mordor].start
            @delegate.parse("quests")
            regex = /started quests:.+current task:.+mordor/mi
            @out.string.must_match regex
        end
    end

    describe "achievements command" do
        it "must list achievements" do
            @player.achievements[:mordor].unlock
            @player.achievements[:peach].unlock
            @delegate.parse("achievements")
            regex = /(mordor|peach)+/mi
            @out.string.must_match regex
        end
    end
end

describe Player do

    before do
        @out = StringIO.new
        $stdout = @out
        @player = Delegate.new.player
        @out.truncate(0)
    end

    after do
        $stdout = STDOUT
    end

    describe "new game" do
        it "must have no unlocked achievements" do
            $stderr.puts @player.total_seconds
            $stderr.puts @player.achievements.map {|k, a| "#{k}: #{a.unlocked}"}
            good = @player.achievements.none? { |_, a| a.unlocked }
            assert(good)
        end
    end

end

最后一次测试失败的时间大约是一半。这就是它失败时的说法:

1) Failure:
Player::new game#test_0001_must have no unlocked achievements [./tests:97]:
Failed assertion, no message given.

我注意到这是因为这个测试就在这里:

it "must give an achievement after 10 minutes" do
    @player.total_seconds = 599
    refute(@player.achievements[:ten_minutes].unlocked)
    @player.total_seconds = 601
    @delegate.check_time
    assert(@player.achievements[:ten_minutes].unlocked)
end

当第一次测试时,另一次测试通过。但那是在委托测试中。为什么@player仍然有成就?即使我把测试放在那里,它应该创建一个新代表的新玩家吗?

我不明白为什么会这样。任何人都可以帮我解释或向我解释这个吗?谢谢!如果你需要在这里看到游戏本身:https://github.com/INOVA-Technology/conquest

修改

继承我的代表班:

班级代表

    attr_accessor :player, :timer

    def initialize
        load_game
        keep_time
    end

    def parse(input)
        check_time unless $options[:loading]
        save_command = true
        directions = "up|down|north|east|south|west|u|d|n|e|s|w"

        # this would be part of a regex, like directions. ex. "uppercut|slash|attack"
        special_attacks = (@player.weapon.nil? ? "dontMakeAnAttackNamedThis" : @player.weapon.regex_attacks)
        # input will always be converted to lower case before getting here
        case input
        when /^(?<direction>(#{directions}))$/
            direction = $~[:direction]
            walk(direction)
        when /^(go|walk)( (?<direction>#{directions}|to mordor|to merge conflictia))?$/
            if direction = $~[:direction]
                walk(direction)
            else
                puts "#{input.capitalize} where?"
            end
        when /^(get|take|pickup|pick up)( (?<item>[a-z ]+))?$/
            item = convert_input($~[:item])
            pickup(item)
        when /^look( (at )?(?<item>[a-z]+))?$/
            item = convert_input($~[:item])
            item.nil? ? look : inspect(item)
        when /^(talk|speak)( to)?( (?<guy>[a-z ]+))?$/
            if guy = convert_input($~[:guy])
                talk(guy)
            else
                puts "Who?"
            end
        when /^buy( (?<item>[a-z ]+)?)?$/
            item = convert_input($~[:item])
            if item
                buy(item)
            else
                puts "Buy what?"
            end
        when /^give( (?<item>[a-z ]+)) to( (?<guy>[a-z ]+))?$/
            item = convert_input($~[:item])
            guy = convert_input($~[:guy])
            give(item, guy)
        when /^inspect( (?<item>[a-z ]+))?$/
            if item = convert_input($~[:item])
                inspect(item)
            else
                puts "Please supply an object to inspect."
            end
        when /^rub sticks( together)?$/
            rub_sticks
        when /^read( (?<book>[a-z ]+))?$/
            if book = convert_input($~[:book])
                read(book)
            else
                puts "Please supply an book to read."
            end
        when /^equip( (?<weapon>[a-z ]+))?$/
            if weapon = convert_input($~[:weapon])
                equip(weapon)
            else
                puts "Please supply an weapon to equip."
            end
        when /^unequip?$/
            unequip
        when /^quests$/
            list_quests
        when /^achievements$/
            list_achievements
        when /^inv(entory)?$/
            inventory
        when /^climb( (?<tree_name>[a-z ]+))?$/
            # this regex needs to be cleaned up, just the tree part really
            # nvm, the whole regex sucks
             = $~[:tree_name]
            climb()
            # doesn't have to be a tree...
        when /^(?<attack>(#{special_attacks}))( (?<enemy>[a-z ]+)?)?$/
            attack = $~[:attack].to_sym
            enemy = convert_input($~[:enemy])
            fight(enemy, attack)
            # save_command = false

        # this'll run if they don't have a weapon or their weapon doesn't 
        # have both an attack method and "attack" in the attacks variable
        when /^attack( (?<enemy>[a-z ]+)?)?$/
            enemy = convert_input($~[:enemy])
            fight(enemy, @player.smack)
            save_command = false
        when /^i(nfo)?$/
            info
        when /^eat( (?<food>[a-z ]+)?)?$/
            if food = convert_input($~[:food])
                eat(food)
            else
                puts "Who?"
            end
        when /^upgrade( weapon)?$/
            @player.upgrade
            save_command = false
        when /^mine$/
            mine
        when /^time$/
            time
        when /^(h|help|\?)$/
            help
        when /^(quit|exit)$/
            quit
        # when /^- (?<code>.+)$/
        #   eval($~[:code])
        #   save_command = false
            # useful for debugging, ALWAYS RE-COMMENT BEFORE A COMMIT
        when /^\s?$/
        else
             = ["I don't speak jibberish.","Speak up. Ur not making any sense.","R u trying to confuse me? Cuz dats not gonna work","What the heck is that supposed to mean?", "Ur about the biggest idiot I've ever seen.", "What the crap are u trying to say?", "Ya, sure.", "Ur face", "Why? Why me?", "I'm about ready to quit, this job is to stressful"]
            puts .rand_choice
            save_command = false
            if $options[:loading] # for saving purposes only
                case input
                when /^damage (?<player_damage>\d+) (?<enemy_damage>\d+)\s?$/
                    player_damage = $~[:player_damage].to_i
                    enemy_damage = $~[:enemy_damage].to_i
                    @player.take_damage(player_damage) if @enemy
                    @enemy.take_damage(enemy_damage) if @enemy
                    @enemy = nil unless @enemy.nil?
                when /^enemy (?<enemy>[a-z_]+)\s?$/
                    enemy = $~[:enemy]
                    @enemy = @player.current_room.people[enemy.to_sym]
                when /^name (?<name>.+)\s?$/
                    name = $~[:name]
                    @player.name = name
                when /^unlock (?<achievement>[a-z_]+)\s?$/
                    achievement = $~[:achievement]
                    @player.give_stuff(@player.achievements[achievement.to_sym].unlock)
                when /^time (?<year>\d+) (?<month>\d+) (?<day>\d+) (?<hour>\d+) (?<minute>\d+)\s?$/
                    t = [$~[:year], $~[:month], $~[:day], $~[:hour], $~[:minute]].map(&:to_i)
                    @player.time = Hash[[:year, :month, :day, :hour, :minute].zip(t)]
                when /^total_seconds (?<seconds>\d+)\s?$/
                    seconds = $~[:seconds].to_i
                    @player.total_seconds = seconds
                when /^upgrade (?<weapon>[a-z_ ]+)\s?$/
                    weapon = $~[:weapon]
                    @player.upgrade_weapon(weapon)
                when /^add_upgrade\s?$/
                    @player.upgrades += 1
                end
            end
        end
        add_command_to_history(input) if !$options[:loading] && save_command
    end

    def keep_time
        @timer = Thread.new do
            loop do
                sleep 2
                @player.total_seconds += 2
                @player.add_minute
            end
        end
    end

    def check_time
        if @player.total_seconds >= 600
            @player.give_stuff(@player.achievements[:ten_minutes].unlock)
            add_command_to_history("unlock ten_minutes")
        end
    end

    def list_quests
        started_quests = @player.quests.values.select { |i| (i.started) }
        unless started_quests.empty?
            puts "Started Quests:".magenta
            started_quests.map do |quest|
                percent = quest.tasks_completed.to_f/quest.tasks.length.to_f*100
                puts "#{quest.name}: #{percent.round}%"
                puts "  Current Task: #{quest.current_task[:description]}" \
                    unless quest.completed
                puts
            end
        end
    end

    def list_achievements
        @player.achievements.each do |_, a|
            puts a.name if a.unlocked
        end
    end

    def help
        puts
        puts "Here are some basic commands to help you out: n, e, s, w, ne, nw, ..."
        puts "You get the point."
        puts %q[You can also type stuff like "go north", "north", and "go n"]
        puts
        puts "Also, \"achievements\" views your unlocks achievements"
        puts "and \"quests\" views the status on your started quests,"
        puts "but there are more quest to start. But remember, have fun, and explore."
        puts "Of course, there are more commands, but you'll have to figure those out."
        puts
    end

    def time
        # this message seems awkwardly worded
        puts @player.get_time.strftime("It's the year of %Y, %b %d, %I:%M %p")
    end

    def walk(direction)
        key = @player.walk(direction)
        if key.nil?
            puts "You can't go that way"
        elsif key
            @player.current_room = @rooms[key]
            @player.give_stuff(@player.current_room.enter)
        end
    end

    def pickup(item_name)
        if item_name.nil?
            puts "Please supply an object to #{input}."
        else
            stuff = @player.pickup(item_name)
        end
    end

    def inventory
        @player.inventory
    end

    def info
        @player.info
        @enemy.info if @enemy
    end

    def eat(food)
        @player.eat(food)
    end

    def read(book)
        if books = @player.items[book.to_sym]
            books.read
        else
            puts "You can't read that."
        end
    end

    def fight(enemy, attack)
        # refactor this
        if enemy.nil?
            if @enemy
                attack_enemy(attack)
            else
                puts "You aren't fighting anyone."
            end
        else
            victim = @player.current_room.people[enemy.to_sym]
            if victim
                if @enemy
                    if victim.name.downcase != @enemy.name.downcase
                        puts "You are already fighting someone else."
                        return
                    end
                end
                if victim.is_alive
                    @enemy = victim
                    add_command_to_history("enemy #{enemy}")
                    attack_enemy(attack)
                else
                    puts "#{enemy} is dead."
                end
            else
                puts "#{enemy} isn't here."
            end
        end
    end

    def attack_enemy(attack)
        weapon = @player.weapon
        damage = if weapon
                    rand(weapon.attacks[attack]) + weapon.upgrade
                else
                    @player.smack
                end
        attack_phrases =[ "You just ultimately destroyed the #{@enemy.name} #{"-".red + damage.to_s.red}", "My goodness gracious, that was impressive #{"-".red + damage.to_s.red}", "#{@enemy.name} just ate dirt #{"-".red + damage.to_s.red}" ]
        puts attack_phrases.rand_choice
        stuff = @enemy.take_damage(damage)

        _damage = 0
        if @enemy.is_alive
            _damage = @enemy.attack
            badguy_says = [ "The #{@enemy.name} attacked you! #{"-".red + _damage.to_s.red}", "#{@enemy.name} is on fire  #{"-".red + _damage.to_s.red}", "POW! That hurt.  #{"-".red + _damage.to_s.red}" ]
            puts badguy_says.rand_choice
            @player.take_damage(_damage)
        else
            @player.give_stuff(stuff)
            @enemy = nil
        end
        add_command_to_history("damage #{_damage} #{damage}")
    end

    def give(item, guy)
        # Do I have this item?
        if the_item = @player.items[item.to_sym]
            # Does this guy even exist? 
            if @player.current_room.people[guy.to_sym]
                # awesome, we r not crazy... But does guy want this item?
                if @player.current_room.people[guy.to_sym].item_wanted == item
                    puts @player.current_room.people[guy.to_sym].action
                    # complete task?
                    if @player.current_room.people[guy.to_sym].task != nil
                        xp = @player.quests[@player.current_room.people[guy.to_sym].task[:quest]].complete(@player.current_room.people[guy.to_sym].task[:task])
                        @player.give_stuff(xp)
                    end

                else
                    puts "hmmm... it seems #{guy} doesn't know what to do with that..."
                end
            else
                puts "Ummmm... That person doesn't seem to be here."
            end
        else
            puts "Ummmm... You don't have that item..."
        end

    end

    def look
        @player.current_room.look
    end

    def equip(weapon_name)
        if weapon = @player.items[weapon_name.to_sym]
            if weapon.is_a?(Weapon)
                @player.weapon = weapon
                puts "#{weapon_name} has been equipped!".cyan
            else
                puts "That's not a weapon, stupid.".red
            end
        else
            puts "you do not own that item...".red
        end
    end

    def unequip
        @player.weapon = nil
    end

    def inspect(item)
        # this could be refactored
        if the_item = @player.items[item.to_sym]
            puts the_item.description
            if the_item.is_a?(Weapon)
                puts "Damage: #{the_item.damage}"
            end
        elsif the_item = @player.current_room.items[item.to_sym]
            puts the_item.description
            if the_item.is_a?(Weapon)
                puts "Damage: #{the_item.damage}"
            end
        elsif the_item = @player.current_room.people[item.to_sym]
            puts the_item.description
            puts "Race: #{the_item.race}"
        else
            puts "This item is not here or your inventory."
        end
    end

    def talk(guy)
        if dude = @player.current_room.people[guy.to_sym]
            if dude.is_a?(Merchant)
                dude.store
            else
                puts dude.talk.yellow
            end
        else
            puts "#{guy} isn't in this room."
        end
    end

    def buy(item)
        merchant = @player.current_room.people.select { |_, p| p.is_a?(Merchant) }.first
        if merchant
            @player.give_stuff(merchant[1].buy(item, @player.gold))
        else
            puts "There is no one to buy from here."
        end
    end

    def rub_sticks
        if @player.items[:sticks]
            # do something involving fire
            puts "I need to implement this."
        end
    end

    def mine
        if @player.current_room.is_a?(Mountain)
            if @player.items[:pickaxe]
                @player.give_stuff(@player.current_room.mine)
            else
                puts "You have nothing to mine with."
            end
        else
            puts "There's nothing here to mine."
        end
    end

    def climb(thing_name)
        thing_name = convert_input(thing_name)
        if  = @player.current_room.items[:tree]
            name = .name.downcase
            if [nil, "tree"].include?(thing_name)
                res = .climb
                @player.give_stuff(res)
            else
                puts "You can't climb that."
            end
        elsif @rooms[@player.current_room[:u]].is_a?(Mountain)
            if [nil, "mountain"].include?(thing_name)
                walk("u")
            end
        else
            puts "There's nothing here to climb."
        end
    end

    def load_game
        File.delete($options[:save_file]) if File.file?($options[:save_file]) && $options[:reset]
        room = :courtyard
        @player = Player.new
        @rooms = RoomList.rooms
        @player.current_room = @rooms[room]
        @player.quests[:main].start(false)
        @enemy = nil
        if File.file?($options[:save_file])
            old_stdout = $stdout
            $stdout = StringIO.new
            $options[:loading] = true
            File.foreach($options[:save_file]) do |line|
                parse(line)
            end
            check_time
            $options[:loading] = nil
            $stdout = old_stdout
            look
        else
            get_name
            @player.current_room.enter
        end
    end

    def get_name
        puts "Wut b ur namez?"
        @player.name = prompt
        add_command_to_history("name #{@player.name}")
    end

    def quit
        puts "Come back when you can't stay so long!"

        t = @player.time.values * " "
        add_command_to_history("time #{t}")
        add_command_to_history("total_seconds #{@player.total_seconds}")
        exit
    end

    #     _   __                   
    #    / | / /__ _   _____  _____
    #   /  |/ / _ \ | / / _ \/ ___/
    #  / /|  /  __/ |/ /  __/ /    
    # /_/ |_/\___/|___/\___/_/     

    #    ______                       
    #   / ____/___  ____  ____  ____ _
    #  / / __/ __ \/ __ \/ __ \/ __ `/
    # / /_/ / /_/ / / / / / / / /_/ / 
    # \____/\____/_/ /_/_/ /_/\__,_/  

    #    _______          
    #   / ____(_)   _____ 
    #  / / __/ / | / / _ \
    # / /_/ / /| |/ /  __/
    # \____/_/ |___/\___/ 

    # __  __           
    # \ \/ /___  __  __
    #  \  / __ \/ / / /
    #  / / /_/ / /_/ / 
    # /_/\____/\__,_/  

    #    __  __    
    #   / / / /___ 
    #  / / / / __ \
    # / /_/ / /_/ /
    # \____/ .___/ 
    #     /_/      

    # do something with this
    lyrics = [
    /^We(')?re no strangers to love[\.,]?$/i,
    /^You know the rules and so do I[\.,]?$/i,
    /^A full commitment(')?s what I(')?m thinking of[\.,]?$/i,
    /^You wouldn(')?t get this from any other guy[\.,]?$/i,

    /^I just wanna tell you how I(')?m feeling[\.,]?$/i,
    /^Gotta make you understand[\.,]?$/i,

    /^Never gonna give you up[\.,]?$/i,
    /^Never gonna let you down[\.,]?$/i,
    /^Never gonna run around and desert you[\.,]?$/i,
    /^Never gonna make you cry[\.,]?$/i,
    /^Never gonna say goodbye[\.,]?$/i,
    /^Never gonna tell a lie and hurt you[\.,]?$/i,

    /^We(')?ve known each other for so long[\.,]?$/i,
    /^Your heart(')?s been aching, but[\.,]?$/i,
    /^You(')?re too shy to say it[\.,]?$/i,
    /^Inside,? we both know what(')?s been going on[\.,]?$/i,
    /^We know the game and we(')?re gonna play it[\.,]?$/i,

    /^And if you ask me how I(')?m feeling[\.,]?$/i,
    /^Don(')?t tell me you(')?re too blind to see[\.,]?$/i,
    # 
    /^Never gonna give you up[\.,]?$/i,
    /^Never gonna let you down[\.,]?$/i,
    /^Never gonna run around and desert you[\.,]?$/i,
    /^Never gonna make you cry[\.,]?$/i,
    /^Never gonna say goodbye[\.,]?$/i,
    /^Never gonna tell a lie and hurt you[\.,]?$/i,

    /^Never gonna give you up[\.,]?$/i,
    /^Never gonna let you down[\.,]?$/i,
    /^Never gonna run around and desert you[\.,]?$/i,
    /^Never gonna make you cry[\.,]?$/i,
    /^Never gonna say goodbye[\.,]?$/i,
    /^Never gonna tell a lie and hurt you[\.,]?$/i,

    /^Ooh, give you up[\.,]?$/i,
    /^Ooh, give you up[\.,]?$/i,
    /^Never gonna give,? never gonna give[\.,]?$/i,
    /^Give you up[\.,]?$/i,
    /^Never gonna give,? never gonna give[\.,]?$/i,
    /^Give you up[\.,]?$/i,

    /^We(')?ve known each other for so long[\.,]?$/i,
    /^Your heart(')?s been aching, but[\.,]?$/i,
    /^You(')?re too shy to say it[\.,]?$/i,
    /^Inside,? we both know what(')?s been going on[\.,]?$/i,
    /^We know the game and we(')?re gonna play it[\.,]?$/i,

    /^I just wanna tell you how I(')?m feeling[\.,]?$/i,
    /^Gotta make you understand[\.,]?$/i,

    /^Never gonna give you up[\.,]?$/i,
    /^Never gonna let you down[\.,]?$/i,
    /^Never gonna run around and desert you[\.,]?$/i,
    /^Never gonna make you cry[\.,]?$/i,
    /^Never gonna say goodbye[\.,]?$/i,
    /^Never gonna tell a lie and hurt you[\.,]?$/i,

    /^Never gonna give you up[\.,]?$/i,
    /^Never gonna let you down[\.,]?$/i,
    /^Never gonna run around and desert you[\.,]?$/i,
    /^Never gonna make you cry[\.,]?$/i,
    /^Never gonna say goodbye[\.,]?$/i,
    /^Never gonna tell a lie and hurt you[\.,]?$/i,

    /^Never gonna give you up[\.,]?$/i,
    /^Never gonna let you down[\.,]?$/i,
    /^Never gonna run around and desert you[\.,]?$/i,
    /^Never gonna make you cry[\.,]?$/i,
    /^Never gonna say goodbye[\.,]?$/i,
    /^Never gonna tell a lie and hurt you[\.,]?$/i
    ]

end

class Array
    def rand_choice
        if RUBY_VERSION.to_f > 1.8
            self.sample
        else
            self[rand(self.length)]
        end
    end
end

我宁愿不使用i_suck_and_my_tests_are_order_dependent!

0 个答案:

没有答案