spring junit test data preparrer class access repositories trough field - spring

I have simple class annotated with #Component and injected repositories like
#Component
class TestsDataPreparer(
private val carRepository: CarRepository
) {
fun withCar(builder: Car.() -> Unit = {}): Car {
return carRepository.save(
Car(
name = builder.name!!
)
)
}
}
which is clear..
But i wonder if it would be ok to do something like this, or if it is considered as anti-pattern.
#Component
class TestsDataPreparer(
val carRepository: CarRepository
) {
fun withCar(builder: Car.() -> Unit = {}): Car {
return carRepository.save(
Car(
name = builder.name!!
)
)
}
}
#Test
fun testCar() {
testsDataPreparer.withCar{this.name="Super-cool-car!"}
assertThat(testsDataPreparer.carRepository.findAll()).hasSize(1)
}
So the question is if it is okay to not inject repository in test class itself, but reuse it from TestsDataPreparer class

Yes, making an originally private field public just for testing can be considered an antipattern. Instead, you can create a CarRepository instance and then pass it to TestsDataPreparer when you create it. But for unit testing, you don't actually need that, you can use a mock and verify that the correct method was called (CarRepository.save).

Related

How to override spring application properties in test classes? Spring’s #ContextConfiguration breaks when using Kotlin inner classes

I’m currently trying to change some Spring configuration properties in test code (they aren’t static, that’s why).
There’s this odd thing when I try to solve my problem with #ContextConfiguration(initializers = [MyTestClass.Initializer::class]).
and in MyTestClass I defined this:
inner class Initializer : ApplicationContextInitializer<ConfigurableApplicationContext> {
override fun initialize(applicationContext: ConfigurableApplicationContext) {
val values = TestPropertyValues.of("spring.datasource.url=" + postgresqlContainer.jdbcUrl)
values.applyTo(applicationContext)
}
}
(I’m using Testcontainers here... how to get this working might be a separate question, feel free to help me out.)
postgresqlContainer is a member of MyTestClass that I want to access. When I run the test I just get an error:
Caused by: java.lang.IllegalArgumentException: No argument provided for a required parameter: instance of fun com.example.MyTestClass.Initializer.<init>(): com.example.MyTestClass.Initializer
Huh, ok, so I kept debugging a bit and I think it’s Spring’s BeanUtils that isn’t able to handle Kotlin inner classes. If I remove the inner keyword from my inner class BeanUtils can create an instance – doesn’t help me of course, since I need access to the property of the outer class.
I wrote a little test to assert my suspicion:
import io.kotlintest.specs.StringSpec
import org.springframework.beans.BeanUtils
class Thing {
inner class InnerThing {
}
}
class BeanUtilTest: StringSpec({
"instantiate inner class" {
BeanUtils.instantiateClass(Thing.InnerThing::class.java)
// fails :-(
}
})
Question: Is there a workaround? How can I override application properties inside my test in Kotlin?
I just ran into this and, after a long time trying to figure out what was going on, I finally came up with a solution.
You can use a companion object as follows (e.g., for MySql):
#Testcontainers
#ExtendWith(SpringExtension::class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ContextConfiguration(initializers = [ExampleIntegrationTest.Companion.Initializer::class])
class ExampleIntegrationTest {
companion object {
#Container
#JvmField
val mySqlContainer = KotlinMySqlContainer()
class Initializer : ApplicationContextInitializer<ConfigurableApplicationContext> {
override fun initialize(configurableApplicationContext: ConfigurableApplicationContext) {
TestPropertyValues.of(
"spring.datasource.url=" + mySqlContainer.jdbcUrl,
"spring.datasource.username=" + mySqlContainer.username,
"spring.datasource.password=" + mySqlContainer.password
).applyTo(configurableApplicationContext.environment)
}
}
}
...
}

Groovy + Spring - DI with no boilerplate (constructor) code

