簡體   English   中英

使用spock mocking或grails進行單元測試mockFor:空指針異常

[英]Unit Testing using spock mocking or grails mockFor: Null pointer exception

我正在對我的老板寫的一些代碼進行單元測試。 他正在畫一個空白,我是TDD的新手,所以請和我一起集思廣益。

我的文件要測試,EmailAssist是這里沒有顯示的服務的幫助類。 EmailAssist應該引用其他幾個服務,包括sectionService,如圖所示。

   class EmailAssist {
       def sectionService
       //condensed to relevant items
       List<CommonsMultipartFile> attachments
       Map emailMap =[:]
       User user
       Boolean valid

       public EmailAssist(){
          valid = false
       }

       public EmailAssist(GrailsParameterMap params, User user){
              //irrelevant code here involving snipped items
              this.setSections(params.list('sections'))
              //series of other similar calls which are also delivering an NPE
       }
       //third constructor using third parameter, called in example but functionally 
       //similar to above constructor.

       //definition of errant function
       void setSections(List sections) {
            emailMap.sections = sectionService.getEmailsInSectionList(sections, user)
        }

正在調用的SectionService部分如下所示。

       Set<String> getEmailsInSectionList(List<String> sections, User user) {
            if(sections && user){
                //code to call DB and update list
            }
            else{
            []
            }

我的測試沒有提供一個部分,所以這應該返回一個空列表,特別是因為我甚至無法在單元測試中訪問數據庫。

單元測試如下。 這是使用mockFor,因為spock的模擬功能似乎不是我需要的。

@TestMixin(GrailsUnitTestMixin)
class EmailAssistSpec extends Specification {
   @Shared
   GrailsParameterMap params
   @Shared
   GrailsMockHttpServletRequest request = new GrailsMockHttpServletRequest()
   @Shared
   User user
   @Shared
   def sectionService

   def setup() {
       user = new User(id: 1, firstName: "1", lastName: "1", username: "1", email: "1@1.com")
       def sectionServiceMock = mockFor(SectionService)
       sectionServiceMock.demand.getEmailsInSectionList() {
            []
       }
       sectionService = sectionServiceMock.createMock()
   }

   def cleanup(){
   }
   void testGetFiles(){
        when:
        //bunch of code to populate request

        params = newGrailsParameterMap([:], request)
        EmailAssist assist = new EmailAssist(params, request, user)
        //Above constructor call generates NPE

確切的NPE如下:java.lang.NullPointerException:無法在null對象上調用方法getEmailsInSectionList()

        emailMap.sections = sectionService.getEmailsInSectionList(sections, user)

對於那些在家里玩的人來說,這是我的setSections功能的主體。 NPE堆棧起源於我的測試文件中的構造函數調用。 我也嘗試使用spock風格的模擬,但該服務仍被視為null。 最糟糕的是,構造函數甚至不是這個測試應該測試的,它只是拒絕傳遞它,因此使測試無法運行。

如果有更多細節我可以提供澄清事情,請告訴我,謝謝!

編輯:我在構造函數中短路了setter來完成測試,但是在測試覆蓋中留下了一些明顯漏洞,我無法弄清楚如何修復。 也許我的嘲笑位於錯誤的地方? Spock的模擬文檔對於復雜的函數來說並不是很方便。

你得到的NPE因為sectionService的內EmailAssist沒有被創建。 您的EmailAssist構造EmailAssist調用EmailAssist sectionService.getEmailsInSectionList(sections, user) ,因為sectionService為null,您將獲得一個NPE。

雖然您在測試setup()創建了一個名為sectionService的模擬,但它不會自動連接/注入到EmailAssist類中。 Grails單元測試中的自動接線非常有限 - 關於實際創建的bean的文檔不是很清楚(我對Grails / Groovy相對較新)。

您需要注入sectionService為您創建emailAssist ,否則就來不及逃跑的NPE。

如果您將單元測試中對構造函數的調用修改為:

@TestMixin(GrailsUnitTestMixin)
class EmailAssistSpec extends Specification {
   @Shared
   GrailsParameterMap params
   @Shared
   GrailsMockHttpServletRequest request = new GrailsMockHttpServletRequest()
   @Shared
   User user
   @Shared
   def mockSectionService = Mock(SectionService)

   def setup() {
       user = new User(id: 1, firstName: "1", lastName: "1", username: "1", email: "1@1.com")
   }

   def cleanup(){
   }

   void testGetFiles(){
        given: "an EmailAssist class with an overridden constructor"

        EmailAssist.metaClass.constructor = { ParamsType params, RequestType request, UserType user -> 
           def instance = new EmailAssist(sectionService: mockSectionService) 
           instance // this returns instance as it's the last line in the closure, but you can put "return instance" if you wish
        }            

        // note that I've moved the population of the request to the given section
        //bunch of code to populate request
        params = newGrailsParameterMap([:], request)

        // this is the list of parameters that you expect sectionService will be called with
        def expectedSectionList = ['some', 'list']

        when: "we call the constructor"
        EmailAssist assist = new EmailAssist(params, request, user)

        then: "sectionService is called by the constructor with the expected parameters"
        1 * mockSectionService.getEmailsInSectionList(expectedSectionList, user)
        // replace a parameter with _ if you don't care about testing the parameter

這個答案是基於伯特貝克威思博客文章在這里

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM