[英]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.