Does Spring provide stub implementation of JpaRepositories? - java

I am trying to unit test my Service classes that looks similiar to this:
#Service
public class SomeQueryService {
private final SomeRepository repository;
public SomeQueryService(SomeRepository repository) {
this.repository = repository;
}
public void doSomething() {
// code doing some magic
}
}
SomeRepository is simple repository interface extending JpaRepository interface.
What I want to do is unit test this service to verify whether it is working properly.
I do not want to use mockito to mock repository behaviour instead, I want to have some in-memory implementation (on list or map) that will imitate database behaviour.
Does Spring provide such fake implementations?
I want to avoid making Stub Implementation of such repository by myself as I will be using such tests in many other places.

RealLifeDeveloper has created an MIT-licensed helper-class to do just what you want: implement the repository-interface with a plain-old-java-object that just wraps a Collection, and called it "InMemoryJpaRepository". You will still have to implement some logic yourself1, though it should be easy if your queries are not too complicated.
An article explaining how to do this with example: https://reallifedeveloper.com/creating-in-memory-versions-of-spring-data-jpa-repositories-for-testing/
The repository (which includes other stuff, too) is on github: https://github.com/reallifedeveloper/rld-build-tool
The specific helper-files for creating the inmemory-db are found at https://github.com/reallifedeveloper/rld-build-tools/tree/master/src/main/java/com/reallifedeveloper/tools/test/database/inmemory if you dont want the whole repo.
1 The rule "Don't put logic in tests" exists for a reason and is clearly violated here. However, the well-known and widely-used alternatives mentioned by the other answers, H2-testing and extensive mocking, have their drawbacks too.

The type of testing you are referring to is called "Integration Testing" or "End to end testing" since it tests the whole application or a big chunk of it compared to unit tests that test only one method.
https://www.guru99.com/unit-test-vs-integration-test.html
You should not unit test your repositories, since they are already well tested by the spring team.
Solution:
You can create a test that starts the whole spring container using Spring Boot:
Just create a class in your test folder and annotate it with:
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyTestClass {
#Test
public void test() {
}
}
You can then configure an embedded database using H2 so that your test does not use the production database, just follow the Spring Boot Database Initialization doc.
https://docs.spring.io/spring-boot/docs/current/reference/html/howto-database-initialization.html
PS. You can also create a specific profile using the following annotation on your test class:
#ActiveProfiles("test")

No Spring Data does not provide a fake implementation of that interface.
You'll have to create it on your own or use a mocking framework like Mockito.

Related

Should #Autowired and #SpringBootTest be used in unit tests?