I have created spring-boot project which bases on Groovy instead of Java.
Now I have following #RestController:
#RestController
class HelloRest {
private final HelloService helloService
#GetMapping("hello")
String hello(#RequestParam("name") String name) {
helloService.createHelloMessage(name)
}
}
Question is how to inject
#Service
class HelloService {...}
in most simple way avoiding boilerplate (in this case the constructor) code?
In Java I would use: #lombok.RequiredArgsConstructor and in fact it works also if I use it in my groovy project.
On the other hand for example the #Immutable annotation from groovy.transform doesn't work as it creates in fact more than single constructor. Whereas Spring expects single constructor to be able automatically #Autowired the dependencies.
As far I see 2 solutions:
Generate the constructor
Use lombok with its annotations
Is there any solution build into Groovy which could be used here instead?
At this moment there is no Groovy mechanism that does same thing as #lombok.RequiredArgsConstructor. The main problem in your case is that Groovy always generates no-args default constructor for all currently known features like #Immutable annotation. The closest (but not accurate) way is to use #TupleConstructor like:
#RestController
#TupleConstructor(includes = ['helloService'], includeFields = true, includeProperties = false, force = true)
class HelloRest {
private final HelloService helloService
#GetMapping("hello")
String hello(#RequestParam("name") String name) {
return helloService.createHelloMessage(name)
}
}
This Groovy code will produce bytecode similar to this Java code:
#RestController
#TupleConstructor(
includeFields = true,
force = true,
includeProperties = false,
includes = {"helloService"}
)
public class HelloRest implements GroovyObject {
private final HelloService helloService;
public HelloRest(HelloService helloService) {
CallSite[] var2 = $getCallSiteArray();
MetaClass var3 = this.$getStaticMetaClass();
this.metaClass = var3;
this.helloService = (HelloService)ScriptBytecodeAdapter.castToType(helloService, HelloService.class);
}
public HelloRest() {
CallSite[] var1 = $getCallSiteArray();
this((HelloService)null);
}
#GetMapping({"hello"})
public String hello(#RequestParam("name") String name) {
CallSite[] var2 = $getCallSiteArray();
return (String)ShortTypeHandling.castToString(var2[0].call(this.helloService, name));
}
}
It is almost what you need, except this default constructor that was generated as well.
Things are getting even more complicated when using #Immutable annotation, because this version would generate 3 constructors:
public HelloRest(HelloService helloService)
public HelloRest()
public HelloRest(HashMap args)
Of course in this case you would have to remove private final in front of HelloService field definition, because this AST transformation works only with fields that are not yet final.
In this case two options you have found (creating construct manually or using Lombok) are probably the best solutions to your problem.
Alternative solution
There is also one "dirty" solution that allows you to write less amount of code, but promotes injection by reflection. Consider following code:
#RestController
class HelloRest {
#Autowired
private final HelloService helloService
#GetMapping("hello")
String hello(#RequestParam("name") String name) {
return helloService.createHelloMessage(name)
}
}
It will generate bytecode similar to following Java code:
#RestController
public class HelloRest implements GroovyObject {
#Autowired
private final HelloService helloService;
public HelloRest() {
CallSite[] var1 = $getCallSiteArray();
MetaClass var2 = this.$getStaticMetaClass();
this.metaClass = var2;
}
#GetMapping({"hello"})
public String hello(#RequestParam("name") String name) {
CallSite[] var2 = $getCallSiteArray();
return (String)ShortTypeHandling.castToString(var2[0].call(this.helloService, name));
}
}
Even though there is only single default constructor that does not even touch our helloService field, Spring bean will get injected by reflection. I share this option only to show all alternatives, although your initial instinct to use constructor injection is the best possible way to use dependency injection in practice.
You can use #Cannonical or #Immutable on the class. That way the constructor will be created for you

Spring framework and java like Object collectors In Scala

In Spring framework and Java world, there is an interesting object collector pattern that I use.
For example consider below -
public interface Calculator {
SomeOutput calculate(SomeInput input);
}
#Component
public class CalImpl1 implements Calculator {
public SomeOutput calculate(SomeInput input){
//some implementation
}
}
#Component
public class CalImpl2 implements Calculator {
public SomeOutput calculate(SomeInput input){
//some implementation
}
}
Now this can easily injected in another class using Spring DI
#Component
public class Main {
//This line collects all to implementors of this and set it here.
#Autowired
public List<Calculator> calculators;
//other methods
}
Now problem is I am not sure how same thing can be achieved in scala. I have done some search and found cake pattern (http://loicdescotte.github.io/posts/scala-di/) used in scala but that didn't seem to achieve same thing as object collectors like above. I also want to follow open close principle which I think gets violated in cake pattern but using object collectors I can easily achieve it.
is there a way achieve same object collectors like implementation in scala?
There are templates in lighbend activator that illustration using spring DI on Play, Akka and Scala applications. Please see this: https://www.lightbend.com/activator/templates#filter:spring
I haven't used Spring as DI, I usually use Guice (explicitly used because it's default on play framework 2) and Implicits parameters both as a compilation DI.
Sample:
class B
class X(x: Int)(implicit c: B)
//DI - mostly define in main method/application
implicit val c: B = new B
val x = new X(2)
Explicitly using java.util.List worked for me. This is not the prettiest solution but it shows that it basically works. Haven't tried that but implementing a corresponding PropertyEditor you could stick with the Scala types.
trait Calculator {
def calculate(input: SomeInput) : SomeOutput
}
#Component
class CalImpl1 extends Calculator {
override def calculate(input: SomeInput): SomeOutput = ...
}
#Component
class CalImpl2 extends Calculator {
override def calculate(input: SomeInput): SomeOutput = ...
}
#Component
class Main #Autowired()(calculators: java.util.List[Calculator]) {
// or inject field if constructor injection is not desired
// #Autowired
// var calculators: java.util.List[Calculator] = _
}
object Main {
def main(args: Array[String]) = {
val ctx = new AnnotationConfigApplicationContext("your package name here")
val main = ctx.getBean(classOf[Main])
// calculators should now be wired in the returned instance
}
}

