onsi / ginkgo

A Modern Testing Framework for Go
http://onsi.github.io/ginkgo/
MIT License
8.36k stars 659 forks source link

Doubt that BeforeEach() and AfterEach() don't get executed as expected. #82

Closed AlexLuya closed 10 years ago

AlexLuya commented 10 years ago

I am trying to test some db query operations,the basic work flow like these:

1,Top Container:BeforeEach() does some universal date settings
2,concrete test container(with Context):BeforeEach() modify data for concrete test
3,Top container:JustBeforeEach() inserts data to db table
4,execute test
5,top container:AfterEach() delete all data from db table 

   var _ = Describe("Dao Test", func() {
   var ()

BeforeEach(func() {
    //basic settings for data   
})

JustBeforeEach(func() {
    //insert data to db 
})

AfterEach(func() {
    //delete all data from db   
})

Context("Test 1", func() {
        BeforeEach(func() {
            //change some data for this test    
        })
        It("shouldn't return error", func() {
            .....
            Expect(recordsFromDB).To(HaveLen(2))
        })
})

Context("Test 2", func() {
        BeforeEach(func() {
           //change some data for this test     
        })

        It("shouldn't return error", func() {
            .....
            Expect(recordsFromDB).To(HaveLen(2))
        })
})

}) In my expectation,each test should satisfy assertion:

Expect(recordsFromDB).To(HaveLen( a number that is bigger then 1)) And if I run each test by adding prefix "F" to "Context(.." one by one,all tests pass,and I run all test in one,only the first test pass,all other tests report zero record got queried out from db.I doubt that BeforeEach and AfterEach didn't work as set,so questions are:

1,Due to except test 1,all other tests that should have data got queried out tell nothing got queried out,I doubt that tests didn't get executed as I set but like this:

 1,Top Container:BeforeEach() does some universal date settings
  ......
 5,top container:AfterEach() delete all data from db table 
 4,execute test
 **(deleting happened before testing)**