In a project where I work, we have been initializing Services for Unit testing in the following way:
Mock dependencies that are needed for the Service.
Create the Service using a constructor.
Something like this :
#RunWith(SpringRunner.class)
public class ServiceTest extends AbstractUnitTest {
#Mock private Repository repository;
private Service service;
#Before
public void init() {
service = new Service(repository);
when(repository.findById(any(Long.class))).thenReturn(Optional.of(new Entity()));
}
}
But our new developer proposed to use #Autowired and #SpringBootTest
#SpringBootTest(classes = ServiceTest.class)
#MockBean(classes = Repository.class)
#RunWith(SpringRunner.class)
public class ServiceTest extends AbstractUnitTest {
#MockBean private Repository repository;
#Autowired private Service service;
#Before
public void init() {
when(repository.findById(any(Long.class))).thenReturn(Optional.of(new Entity()));
}
}
Before that, I supposed that #Autowired and #SpringBootTest should be used only in integration tests. But googled a lot and I see that some people use those two in Unit tests.
I read boot-features-testing. Also, I read this Unit tests vs integration tests with Spring.
For me, it still doesn`t feel well that we need to involve Spring to do dependency injection for unit tests, as we can do this by ourselves to do the unit testing.
So, should #Autowired and #SpringBootTest be used in unit tests?
No. A unit test is to test a single component in isolation. Using constructor injection in your beans allows you to very simply call new SomeService(myMock), no Spring required.
Writing component or functional tests (testing your application but not wiring it up to external services for a full integration test, mocking only external interfaces; this is good for things like MockMvc tests) is a good match for #SpringBootTest, and in that case you may need to create mock objects in a Spring configuration and autowire them into your test so you can manipulate them.
In TDD tests should be helpful, straight forward, fast and keep maintenance at minimum. Otherwise devs will be annoyed and try to avoid tests.
So I recommend not to be to strict if it's a pure unit-test or a bit of a integration. Choose the test-scope that suits best for your situation and use the technical features that fit into this scope.
Don't use DI if you are doing a "real" unittest, testing a self-contained method on its own. Those tests make sense if the method does something meaningful, e.g. algorithm, calculation, decision making. Mocking sources of data is great here to get predictible input values.
The downside of a #SpringBootTest that you don't really need is a hell of a startup-time (depends on project size) which is really annoying.
Use CDI if a method calls functionality on a dependency. 1) Setting myService.service2 = new Service2() by hand leaves you with a Service2 that is also not handled by a DI-Container which maybe requires you to set some more dependencies... 2) CDI in testing is a breeze with Spring - so why would you bloat your tests with setup-code? 3) DI involves proxies which sometimes behave a little different from simple instances.
Use #ContextConfiguration(classes = {ServiceTest.class}) to get CDI with a faster startup compared to #SpringBootTest.
Don't test glueing code with unittests as it does not have any intrinsic value. Those tests are hard to understand (who loves documenting a test?), will require a lot of mocking and will often be subject to change. Test such code in association with other methods/classes even if it means you only have integration tests for these parts of the code.

Integration testing without Spring context

Should I always start Spring context during integration tests? (I mean using #SpringBootTest annotation)
Currently I'm writing integration test that involves a few classes and in order to make it faster I create object graph by hand (i.e. I don't start Spring IoC container). So currently my integration test (written in Spock) looks like this:
class UserConverterIT extends Specification {
UserConverter converter = new UserConverter(new UserDtoFactory(new UserGroupPolicy()))
def 'should ...'() {
when:
converter.convert(...)
then:
...
}
}
Alternatively I could add #SpringBootTest annotation, put #Autowire above UserConverter field and all dependecies would be injected automatically. However, the first approach is much faster. Is there something wrong in this approach?
As you said the #Autowired annotation would inject all dependencies and load the whole context automatically. Your approach is also working but its really fragile!
How can you guarantee that in your tests you never need some beans which you didn't manually new them?
Also there is another important thing. When you let the spring to inject the dependencies, if there is a problem on declaring the beans, problem would show on test phase, but in your approach they won't identify.
Also you might sometimes #Autowired an interface which told spring to get the implementation on runtime. For example you have a parent module which has an interface that implements in the child module. When you want to write a test case in parent you don't have access to child implementation to make new of that.

Mocking aspects, testng and spring 4

I have a very basic scenario where I just need to call a method which has an annotation. This annotation simply calls an AspectJ advice. I just need to make sure that the advice is being called, ideally via a mock verify. Tests are being run using TestNG and mocking using Mockito. Spring is version 4.
class under test
public class MyClassUT
{
#MyAnnotation
public myMethod...
{
...
}
}
test class
#ContextConfiguration(classes = {SpringTestConfig.class})
#WebAppConfiguration
public class MyClassUtTest extends AbstractTestNGSpringContextTests
{
#InjectMocks private MyClassUT mine;
#BeforeMethod
public void init()
{
MockitoAnnotations.initMocks(this);
}
#Test
public void testMyMethod()
{
mine...
}
}
The problem is that the advice is being called and everything is OK, except for the fact that the advice class is instantiated once by spring and another time before calling the said method. The instance being used is the latter which of course has no dependencies injected so it fails. What I am trying to do is provide spring with a mock of my advice or at least inject a mock of the service it depends on and ask AspectJ to use that existing instance.
I have tried using factory methods for the advice, spring test configurations etc, however nothing seems to be working. I have tried also with EnableAspectJautoproxy to no avail, instantiated the aspect with a #Bean annotation, also as a factory method - but nothing works well unfortunately.
(It is also interesting to note that when I enable AspectJ in eclipse, the aspect test also run in maven and as far as I know, nothing changes in pom.xml.)
So, my question is:
How do I make the test use an instance of the aspect I or spring create so that when the method MyMethod is called, its dependencies are in place , or the mock version is used?
This problem is basically equivalent,
but
How do I do this without a single line of XML config - I've seen using an apectOf factory method config being mentioned a lot, but I need a pure annotation solution, if possible;
Works with TestNg not JUnit;
Thank you!

How to test Rest-APIs that rely on persistence with arquillian

I would like to test a class that provides a rest endpoint via JAX-RS. This class depends on a JPA EntityManager an thus on a database which needs to be populated prior to test execution. I saw solutions for database population like dbunit, but I want to populate the data directly from my test class (or delegated via object mother pattern). But when testing rest endpoints I need to use the annotation option #Deployment(testable = false) which refuses me to inject the EntityManager into my test class.
So how can I solve this situation?
Or are there any better best practices? (maybe mocking, but that's also not possible for black box tests)
You could create a bean to generate your test data:
#Startup
#Singleton
public class TestDataGenerator {
#PersistenceContext
private EntityManager em;
#PostConstruct
private void generateTestData() {
// Generate your test data
}
}
The TestDataGenerator class defined above is annotated with #Singleton (ensuring there will be only one instance of the class) and #Startup (for eager initialization during the application startup sequence).
Add the TestDataGenerator class to your Arquillian deployment:
#RunWith(Arquillian.class)
public class MyArquillianTest {
private Client client = ClientBuilder.newClient();
#Deployment
#RunAsClient
public static WebArchive createDeployment() {
return ShrinkWrap.create(WebArchive.class)
.addClasses(TestDataGenerator.class, ...)
.addAsResource("test-persistence.xml", "META-INF/persistence.xml")
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
}
#Test
public void testAPI(#ArquillianResource URL deploymentUrl) {
// Test your REST service
WebTarget target = client.target(deploymentUrl.toURI()).path("api");
}
}
Observe that #RunAsClient is equivalent to #Deployment(testable = false).
The #ArquillianResource annotation allows you to inject the URL to test your web application.
For tests, I usually try and separate black box and unit testing completely (I suppose it's a preference on how you do it).
For example, my REST Api could rely on whatever it wants, but usually it doesn't do much but call my database layer or some sort of facade accessing my DB layer. The objects are injected, yes, but usually I make the fields package private, which meant that you can set them from the same package (which works with Junit as well).
For example:
public class Facade1 {
#Inject
Facade facade;
public void doSomething() { ... }
}
This class represents my REST API. I could test doSomething by simply adding a mock object as facade. Mind you, this is a quite useless test, but you get the idea. Unit tests should happen in isolation with as much mocking as possible.
Now testing the actual Rest API I usually resort to a python integration tester. Python has nice http libraries that allow you to make request very easily.
For that, I set up a staging environment for my Rest Server. The environment is a live-like representation for testing. Everything there needs to works and needs to be on the same version as the production deployment.
I then use python to poke my REST Api and verify the responses. Since I've set up my staging environment I have complete control over database content. Therefore it is easy for me to test that all responses are correct.
My typical process then is:
Compile
Build
Deploy
Integration test
I hope that helps. If you want clearer examples, you might want to post a bit more code as it's a bit hard to imagine for me what it is exactly you'd like to do :)

Testing Hibernate DAO using Junit

I am using a combination of Spring and Hibernate in my project and would like to test the DAO methods like Save and Delete methods.
daoFoundation is a wrapper class created over hibernateSession.
#Override
public String createSubject(Subject subject) {
String subjectId = (String) daoFoundation.save(subject);
return subjectId;
}
This is what I wrote in my JUnit Runs with SpringJunit4ClassRunner
I created the subject object in my SetupMethod.
#Test
public void createSubjectTest(){
subjectDao.createSubject(subject);
assertNotNull(hassSubjectSelection.getId());
}
Is this sufficient or do I need to write anything additional in my test class?
The easiest way is to import your Spring application context, autowire in the DAO's you want to test and then mark either your test methods or the entire class as #Transactional. This will create a Hibernate session, run your test and then automatically roll back the transaction so you don't effect your database state with your tests.
Have a look at how to run unit tests with Spring here. You can get Spring to create your entire application context using the #ContextConfiguration annotation. So if you create your database using an XML file called database-servlet.xml then you would annotate
#ContextConfiguration(locations={"classpath:/database-servlet.xml"})
public class Test()
You can use the annotation #RunWith(SpringJUnit4ClassRunner.class) to use functionality of the Spring TestContext Framework with your unit tests. This allows you to do things like declare expected exceptions that should be thrown, run timed tests, repeat test runs X times and a bunch of other cool stuff.
Basically to get this working, your test class should look similar to the following:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={YOUR APP CONTEXT FILES HERE AS A COMMA SEPARATED LIST})
public class Test(){
#Autowired
private YourDAO yourDAO;
#Test
#Transactional
public void testSave(){
//Test save method here. Any database changes made here will be
//automatically rolled back when the test finishes.
}
Let me know if that works or not.
The best way to test your dao layer is to use the spring jdbctemplate to write data to your database the test your get and delete methods. Then in the #after delete the records you wrote. Then use hibernate to write to your database and use jdbctemplate to read them back. Then delete your test rows. Anything less and all you are really doing is testing hibernate's caching.

Categories

Resources