Zachary Carter
Zachary Carter

Reputation: 373

Object comparison in spock test fails

I'm trying to write a test using spock for a method I have.

The method looks like this -

InvoiceView getInvoiceDetailView(String invoiceNumber, Boolean isNew) {
    InvoiceView detailView;
    if (isNew) {
        newInvoiceDao.em = billingEm;
        //call InvoiceDao
        List<GroovyRowResult> invoiceSummaryRowResult = billingAdmin.rows(InvoiceQueries.GET_NEW_INVOICE_SUMMARIES_BY_NUMBER, [invoiceNumber:invoiceNumber]);
        List<GroovyRowResult> invoiceDetailsRowResult = billingAdmin.rows(InvoiceQueries.GET_NEW_INVOICE_DETAILS_BY_NUMBER, [invoiceNumber:invoiceNumber]);
        InvoiceModel invoiceModel = newInvoiceDao.getInvoice(invoiceNumber);
        detailView = new InvoiceView(invoice:invoiceModel,
                                             summary:invoiceSummaryRowResult,
                                            details:invoiceDetailsRowResult);
} ...

My test is simply trying to assert that when this method is called with true passed in as the value for the isNew parameter, a valid InvoiceView object is returned.

In my spock test I create some objects which I expect to be returned from the method :

def expectedInvoiceModel = new InvoiceModel()
def expectedInvoiceSummaryRowResult = new ArrayList<GroovyRowResult>()
def expectedInvoiceDetailsRowResult = new ArrayList<GroovyRowResult>()

def expectedInvoiceView = new InvoiceView(invoice:expectedInvoiceModel,
                                          summary:expectedInvoiceSummaryRowResult,
                                          details:expectedInvoiceDetailsRowResult)

I also mock the dao object which I will be calling into:

def setup() {
    ...
    service.billingAdmin = Mock(Sql)
    ...
}

So finally onto the Spock fixture. I have the offending comparison line commented out and a work around in place. My question is - why does the first line in my "then" block fail?

def "getInvoiceDetailView returns valid InvoiceView for new invoice"() {
    given:
        service.billingAdmin.rows(InvoiceQueries.GET_NEW_INVOICE_SUMMARIES_BY_NUMBER, [invoiceNumber:invoiceNumber]) >> expectedInvoiceSummaryRowResult
        service.billingAdmin.rows(InvoiceQueries.GET_NEW_INVOICE_DETAILS_BY_NUMBER, [invoiceNumber:invoiceNumber]) >> expectedInvoiceDetailsRowResult
        service.newInvoiceDao.getInvoice(_) >> expectedInvoiceModel

    when:
        def result = service.getInvoiceDetailView(invoiceNumber, true)
    then:
        //result == expectedInvoiceView -- Why doesn't this work?
        result.invoice == expectedInvoiceModel
        result.summary == expectedInvoiceSummaryRowResult
        result.details == expectedInvoiceDetailsRowResult
}

Upvotes: 0

Views: 5527

Answers (2)

Carlos
Carlos

Reputation: 1033

Zachary.

You didn't mock the getInvoice method correctly. Although you invoke it passing two parameters, your mocked call is expecting only one (the "_" character accepts any parameter, but will match against exactly one). Since no mocked method is matched, the call to getInvoice will return the default value, null, making your test fail.

You should do the following to make your test pass:

service.newInvoiceDao.getInvoice(_, _) >> expectedInvoiceModel

I have written a small test to make that more clear. Given the following class:

private static class StringUtils {
    public String concatenate(String a, String b) {
        return a + b;
    }
}

the following test will fail:

def "concatenation should work!"() {
    given: 
    StringUtils su = Mock()
    su.concatenate(_) >> "ab"

    when:
    def result = su.concatenate("a", "b")

    then:
    result == "ab" 
}

Notice that my mocked method, expects only one parameter:

su.concatenate(_) >> "ab"

This is the error message showing that I got a null value back:

Condition not satisfied:

result == "ab"
|      |
null   false

However, if I mock the concatenate method properly the test will succeed:

su.concatenate(_, _) >> "ab"

Upvotes: 0

Peter Niederwieser
Peter Niederwieser

Reputation: 123996

Apparently, the objects aren't equal as per the equals method of class InvoiceView. Or maybe that class doesn't declare an equals method, and the objects aren't identical (as per Object.equals).

Upvotes: 2

Related Questions