caiolopes
caiolopes

Reputation: 571

Micronaut: Injecting a list of values with @Property to a test class does not work as expected

How can I define some specific property for testing that is a list and not just a string?

The documentation explains, how to do with string, but I can't set the value to a list of strings.

application.yml

items:
  - "Item 1"
  - "Item 2"

Test file:

@MicronautTest(environments = ["test"])
class MyTest {

    @Test
    @Property(name = "items", value = "Item 1,Item 2") // this does not work
    fun justWithOneItem() {
        // ...
    }
}

On the actual code, this works (as documented here)

Project file:

@Singleton
class SomeClass {
    @set:Inject
    @setparam:Property(name = "items")
    var items: List<String>? = null

    // ...
}

I know that I can create an application-test.yml and do

@MicronautTest(environments = ["test"])
class MyTest {
    // ...
}

But I would rather prefer to set it programmatically instead of creating a new env/yaml file.

Upvotes: 4

Views: 9974

Answers (2)

James Kleeh
James Kleeh

Reputation: 12238

I think you have 2 options:

  1. Use @Property(name = "items[0]", value = "Item1") and @Property(name = "items[1]", value = "Item2")

  2. Change your test to implement TestPropertyProvider and provide the config via the returned map

Upvotes: 4

Szymon Stepniak
Szymon Stepniak

Reputation: 42264

Adding @Property to a test method like that would work if you would inject an items list in your test class. Something like this: (I know it's Spock test, and not Kotlin, but you should get the point.)

package com.github.wololock.micronaut.products

import io.micronaut.context.annotation.Property
import io.micronaut.test.annotation.MicronautTest
import spock.lang.Specification

@MicronautTest(environments = ["test"])
class MyTest extends Specification {

    @Property(name = "items")
    List<String> items

    def "should use default items"() {
        expect:
        items == ["Item 1", "Item 2"]
    }

    @Property(name = "items", value = "Item 3,Item 4,Item 5")
    def "should override default items"() {
        expect:
        items == ["Item 3", "Item 4", "Item 5"]
    }
}

However, you are using a bean of SomeClass that uses injected items. The thing here is that when you inject someClass object to your test class, the bean is already created and it is immutable. That is why adding @Property under one of the test methods won't reconstruct the bean object.

package com.github.wololock.micronaut.products

import io.micronaut.context.annotation.Property
import io.micronaut.test.annotation.MicronautTest
import spock.lang.Specification

import javax.inject.Inject

@MicronautTest(environments = ["test"])
class MyTest extends Specification {

    @Inject
    SomeClass someClass

    def "should use default items"() {
        expect:
        someClass.items == ["Item 1", "Item 2"]
    }

    @Property(name = "items", value = "Item 3,Item 4,Item 5")
    def "should override default items"() {
        expect:
        someClass.items == ["Item 3", "Item 4", "Item 5"]
    }
}

Output:

enter image description here

Luckily, there is a solution to that problem. You can use a @MockBean to deliver an implementation of SomeClass object. In this case, you can define a method that expects @Property(name="items") List<String> items parameter, thus adding a @Property annotation over a test method will override values passed to the method annotated with @MockBean:

package com.github.wololock.micronaut.products

import io.micronaut.context.annotation.Property
import io.micronaut.test.annotation.MicronautTest
import io.micronaut.test.annotation.MockBean
import spock.lang.Specification

import javax.inject.Inject

@MicronautTest(environments = ["test"])
class MyTest extends Specification {

    @Inject
    SomeClass someClass

    def "should use default items"() {
        expect:
        someClass.items == ["Item 1", "Item 2"]
    }

    @Property(name = "items", value = "Item 3,Item 4,Item 5")
    def "should override default items"() {
        expect:
        someClass.items == ["Item 3", "Item 4", "Item 5"]
    }

    @MockBean(SomeClass)
    SomeClass someClassMock(@Property(name = "items") List<String> items) {
        return new SomeClass(items)
    }
}

Output:

enter image description here

I have used Java and Spock Framework tests (Groovy) in my examples, but you should be able to achieve the same with Kotlin. I hope it helps.

Upvotes: 3

Related Questions