I am performing integration tests by using embedded Glassfish 3.1.2. The first thing I do in the test is to reset the database so each test have a completely fresh database to play with.
However, the problem is that the objects are persisted in the shared cache and not stored in the database. So when the next test starts it will get the old records from the cache instead of the database.
I can easily get rid of the problem by define
<property name="eclipselink.cache.shared.default" value="false"/>
in my persistence.xml file.
#BeforeClass
public static void startup() throws Exception {
container = EJBContainer.createEJBContainer();
context = container.getContext();
}
#Before
public void setUp() throws Exception {
//Clean database before every test using dbunit
}
#Test // This is the first test, works well since the test is first in order
public final void testCreateUser() throws Exception {
UserService userService = (UserService) context.lookup("java:global/galleria/galleria-ejb/UserService");
User user = new User(TEST_USER_ID, TEST_PASSWORD);
User actualUser = userService.signupUser(user);
assertTrue(actualUser != null);
assertEquals(TEST_USER_ID, actualUser.getUserId());
assertFalse(Arrays.equals(TEST_PASSWORD, actualUser.getPassword()));
logger.info("Finished executing test method {}", testMethod.getMethodName());
}
#Test // This is the second test, fails since the database not is clean
public final void testCreateUser() throws Exception {
UserService userService = (UserService) context.lookup("java:global/galleria/galleria-ejb/UserService");
User user = new User(TEST_USER_ID, TEST_PASSWORD);
User actualUser = userService.signupUser(user); // FAILS since TEST_USER_ID already in cache!!
//..
}
#Stateless
#EJB(name = "java:global/galleria/galleria-ejb/UserService", beanInterface = UserService.class)
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public class UserServiceImpl implements UserService
{
#EJB
private UserRepository userRepository;
#Override
#PermitAll
public User signupUser(User user) throws UserException {
User existingUser = userRepository.findById(user.getUserId());
if (existingUser != null)
{
logger.error("Attempted to create a duplicate user.");
throw new UserException(DUPLICATE_USER);
}
try {
user = userRepository.create(user);
} catch (EntityExistsException entityExistsEx) {
logger.error("Attempted to create a duplicate user.");
throw new UserException(DUPLICATE_USER, entityExistsEx);
}
return user;
}
//..
}
However, I do not want to disable caching in persistence.xml file, since I will get performance loss later on. I only want to do it while testing. Note that I am using JTA data source here.
Any ideas?
Off topic, I am trying to learn java ee, and following the Galleria EE project and try to modify it for my needs.
Best regards
Check out http://wiki.eclipse.org/EclipseLink/Examples/JPA/Caching
as both JPA 2.0 and EclipseLink native api allow clearing the shared cache. You could call this api at the start or end of your tests.
Related
tl;dr:
Seems like the Mock of the repository I created with custom behavior regarding the save method when injected loses the custom behavior.
Problem Description
I've been trying to test a Service in Spring. The method of interest in particular takes some parameters and creates a User that is saved into a UserRepository through the repository method save.
The test I am interest in making is comparing these parameters to the properties of the User passed to the save method of the repository and in this way check if it is properly adding a new user.
For that I decided to Mock the repository and save the param passed by the service method in question to the repository save method.
I based myself on this question to save the User.
private static User savedUser;
public UserRepository createMockRepo() {
UserRepository mockRepo = mock(UserRepository.class);
try {
doAnswer(new Answer<Void>() {
#Override
public Void answer(InvocationOnMock invocation) throws Throwable {
savedUser= (User) invocation.getArguments(0);
return null;
}
}).when(mockRepo).save(any(User.class));
} catch( Exception e) {}
return mockRepo;
}
private UserRepository repo = createMockRepo();
Two notes:
I gave the name repo in case the name had to match the one in the service.
There is no #Mock annotation since it starts failing the test, I presume that is because it will create a mock in the usual way (without the custom method I created earlier).
I then created a test function to check if it had the desired behavior and all was good.
#Test
void testRepo() {
User u = new User();
repo.save(u);
assertSame(u, savedUser);
}
Then I tried doing what I saw recommended across multiple questions, that is, to inject the mock into the service as explained here.
#InjectMocks
private UserService service = new UserService();
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
This is where the problems arise, the test I created for it throws a null exception when I try to access savedUser properties (here I simplified the users properties since that doesn't seem to be the cause).
#Test
void testUser() {
String name = "Steve";
String food = "Apple";
service.newUser(name, food);
assertEquals(savedUser.getName(), name);
assertEquals(savedUser.getFood(), food);
}
Upon debugging:
the service seems to have received the mock: debugged properties of the service
the savedUser is indeed null: debugged savedUser propert .
I decided to log the function with System.out.println for demonstrative purposes.
A print of my logging of the tests, demonstrating that the user test doesn't call the answer method
What am I doing wrong here?
Thank you for the help in advance, this is my first stack exchange question any tips for improvement are highly appreciated.
Instead of instanciating your service in the test class like you did, use #Autowired and make sure your UserRepository has #MockBean in the test class
#InjectMocks
#Autowired
private UserService service
#MockBean
private UserRepository mockUserRepo
With this, you can remove your setup method
But make sure your UserRepository is also autowired insider your Service
You should not need Spring to test of this. If you are following Spring best practicies when it comes to autowiring dependencies you should be able just create the objects yourself and pass the UserRepository to the UserService
Best practices being,
Constructor injection for required beans
Setter injection for optional beans
Field injection never unless you cannot inject to a constructor or setter, which is very very rare.
Note that InjectMocks is not a dependency injection framework and I discourage its use. You can see in the javadoc that it can get fairly complex when it comes to constructor vs. setter vs. field.
Note that working examples of the code here can be found in this GitHub repo.
A simple way to clean up your code and enable it to be more easily tested would be to correct the UserService to allow you to pass whatever implementation of a UserRepository you want, this also allows you to gaurentee immuability,
public class UserService {
public UserService(final UserRepository userRepository) {
this.userRepository = userRepository;
}
public final UserRepository userRepository;
public User newUser(String name, String food) {
var user = new User();
user.setName(name);
user.setFood(food);
return userRepository.save(user);
}
}
and then your test would be made more simple,
class UserServiceTest {
private UserService userService;
private UserRepository userRepository;
private static User savedUser;
#BeforeEach
void setup() {
userRepository = createMockRepo();
userService = new UserService(userRepository);
}
#Test
void testSaveUser(){
String name = "Steve";
String food = "Apple";
userService.newUser(name, food);
assertEquals(savedUser.getName(), name);
assertEquals(savedUser.getFood(), food);
}
public UserRepository createMockRepo() {
UserRepository mockRepo = mock(UserRepository.class);
try {
doAnswer(
(Answer<Void>) invocation -> {
savedUser = (User) invocation.getArguments()[0];
return null;
})
.when(mockRepo)
.save(any(User.class));
} catch (Exception e) {
}
return mockRepo;
}
}
However, this doesn't add a lot of benefit in my opinion as you are interacting with the repository directly in the service unless you fully understand the complexity of a Spring Data Repository, you are after all also mocking networking I/O which is a dangerous thing to do
How do #Id annotations work?
What about Hibernate JPA interact with my Entitiy?
Do my column definitions on my Entitiy match what I would deploy against when
using something like Liquibase/Flyway to manage the database
migrations?
How do I test against any constraints the database might have?
How do I test custom transactional boundaries?
You're baking in a lot of assumptions, to that end you could use the #DataJpaTest documentation annotation that Spring Boot provides, or replicate the configuration. A this point I am assuming a Spring Boot application, but the same concept applies to Spring Framework applications you just need to setup the configurations etc. yourself.
#DataJpaTest
class BetterUserServiceTest {
private UserService userService;
#BeforeEach
void setup(#Autowired UserRepository userRepository) {
userService = new UserService(userRepository);
}
#Test
void saveUser() {
String name = "Steve";
String food = "Apple";
User savedUser = userService.newUser(name, food);
assertEquals(savedUser.getName(), name);
assertEquals(savedUser.getFood(), food);
}
}
In this example we've went a step further and removed any notion of mocking and are connecting to an in-memory database and verifying the user that is returned is not changed to what we saved.
Yet there are limitations with in-memory databases for testing, as we are normally deploying against something like MySQL, DB2, Postgres etc. where column definitions (for example) cannot accurately be recreated by an in-memory database for each "real" database.
We could take it a step further and use Testcontainers to spin up a docker image of a database that we would connecting to at runtime and connect to it within the test
#DataJpaTest
#Testcontainers(disabledWithoutDocker = true)
class BestUserServiceTest {
private UserService userService;
#BeforeEach
void setup(#Autowired UserRepository userRepository) {
userService = new UserService(userRepository);
}
#Container private static final MySQLContainer<?> MY_SQL_CONTAINER = new MySQLContainer<>();
#DynamicPropertySource
static void setMySqlProperties(DynamicPropertyRegistry properties) {
properties.add("spring.datasource.username", MY_SQL_CONTAINER::getUsername);
properties.add("spring.datasource.password", MY_SQL_CONTAINER::getPassword);
properties.add("spring.datasource.url", MY_SQL_CONTAINER::getJdbcUrl);
}
#Test
void saveUser() {
String name = "Steve";
String food = "Apple";
User savedUser = userService.newUser(name, food);
assertEquals(savedUser.getName(), name);
assertEquals(savedUser.getFood(), food);
}
}
Now we are accurately testing we can save, and get our user against a real MySQL database. If we took it a step further and introduced changelogs etc. those could also be captured in these tests.
I working on writing tests for a crud application. I need to test the service and repository for Delete and Update statements. How would I go about mocking the repository for delete and update since they won't be returning data?
For example:
#Override
public void makeUserActive(long userId) {
try {
Optional<UserEntity> userEntityList = usersJpaRepository.findById(userId);
UserEntity userEntity = userEntityList.get();
userEntity.setIsActive(1);
usersJpaRepository.save(userEntity);
} catch (Exception e) {
logger.error("Cant make active user", e);
}
}
How do i test the service that mocks this repository and also the repository itself since it wont be returning a value
The question is what is the thing you want to be tested?
If you would like to test your repository you can achieve this by using Springs #DataJpaTest. see Integration Testing With #DataJpaTest
If you would like to test the logic inside your makeUserActive-Method you must make sure to mock your repository.
Assuming the service which contains your makeUserActive-Method looks something like this:
public class UserService{
private final UsersJpaRepository usersJpaRepository;
public UserService(UsersJpaRepository usersJpaRepository) {
this.usersJpaRepository = usersJpaRepository;
}
public void makeUserActive(long userId){
// your code from your question
}
}
You could write your Unit Test like this:
#Test
void makeUserActiveTest(){
UsersJpaRepository repository = new InMemoryUsersJpaRepository();
UserEntity user = new UserEntity();
user = repository.save(user);
UserService service = new UserService(repository);
service.makeUserActive(user.getId());
Optional<UserEntity> activatedUser = repository.findById(user.getId());
assertTrue(activatedUser.isPresent());
assertEquals(1, activatedUser.get().isActive());
}
The InMemoryUsersJpaRepository is a self written Mock which will store all data in an internal Map. The code could look something like this:
public class InMemoryUsersJpaRepository extends UsersJpaRepository {
private Map<Long, UserEntity> users = new HashMap<>();
private Long idCounter = 1L;
#Override
public UserEntity save(UserEntity user) {
if(user.getId() == null){
user.setId(idCounter);
idCounter++;
}
users.put(user.getId(), user);
return user;
}
#Override
public Optional<UserEntity> findById(long userId) {
return Optional.of(users.get(userId));
}
}
This way you will test the logic of your makeUserActive-Method which is currently to simply set the isActivated Flag on you UserEntity.
Also I would like to warn you about the answer of Mensur Qulami.
The Code in his answer will lead to a passing test but I'am pretty sure it does not test the thing you want to be tested.
You should always test the expected and observable behaviour of your method.
In your case this would be the isActivated Flag that should be 1.
The fact that your makeUserActive-Method calls the findById and save Method of the UsersJpaRepository is a mere implementation detail and the testing of those generally lead to brittle tests.
For the methods returning void, you can simply verify that they have been called. Here's an example, that mocks both an object returning method and void returning method.
#ExtendWith(MockitoExtension.class)
class ServiceTest {
#Mock
private Repository repository;
#InjectMocks
private Service service; // assume that this is your class
#Test
void testMakeUserActive() {
// given:
final UserEntity userEntity = new UserEntity();
// mocks:
when(repository.findById(1)).thenReturn(Optional.of(userEntity));
// when:
service.makeUserActive(1);
// then:
verify(repository).findById(1);
verify(repository).save(userEntity);
}
}
I have a web service DocGenerationServiceImpl that inserts (for every format) a record in the table using DocRepository and object representing the record as DocFileDO. In the for-loop, I can get the id of the record that was created in the table. For each record, I will call the executor's execute method where DocGenTask will search for the record given the id. However, for example, there are 3 formats, the DocGenTask is able to get only the last record. The first 2 it cannot find. Although it's using hibernateTemplate. Can please advise?
#RestfulService
#Controller
#RequestMapping("/docs")
public class DocGenerationServiceImpl {
#Autowired
private TaskExecutor taskExecutor;
#Autowired
private DocRepository docRepository;
#RequestMapping(value = "/generate", method = RequestMethod.POST)
#ResponseBody
public String generatedDocFile(DOCParam param) {
for(String format : param.getFormatList()) {
DocFileDO docFileDO = new DocFileDO();
...
docRepository.saveDocFile(docFileDO);
log.debug("docFileDO id = " + docFileDO.getId());
DocGenTask task = new DocGenTask(docFileDO.getId());
task.setDocRepository(docRepository);
taskExecutor.execute(task);
}
}
}
#Repository
public class DocRepository {
#Autowired
private HibernateTemplate hibernateTemplate;
public DocFileDO saveDocFile(DocFileDO docFile) {
hibernateTemplate.save(docFile);
hibernateTemplate.flush();
return docFile;
}
public DocFileDO getDocFile(Long docFileId) {
return hibernateTemplate.get(DocFileDO.class, docFileId);
}
}
public class DocGenTask implements Runnable {
public void run() {
generate();
}
private void generate() {
DocFileDO docFileObj = docRepository.getDocFile(docFileId);
}
}
A couple of things
Don't use HibernateTemplate it should be considered deprecated as of Hibernate 3.0.1 (which was released somewhere in 2006). Use the SessionFactory directly and use the getCurrentSession() method to get a hibernate Session to operate on.
You don't have transactions setup (judging from the snippets), to work with a databse you need proper transaction setup.
Your controller is doing much, all of this should be inside a service.
The first refactor your repository
#Repository
public class DocRepository {
#Autowired
private SessionFactory sf;
public DocFileDO saveDocFile(DocFileDO docFile) {
Session session = sf.getCurrentSession();
session.save(docFile);
return docFile;
}
public DocFileDO getDocFile(Long docFileId) {
return sf.getCurrentSession().get(DocFileDO.class, docFileId);
}
}
Now your code will probably fail due to improper transaction setup. Add #Transactional to all the methods (or class) that need a transaction (like the saveDocFile method).
As mentioned you probably should move the code found in the controller to a service. The controller should be nothing more then a thin integration layer converting from the web to an internal representation of something and then kick off a service/business method somewhere. This service-/business-method is also your transactional unit-of-work it either all succeeds or all fails.
I'm currently having the issue that the #Transactional annotation doesn't seem to start a transaction for Neo4j, yet (it doesn't work with any of my #Transactional annotated methods, not just with the following example).
Example:
I have this method (UserService.createUser), which creates a user node in the Neo4j graph first and then creates the user (with additional information) in a MongoDB. (MongoDB doesn't support transactions, thus create the user-node first, then insert the entity into MongoDB and commit the Neo4j-transaction afterwards).
The method is annotated with #Transactional yet a org.neo4j.graphdb.NotInTransactionException is thrown when it comes to creating the user in Neo4j.
Here is about my configuration and coding, respectively:
Code based SDN-Neo4j configuration:
#Configuration
#EnableTransactionManagement // mode = proxy
#EnableNeo4jRepositories(basePackages = "graph.repository")
public class Neo4jConfig extends Neo4jConfiguration {
private static final String DB_PATH = "path_to.db";
private static final String CONFIG_PATH = "path_to.properties";
#Bean(destroyMethod = "shutdown")
public GraphDatabaseService graphDatabaseService() {
return new GraphDatabaseFactory().newEmbeddedDatabaseBuilder(DB_PATH)
.loadPropertiesFromFile(CONFIG_PATH).newGraphDatabase();
}
}
Service for creating the user in Neo4j and the MongoDB:
#Service
public class UserService {
#Inject
private UserMdbRepository mdbUserRepository; // MongoRepository
#Inject
private Neo4jTemplate neo4jTemplate;
#Transactional
public User createUser(User user) {
// Create the graph-node first, because if this fails the user
// shall not be created in the MongoDB
this.neo4jTemplate.save(user); // NotInTransactionException is thrown here
// Then create the MongoDB-user. This can't be rolled back, but
// if this fails, the Neo4j-modification shall be rolled back too
return this.mdbUserRepository.save(user);
}
...
}
Side-notes:
I'm using spring version 3.2.3.RELEASE and spring-data-neo4j version 2.3.0.M1
UserService and Neo4jConfig are in separate Maven artifacts
Starting the server and SDN reading operations work so far, I'm just having troubles with writing operations
I'm currently migrating our project from the tinkerpop-framework to SDN-Neo4j. This user creation-process has worked before (with tinkerpop), I just have to make it work again with SDN-Neo4j.
I'm running the application in Jetty
Does anyone have any clue why this is not working (yet)?
I hope, this information is sufficient. If anything is missing, please let me know and I'll add it.
Edit:
I forgot to mention that manual transaction-handling works, but of course I'd like to implement it the way "as it's meant to be".
public User createUser(User user) throws ServiceException {
Transaction tx = this.graphDatabaseService.beginTx();
try {
this.neo4jTemplate.save(user);
User persistantUser = this.mdbUserRepository.save(user);
tx.success();
return persistantUser;
} catch (Exception e) {
tx.failure();
throw new ServiceException(e);
} finally {
tx.finish();
}
}
Thanks to m-deinum I finally found the issue. The problem was that I scanned for those components / services in a different spring-configuration-file, than where I configured SDN-Neo4j. I moved the component-scan for those packages which might require transactions to my Neo4jConfig and now it works
#Configuration
#EnableTransactionManagement // mode = proxy
#EnableNeo4jRepositories(basePackages = "graph.repository")
#ComponentScan({
"graph.component",
"graph.service",
"core.service"
})
public class Neo4jConfig extends Neo4jConfiguration {
private static final String DB_PATH = "path_to.db";
private static final String CONFIG_PATH = "path_to.properties";
#Bean(destroyMethod = "shutdown")
public GraphDatabaseService graphDatabaseService() {
return new GraphDatabaseFactory().newEmbeddedDatabaseBuilder(DB_PATH)
.loadPropertiesFromFile(CONFIG_PATH).newGraphDatabase();
}
}
I still will have to separate those components / services which require transactions from those which don't, though. However, this works for now.
I assume that the issue was that the other spring-configuration-file (which included the component-scan) was loaded before Neo4jConfig, since neo4j:repositories has to be put before context:component-scan. (See Note in Example 20.26. Composing repositories http://static.springsource.org/spring-data/data-neo4j/docs/current/reference/html/programming-model.html#d0e2948)
In my unit tests I autowired some DataSources, which use URLs like
jdbc:derby:memory:mydb;create=true
to create an in-memory DBs.
To drop an in-memory Derby db you have to connect with:
jdbc:derby:memory:mydb;drop=true
I would like this to happen after every test and start with a fresh db. How can I do this using Spring?
How to shutdown Derby in-memory database Properly
gave me a hint to a solution:
mydb.drop.url = jdbc:derby:memory:mydb;drop=true
...
<bean id="mydbDropUrl" class="java.lang.String">
<constructor-arg value="${mydb.drop.url}" />
</bean>
...
#Resource
private String mydbDropUrl;
#After
public void tearDown() {
try {
DriverManager.getConnection(mydbDropUrl);
} catch (SQLException e) {
// ignore
}
}
A downside is the use of the String constructor which accepts a String (an immutable String object around an immutable String object). I read that there is a #Value annotation in Spring 3, which might help here, but I'm using Spring 2.5.
Please let me know if you have a nicer solution.
There is a database-agnostic way to do this if you are using Spring together with Hibernate.
Make sure the application context will be created / destroyed before / after every test method:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({"classpath*:application-context-test.xml"})
#TestExecutionListeners({DirtiesContextTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class})
#DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public abstract class AbstractTest {
}
Instruct Hibernate to auto create the schema on startup and to drop the schema on shutdown:
hibernate.hbm2ddl.auto = create-drop
Now before every test
the application context is created and the required spring beans are injected (spring)
the database structures are created (hibernate)
the import.sql is executed if present (hibernate)
and after every test
the application context is destroyed (spring)
the database schema is dropped (hibernate).
If you are using transactions, you may want to add the TransactionalTestExecutionListener.
After spring test 3, you can use annotations to inject configurations:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("/spring-test.xml")
public class MyTest {
}
Just do something like:
public class DatabaseTest implements ApplicationContextAware {
private ApplicationContext context;
private DataSource source;
public void setApplicationContext(ApplicationContext applicationContext) {
this.context = applicationContext;
}
#Before
public void before() {
source = (DataSource) dataSource.getBean("dataSource", DataSource.class);
}
#After
public void after() {
source = null;
}
}
Make your bean have a scope of prototype (scope="prototype"). This will get a new instance of the data source before every test.
If you use the spring-test.jar library, you can do something like this:
public class MyDataSourceSpringTest extends
AbstractTransactionalDataSourceSpringContextTests {
#Override
protected String[] getConfigLocations() {
return new String[]{"classpath:test-context.xml"};
}
#Override
protected void onSetUpInTransaction() throws Exception {
super.deleteFromTables(new String[]{"myTable"});
super.executeSqlScript("file:db/load_data.sql", true);
}
}
And an updated version based on latest comment, that drops db and recreates tables before every test:
public class MyDataSourceSpringTest extends
AbstractTransactionalDataSourceSpringContextTests {
#Override
protected String[] getConfigLocations() {
return new String[]{"classpath:test-context.xml"};
}
#Override
protected void onSetUpInTransaction() throws Exception {
super.executeSqlScript("file:db/recreate_tables.sql", true);
}
}
This is what we do at the start of every test.
Drop all Previous Objects.
Create all tables mentioned in the create_table.sql
Insert values onto the created tables based on what you want to test.
#Before
public void initialInMemoryDatabase() throws IOException, FileNotFoundException {
inMemoryDerbyDatabase.dropAllObjects();
inMemoryDerbyDatabase.executeSqlFile("/create_table_policy_version_manager.sql");
inMemoryDerbyDatabase.executeSqlFile("/insert_table_policy_version_manager.sql");
}
Works like a charm!