How to initialise/wire beans in Grails Spock unit tests?

I'm wanting to test a Grails controller that contains a bean (I'll move it to a Service when I get it working, but I just want to keep it simple now).
//resources.groovy
beans {
myBean(com.me.MyBean)
}
// MyBean.java
// this needs to be in java as it is playing with spring-data-neo4j
package com.me;
public class MyBean {
String show() {
return "Hello";
}
}
// TestController.groovy
package com.me
import com.me.MyBean
class TestController {
def myBean
def index() {
render myBean.show()
}
}
// TestControllerSpec.groovy
package com.me
import grails.test.mixin.TestFor
import spock.lang.Specification
import com.me.*
#TestFor(TestController)
class TestControllerSpec extends Specification {
def myBean
def setup() {
defineBeans {
myBean(com.me.MyBean) {bean->
bean.autowire = true
}
}
}
def cleanup() {
}
def "show() returns Hello"() {
when:
def rc = controller.myBean.show()
def rc2 = myBean.show()
then:
rc == "Hello"
rc2 == "Hello"
}
}
Within TestControllerSpec, myBean is null. controller.myBean is also null. I think this is because Spring is not picking the bean up and wiring it in. I gather that in unit tests not all spring beans are available, but what do I need to do to get controller.myBean to be instantiated and wired up correctly?
You must be mocking the myBean as below
def myBean = Mock(MyBean)
or
MyBean myBean = Mock()
and then stub out method for your need if required as below:
myBean.show >> "test data"
and then assign it to controller object which is already mocked for you.
controller.myBean = myBean
and there you go.
Or optionally you can stub out myBean and give stubbed implementations. For example,
MyBean myBean = Stub(){
show() >> {return "sample text"}
}
controller.myBean = myBean
The reason for doing this is we are not testing the integration of application entities like controller, views or domain but we are testing a single unit i.e. a method and hence we should be just testing it and for integration we should be using integration test cases which would be similar in everything except you won't require any mocking in normal scenarios.
Edit:
found another useful feature to mock services or beans using defineBeans closure as below:
defineBeans {
adapter(Adapter)
helperService(HelperService)
}
This will allow beans to be accessed from grailsApplication.
Hope it helps.

How do I mock an autowired #Value field in Spring with Mockito?

