Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Integration tests on springboot with constructor injection pattern

I am trying to use Constructor Injection dependency pattern.

I wonder what is the correct approach to inject JPA Repositories on Integration test classes:

I have my source code:

RepoClass

@Repository
public interface MyClassRepo extends JpaRepository<MyClass, Long> {
... methods ...
}

Service following cosntructor injection

public class MyClassService {

  private final MyClassRepo myClassRepo;

  public DeviceServiceImpl(final MyClassRepo myClassRepo) {
    this.myClassRepo = myClassRepo;
  }

  public boolean myMethodToTest() {
    ... whatever...
  }
}

To test it: (Here comes my issue)

SpringRunner class OPTION 1: Constructor injection

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = MyTestConfigClass.class) // With necessary imports
@SpringBootTest
public class MyClassTester {
  private final MyClassService myClassService;
  private final MyClassRepository myClassRepository;

  public MyClassTester (final MyClassRepository deviceRepository) {
    this.myClassRepository = myClassRepository;
    this.myClassService= new myClassService(myClassRepository); 
  } 

}

Does not work since console output says:

Test class should have exactly one public zero-argument constructor

SpringRunner class OPTION 2: Autowired injection

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = MyTestConfigClass.class) // With necessary imports
@SpringBootTest
public class MyClassTester {
    @Autowired
    private MyClassRepository myClassRepository;

    private MyClassService myClassService = new myClassService(myClassRepository);

}

I feel like it is breaking the desired pattern.

SpringRunner class OPTION 3: Empty Constructor

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = MyTestConfigClass.class) // With necessary imports
@SpringBootTest
public class MyClassTester {

  private final MyClassService myClassService;
  private final MyClassRepository myClassRepository;

  public MyClassTester () {
    this.myClassRepository = new MyClassRepository(); // Obviously NOT working, since its an interface
    this.myClassService= new myClassService(myClassRepository); 
  } 
}

As commented: Obviously NOT working, since MyClassRepository its an interface

Is there any better approach to solve this issue?

like image 304
Mayday Avatar asked Nov 22 '18 09:11

Mayday


2 Answers

Use Junit 5. It allows constructors with multiple arguments.

Option 1 would need @Autowired added to the test constructor

like image 191
pcoates Avatar answered Sep 19 '22 14:09

pcoates


From the documentation:

The TestContext framework does not instrument the manner in which a test instance is instantiated. Thus the use of @Autowired or @Inject for constructors has no effect for test classes.

https://docs.spring.io/autorepo/docs/spring-framework/4.2.0.RC2/spring-framework-reference/html/integration-testing.html#testcontext-fixture-di

like image 27
Willem Avatar answered Sep 22 '22 14:09

Willem