But I can't figure out a way to prove it,so is there any approach(some cmd options) to print out what funcs(here Before/After/JustBefore Each and test itself) got executed in what orders list like:

 timestamp xxxxxx: BeforeEach()
  timestamp xxxxxx: Context("111111111111"):BeforeEach()
  timestamp xxxxxx: JustBeforeEach()
  timestamp xxxxxx: Context("Test 1", func() {....
  timestamp xxxxxx: AfterEach()

2,I found that if a test failed,corresponded AfterEach() won't get executed,I doubt this confuses framework users.In most of time,I hope it will get executed no matter corresponded test passed or not.

onsi commented 10 years ago

Can you point me at some actual code?

For 1) I've been thinking of making verbose mode print out each component as it runs. For 2) The AfterEaches do get executed, even when a test fails.

I can most easily help you by looking at your code. The sort of use-case you describe here is very common.

AlexLuya commented 10 years ago

Thanks,the actual code is here:

 import (
. "service/activity"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
. "service/activity/query"
. "common/consts"
"entity"
"service/activity/create"
"dsc/crud/impl"
)

var _ = Describe("Joinable Activity Query Dao Test", func() {
        var (
            queryDao = NewDao()

        conditions = new(Conditions)

        category    string
        creatorImage string
        creatorName  string
        endTime     uint64
        joinConditions *JoinConditions
        myId        string
        name         string
        place        string
        startTime     uint64
        status       string
        where        *entity.Site

        requiredBirthYearFrom = uint16(1983)
        requiredBirthYearTo   = uint16(2001)
        requiredEduLevel      = uint8(3)
        requiredSex           = "MALE"
        requiredSkillLevel    = uint8(3)

        avty1, avty2, avty3, avty4 Activity
    )

    BeforeEach(func() {
        category = "Tennis"
        creatorImage = "preety.png"
        creatorName = "Alex Luya"
        endTime = startTime+2
        joinConditions = &JoinConditions{requiredBirthYearFrom, requiredBirthYearTo, requiredEduLevel, requiredSex, requiredSkillLevel}
        myId = "user/001"
        name = "Tennis with pretty girl"
        place = "Tennis PlayGroud"
        startTime = uint64(2)
        status = "PUBLISHED"
        where = &entity.Site{entity.LatLng{90, 90}, place}

        avty1 = Activity{Id:"1", Category:category, CreatorId:myId, CreatorImage:creatorImage, CreatorName:creatorName, Name:name, JoinConditions:joinConditions, Status:status, StartTime:startTime, EndTime:endTime, Where:where}
        avty2 = avty1 //copy to create
        avty2.Id = "2"
        avty3 = avty1 //copy to create
        avty3.Id = "3"
        avty4 = avty1 //copy to create
        avty4.Id = "4"

        conditions.Category = category
        conditions.CreatorId = myId
        conditions.MyBirthYear = requiredBirthYearFrom
        conditions.MyEduLevel = requiredEduLevel
        conditions.MySex = MALE
        conditions.ItemCount = 20
        conditions.Keywords = name
        conditions.Place = place
        conditions.StartTime = startTime
        conditions.EndTime = endTime
    })

    JustBeforeEach(func() {
        createActivities(avty1, avty2, avty3, avty4)
    })

    AfterEach(func() {
        //hard delete message from db
        if err := impl.NewHardDeleteDao(TABLE).DeleteAll(); err != nil {
            panic(err)
        }
    })

    Context("One of activities isn't public", func() {
            BeforeEach(func() {
                avty1.JoinConditions = nil
            })
            It("shouldn't return error", func() {

                    //when
                    items, err := queryDao.JoinableBy(conditions)

                    //then
                    Expect(err).NotTo(HaveOccurred())
                    Expect(items).To(HaveLen(3))
                })
        })

    Context("One activity's place no reachable", func() {
            BeforeEach(func() {
                avty1.Where = &entity.Site{entity.LatLng{90, 90}, "Different place"}
            })

            It("shouldn't return error", func() {

                    //when
                    items, err := queryDao.JoinableBy(conditions)

                    //then
                    Expect(err).NotTo(HaveOccurred())
                    Expect(items).To(HaveLen(3))
                })
        })

    Context("no activities will happen in my general activity place", func() {
            BeforeEach(func() {
                conditions.Place = ""
                conditions.MyActivityPlaces = []string{"elsewhere-1", "elsewhere-1"}
            })
            It("shouldn't return error", func() {
                    //when
                    items, err := queryDao.JoinableBy(conditions)

                    //then
                    Expect(err).NotTo(HaveOccurred())
                    Expect(items).To(HaveLen(0))
                })
        })
})

func createActivities(avties ...Activity) {
    createDao := create.NewDao()
    for _, v := range avties {
        if err := createDao.ToDBThenCache(&v); err != nil {
            panic(err)
       }
   }
}
onsi commented 10 years ago

It looks like you are creating queryDao once in your top-level Describe and then reusing the same queryDao instance across all your tests.

Try, instead, declaring var queryDao *Dao in your top-level Describe and then creating a new queryDao in your BeforeEach. I think the tests are telling you that one queryDao instance doesn't support multiple queries?

Finally, you can replace

if err := impl.NewHardDeleteDao(TABLE).DeleteAll(); err != nil {
            panic(err)
}

and

func createActivities(avties ...Activity) {
    createDao := create.NewDao()
    for _, v := range avties {
        if err := createDao.ToDBThenCache(&v); err != nil {
            panic(err)
       }
   }
}

with

err := impl.NewHardDeleteDao(TABLE).DeleteAll()
Expect(err).NotTo(HaveOccurred())

and

func createActivities(avties ...Activity) {
    createDao := create.NewDao()
    for _, v := range avties {
        err := createDao.ToDBThenCache(&v)
        Expect(err).NotTo(HaveOccurred())
   }
}

less typing and better error messages.

AlexLuya commented 10 years ago

You are right,thank you very much.Does the restriction that queryDao can't be shared across the tests come from ginkgo?