I'm using Spring 3.1.4.RELEASE and Mockito 1.9.5. In my Spring class I have:
#Value("#{myProps['default.url']}")
private String defaultUrl;
#Value("#{myProps['default.password']}")
private String defaultrPassword;
// ...
From my JUnit test, which I currently have set up like so:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "classpath:test-context.xml" })
public class MyTest
{
I would like to mock a value for my "defaultUrl" field. Note that I don't want to mock values for the other fields — I'd like to keep those as they are, only the "defaultUrl" field. Also note that I have no explicit "setter" methods (e.g. setDefaultUrl) in my class and I don't want to create any just for the purposes of testing.
Given this, how can I mock a value for that one field?
You can use the magic of Spring's ReflectionTestUtils.setField in order to avoid making any modifications whatsoever to your code.
The comment from Michał Stochmal provides an example:
use ReflectionTestUtils.setField(bean, "fieldName", "value"); before invoking your bean method during test.
Check out this tutorial for even more information, although you probably won't need it since the method is very easy to use
UPDATE
Since the introduction of Spring 4.2.RC1 it is now possible to set a static field without having to supply an instance of the class. See this part of the documentation and this commit.
It was now the third time I googled myself to this SO post as I always forget how to mock an #Value field. Though the accepted answer is correct, I always need some time to get the "setField" call right, so at least for myself I paste an example snippet here:
Production class:
#Value("#{myProps[‘some.default.url']}")
private String defaultUrl;
Test class:
import org.springframework.test.util.ReflectionTestUtils;
ReflectionTestUtils.setField(instanceUnderTest, "defaultUrl", "http://foo");
// Note: Don't use MyClassUnderTest.class, use the instance you are testing itself
// Note: Don't use the referenced string "#{myProps[‘some.default.url']}",
// but simply the FIELDs name ("defaultUrl")
You can use this magic Spring Test annotation :
#TestPropertySource(properties = { "my.spring.property=20" })
see
org.springframework.test.context.TestPropertySource
For example, this is the test class :
#ContextConfiguration(classes = { MyTestClass.Config.class })
#TestPropertySource(properties = { "my.spring.property=20" })
public class MyTestClass {
public static class Config {
#Bean
MyClass getMyClass() {
return new MyClass ();
}
}
#Resource
private MyClass myClass ;
#Test
public void myTest() {
...
And this is the class with the property :
#Component
public class MyClass {
#Value("${my.spring.property}")
private int mySpringProperty;
...
I'd like to suggest a related solution, which is to pass the #Value-annotated fields as parameters to the constructor, instead of using the ReflectionTestUtils class.
Instead of this:
public class Foo {
#Value("${foo}")
private String foo;
}
and
public class FooTest {
#InjectMocks
private Foo foo;
#Before
public void setUp() {
ReflectionTestUtils.setField(Foo.class, "foo", "foo");
}
#Test
public void testFoo() {
// stuff
}
}
Do this:
public class Foo {
private String foo;
public Foo(#Value("${foo}") String foo) {
this.foo = foo;
}
}
and
public class FooTest {
private Foo foo;
#Before
public void setUp() {
foo = new Foo("foo");
}
#Test
public void testFoo() {
// stuff
}
}
Benefits of this approach: 1) we can instantiate the Foo class without a dependency container (it's just a constructor), and 2) we're not coupling our test to our implementation details (reflection ties us to the field name using a string, which could cause a problem if we change the field name).
You can also mock your property configuration into your test class
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "classpath:test-context.xml" })
public class MyTest
{
#Configuration
public static class MockConfig{
#Bean
public Properties myProps(){
Properties properties = new Properties();
properties.setProperty("default.url", "myUrl");
properties.setProperty("property.value2", "value2");
return properties;
}
}
#Value("#{myProps['default.url']}")
private String defaultUrl;
#Test
public void testValue(){
Assert.assertEquals("myUrl", defaultUrl);
}
}
I used the below code and it worked for me:
#InjectMocks
private ClassABC classABC;
#Before
public void setUp() {
ReflectionTestUtils.setField(classABC, "constantFromConfigFile", 3);
}
Reference: https://www.jeejava.com/mock-an-autowired-value-field-in-spring-with-junit-mockito/
Also note that I have no explicit "setter" methods (e.g. setDefaultUrl) in my class and I don't want to create any just for the purposes of testing.
One way to resolve this is change your class to use Constructor Injection, that can be used for testing and Spring injection. No more reflection :)
So, you can pass any String using the constructor:
class MySpringClass {
private final String defaultUrl;
private final String defaultrPassword;
public MySpringClass (
#Value("#{myProps['default.url']}") String defaultUrl,
#Value("#{myProps['default.password']}") String defaultrPassword) {
this.defaultUrl = defaultUrl;
this.defaultrPassword= defaultrPassword;
}
}
And in your test, just use it:
MySpringClass MySpringClass = new MySpringClass("anyUrl", "anyPassword");
Whenever possible, I set the field visibility as package-protected so it can be accessed from the test class. I document that using Guava's #VisibleForTesting annotation (in case the next guy wonders why it's not private). This way I don't have to rely on the string name of the field and everything stays type-safe.
I know it goes against standard encapsulation practices we were taught in school. But as soon as there is some agreement in the team to go this way, I found it the most pragmatic solution.
Another way is to use #SpringBootTest annotation properties field.
Here we override example.firstProperty property:
#SpringBootTest(properties = { "example.firstProperty=annotation" })
public class SpringBootPropertySourceResolverIntegrationTest {
#Autowired private PropertySourceResolver propertySourceResolver;
#Test
public void shouldSpringBootTestAnnotation_overridePropertyValues() {
String firstProperty = propertySourceResolver.getFirstProperty();
String secondProperty = propertySourceResolver.getSecondProperty();
Assert.assertEquals("annotation", firstProperty);
Assert.assertEquals("defaultSecond", secondProperty);
}
}
As you can see It overrides only one property. Properties not mentioned in #SpringBootTest stay untouched. Therefore, this is a great solution when we need to override only specific properties for the test.
For single property you can write it without braces:
#SpringBootTest(properties = "example.firstProperty=annotation")
Answer from: https://www.baeldung.com/spring-tests-override-properties#springBootTest
I also encourage you to whenever possible pass property as a parameter in constructor like in Dherik answer (https://stackoverflow.com/a/52955459/1673775) as it enables you to mock properties easily in unit tests.
However in integration tests you often don't create objects manually, but:
you use #Autowired
you want to modify property used in a class that is used in your integration test indirectly as it is deep dependency of some directly used class.
then this solution with #SpringBootTest might be helpful.

Resources