I am writing (NUnit) unit tests that target a component of type MyService. Example:
public class MyService : IMyService
{
private readonly IMyRepo _myRepo;
public MyService (IMyRepo myRepo)
{
_myRepo = myRepo;
}
public MyService ()
{
_myRepo = new MyRepo();
}
...
}
I am trying to use AutoFixture as a factory that will generate my test target. I am also trying to have it (AutoFixture) populate my target with mock dependencies (using Moq).
Here's my attempt at doing this:
[Test, AutoMoqData]
public void MyTest(MyService target)
{
...
}
The [AutoMoqData] attribute (based on #ploeh's blog post) extends AutoFixture's [AutoData] attribute with AutoFixture's AutoMoqCustomization feature:
public class AutoMoqDataAttribute : AutoDataAttribute
{
public AutoMoqDataAttribute()
: base(new Fixture()
.Customize(new AutoMoqCustomization()))
{
}
}
When I run unit tests based on the above, I would like to get a real instance of MyService with a Mock<IMyRepo> dependency inside it.
Instead I'm getting a concrete instance of MyRepo.
It seems that AutoMoqCustomization opts to invoke the simplest constructor - the one with no arguments. How can I configure AutoMoqCustomizationto ignore it and instead instantiate the second constructor?
PS - Here is a list of the relevant nuget packages in use:
<package id="AutoFixture" version="3.50.2" targetFramework="net452" />
<package id="AutoFixture.AutoMoq" version="3.50.2" targetFramework="net452" />
<package id="AutoFixture.NUnit3" version="3.50.2" targetFramework="net452" />
<package id="Moq" version="4.5.29" targetFramework="net452" />
<package id="NUnit" version="3.5.0" targetFramework="net452" />
You can try to define a greedy constructor selection algorithm for AutoFixture, but really, the best solution is to remove the parameterless constructor:
public class MyService : IMyService
{
private readonly IMyRepo _myRepo;
public MyService (IMyRepo myRepo)
{
_myRepo = myRepo;
}
}
Having a parameterless constructor is a code smell called Bastard Injection. In most cases, there's no good reason for it.
In my opinion, the best option is to apply the GOOS principle of listening to your tests. When the test becomes difficult to write, it's time to reconsider the design of the System Under Test (SUT). AutoFixture tends to amplify this effect. That's what's happening here.
Related
We use testng as out testing framework. We also use Lombok #Log4j2 to instantiate our log objects. I need to test some code that it logs certain messages under certain conditions.
I have seen examples using junit and Mockito. But I cannot find how to do it in testng. Switching to junit is not an option.
Edit
I have implemented a class (CaptureLogger) which extends AbstractLogger
import org.apache.logging.log4j.spi.AbstractLogger;
public class CaptureLogger extends AbstractLogger {
...
}
I am unable to to hook it up to the logger for the class under test.
CaptureLogger customLogger = (CaptureLogger) LogManager.getLogger(MyClassUnderTest.class);
generates an error message:
java.lang.ClassCastException: org.apache.logging.log4j.core.Logger cannot be cast to CaptureLogger
I have found out that LogManager.getLogger returns the Logger interface, not the Logger object (which implements the Logger interface).
How can I create an instance of my CaptureLogger?
You can define your own appender like this:
package com.xyz;
import static java.util.Collections.synchronizedList;
import java.util.ArrayList;
import java.util.List;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
#Plugin(name = "LogsToListAppender", category = "Core", elementType = Appender.ELEMENT_TYPE)
public class LogsToListAppender extends AbstractAppender {
private static final List<LogEvent> events = synchronizedList(new ArrayList<>());
protected LogsToListAppender(String name, Filter filter) {
super(name, filter, null);
}
#PluginFactory
public static LogsToListAppender createAppender(#PluginAttribute("name") String name,
#PluginElement("Filter") Filter filter) {
return new LogsToListAppender(name, filter);
}
#Override
public void append(LogEvent event) {
events.add(event);
}
public static List<LogEvent> getEvents() {
return events;
}
}
Then create a file called log4j2-logstolist.xml in the root of the classpath where the appender will be referenced:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" packages="com.xyz" >
<Appenders>
<LogsToListAppender name="LogsToListAppender" />
</Appenders>
<Loggers>
<Root level="TRACE">
<AppenderRef ref="LogsToListAppender" />
</Root>
</Loggers>
</Configuration>
You should take special care (to update it properly) of the attribute packages="com.xyz" (the package of your appender) or it won't be available. For more information check https://www.baeldung.com/log4j2-custom-appender
And finally create TestNG test:
package com.xyz;
import static org.testng.Assert.assertTrue;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.config.Configurator;
import org.testng.annotations.Test;
#Test
public class LogsTest {
static {
Configurator.initialize(null, "classpath:log4j2-logstolist.xml");
}
#Test
public void testLogs() {
// call your code that produces log, e.g.
LogManager.getLogger(LogsTest.class).trace("Hello");
assertTrue(LogsToListAppender.getEvents().size() > 0);
}
}
As you can see we are forcing Log4j2 to use the custom configuration with Configurator.initialize(null, "classpath:log4j2-logstolist.xml"); when the class is initialized (static{} block).
Keep in mind that it will be useful for you to check logger name as well, e.g. LogsToListAppender.getEvents().stream().filter(a -> CLASS_THAT_PRODUCES_LOG.class.getName().equals(a.getLoggerName())).collect(toList());
you can access the actual message using LogEvent::getMessage() method
As Long as you're using Lombok for logger generation you can't do much at the level of the source code itself with the given tools. For example, if you place #Log4j2 annotation, it generates:
private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
The compiled code already comes with this line.
You can try to mock LogManager.getLogger method with PowerMockito but I don't really like this kind of tools. Stating this though since it can be a viable direction.
There are couple of ways to work with the framework itself.
One way (and I'm not familiar with Log4j2 specifically but it should offer this capability - I did something similar with Log4j 1.x many years ago) is to provide your own implementation of logger and associate it with the logger factory at the level of Log4j2 configurations.
Now if you do this, then the code generated by lombok will return your instance of logger that can memorize the messages that were logged at different levels (it's the custom logic you'll have to implement at the level of Logger).
Then the logger will have a method public List<String> getResults() and you'll call the following code during the verification phase:
public void test() {
UnderTest objectUnderTest = ...
//test test test
// verification
MyCustomLogger logger = (MyCutomLogger)LogManager.getLogger(UnderTest.class);
List<String> results = logger.getResults();
assertThat(results, contains("My Log Message with Params I expect or whatever");
}
Another somewhat similar way I can think of is to create a custom appender that will memorize all the messages that were sent during the test. Then you could (declaratively or programmatically bind that appender to the Logger obtained by the LogFactory.getLogger for the class under test (or also for other classes depending on your actual needs).
Then let the test work and when it comes to verification - get the reference to the appender from the log4j2 system and ask for results with some public List<String> getResults() method what must exist on the appender in addition to the methods that it must implement in order to obey the Appender contract.
So the test could look something like this:
public void test () {
MyTestAppender app = createMemorizingAppender();
associateAppenderWithLoggerUnderTest(app, UnderTest.class);
UnderTest underTest = ...
// do your tests that involve logging operations
// now the verification phase:
List<String> results = app.getResults();
assertThat(results, contains("My Log Message with Params I expect or whatever");
}
I am curious if there is an easier way to mock the dependencies inside a component that uses AOP.
For example I have a component that uses CRUD methods to persist changes to the database. For audit logging I figured I could use AOP so I didn't have to put my logging calls inside of the methods. The issue that I am running into is during unit testing when I want to mock the database and session storage used inside the object that provides the AOP. The only way I have been able to figure out how to fake the database and session is to create a new mock component file that extends the original and then overrides the init method. Using this method is not very flexible as I have to create several extra files for a simple unit test. Below is some of the test code:
This is the bean that gets created with the di/1 bean factory:
//customerBean.cfc
component accessors="true" extends="models.abstracts.AbstractModel" displayname="Customer" {
property name="ID" type="numeric" default="0";
property name="Name" type="string" length="100" default="<Unknown>" logEvents="update";
property name="Type" type="string" default="Homeowner" logEvents="update";
property name="Status" type="string" default="Active" logEvents="update";
public any function init(customerDAO) {
variables.DAO = arguments.customerDAO;
}
}
Here is the component that the customerBean extends:
//abstractModel.cfc
component accessors="true" {
public any function doCreate(required struct data) {
setID(save());
return getID();
}
}
This is the AOP component that has two dependencies that need to be mocked:
//customerLogger.cfc
component accessors="true" {
property name="tableToLogTo" default="CustomerLogs" setter="false";
public any function init(LoggerServiceDAO, scopeStorage) {
variables.loggerServiceDAO = arguments.loggerServiceDAO;
variables.scopeStorage = arguments.scopeStorage;
}
public any function after(targetBean, methodName, args, result) {
variables.loggerServiceDAO.insertLog(method=arguments.methodName, user=variables.scopeStorage.get('session.user.id'));
}
}
Here is the code I use to get the CustomerBean:
bf = new framework.aop(
folders="/models,/system,/tests/Mocks", config={transients=["entity", "process", "transients", "report", "forms"],transientPattern="Bean$"}
);
bf.intercept('CustomerBean', 'CustomerLoggerMock', 'doCreate');
customerBean = bf.getBean('CustomerBean', {customerDAO: customerDAOMock});
Ideally what I would like to do is use Testbox to mock the two objects and use the nice built in verification methods. Is this possible or am I stuck creating all of these mock files?
I want a repository (say, UserRepository) created with the help of Spring Data. I am new to spring-data (but not to spring) and I use this tutorial. My choice of technologies for dealing with the database is JPA 2.1 and Hibernate. The problem is that I am clueless as to how to write unit tests for such a repository.
Let's take create() method for instance. As I am working test-first, I am supposed to write a unit test for it - and that's where I bump into three problems:
First, how do I inject a mock of an EntityManager into the non-existing implementation of a UserRepository interface? Spring Data would generate an implementation based on this interface:
public interface UserRepository extends CrudRepository<User, Long> {}
However, I don't know how to force it to use an EntityManager mock and other mocks - if I had written the implementation myself, I would probably have a setter method for EntityManager, allowing me to use my mock for the unit test. (As for actual database connectivity, I have a JpaConfiguration class, annotated with #Configuration and #EnableJpaRepositories, which programmatically defines beans for DataSource, EntityManagerFactory, EntityManager etc. - but repositories should be test-friendly and allow for overriding these things).
Second, should I test for interactions? It is hard for me to figure out what methods of EntityManager and Query are supposed to be called (akin to that verify(entityManager).createNamedQuery(anyString()).getResultList();), since it isn't me who is writing the implementation.
Third, am I supposed to unit-test the Spring-Data-generated methods in the first place? As I know, the third-party library code is not supposed to be unit-tested - only the code the developers write themselves is supposed to be unit-tested. But if that's true, it still brings the first question back to the scene: say, I have a couple of custom methods for my repository, for which I will be writing implementation, how do I inject my mocks of EntityManager and Query into the final, generated repository?
Note: I will be test-driving my repositories using both the integration and the unit tests. For my integration tests I am using an HSQL in-memory database, and I am obviously not using a database for unit tests.
And probably the fourth question, is it correct to test the correct object graph creation and object graph retrieval in the integration tests (say, I have a complex object graph defined with Hibernate)?
Update: today I've continued experimenting with mock injection - I created a static inner class to allow for mock injection.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
#Transactional
#TransactionConfiguration(defaultRollback = true)
public class UserRepositoryTest {
#Configuration
#EnableJpaRepositories(basePackages = "com.anything.repository")
static class TestConfiguration {
#Bean
public EntityManagerFactory entityManagerFactory() {
return mock(EntityManagerFactory.class);
}
#Bean
public EntityManager entityManager() {
EntityManager entityManagerMock = mock(EntityManager.class);
//when(entityManagerMock.getMetamodel()).thenReturn(mock(Metamodel.class));
when(entityManagerMock.getMetamodel()).thenReturn(mock(MetamodelImpl.class));
return entityManagerMock;
}
#Bean
public PlatformTransactionManager transactionManager() {
return mock(JpaTransactionManager.class);
}
}
#Autowired
private UserRepository userRepository;
#Autowired
private EntityManager entityManager;
#Test
public void shouldSaveUser() {
User user = new UserBuilder().build();
userRepository.save(user);
verify(entityManager.createNamedQuery(anyString()).executeUpdate());
}
}
However, running this test gives me the following stacktrace:
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContext(CacheAwareContextLoaderDelegate.java:99)
at org.springframework.test.context.DefaultTestContext.getApplicationContext(DefaultTestContext.java:101)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:109)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:319)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:212)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:232)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:89)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:175)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:77)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:195)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:63)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userRepository': Error setting property values; nested exception is org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'entityManager' threw exception; nested exception is java.lang.IllegalArgumentException: JPA Metamodel must not be null!
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1493)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1197)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:684)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:121)
at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:60)
at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:100)
at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:250)
at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContextInternal(CacheAwareContextLoaderDelegate.java:64)
at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContext(CacheAwareContextLoaderDelegate.java:91)
... 28 more
Caused by: org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'entityManager' threw exception; nested exception is java.lang.IllegalArgumentException: JPA Metamodel must not be null!
at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:108)
at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:62)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1489)
... 44 more
tl;dr
To make it short - there's no way to unit test Spring Data JPA repositories reasonably for a simple reason: it's way to cumbersome to mock all the parts of the JPA API we invoke to bootstrap the repositories. Unit tests don't make too much sense here anyway, as you're usually not writing any implementation code yourself (see the below paragraph on custom implementations) so that integration testing is the most reasonable approach.
Details
We do quite a lot of upfront validation and setup to make sure you can only bootstrap an app that has no invalid derived queries etc.
We create and cache CriteriaQuery instances for derived queries to make sure the query methods do not contain any typos. This requires working with the Criteria API as well as the meta.model.
We verify manually defined queries by asking the EntityManager to create a Query instance for those (which effectively triggers query syntax validation).
We inspect the Metamodel for meta-data about the domain types handled to prepare is-new checks etc.
All stuff that you'd probably defer in a hand-written repository which might cause the application to break at runtime (due to invalid queries etc.).
If you think about it, there's no code you write for your repositories, so there's no need to write any unittests. There's simply no need to as you can rely on our test base to catch basic bugs (if you still happen to run into one, feel free to raise a ticket). However, there's definitely need for integration tests to test two aspects of your persistence layer as they are the aspects that related to your domain:
entity mappings
query semantics (syntax is verified on each bootstrap attempt anyway).
Integration tests
This is usually done by using an in-memory database and test cases that bootstrap a Spring ApplicationContext usually through the test context framework (as you already do), pre-populate the database (by inserting object instances through the EntityManager or repo, or via a plain SQL file) and then execute the query methods to verify the outcome of them.
Testing custom implementations
Custom implementation parts of the repository are written in a way that they don't have to know about Spring Data JPA. They are plain Spring beans that get an EntityManager injected. You might of course wanna try to mock the interactions with it but to be honest, unit-testing the JPA has not been a too pleasant experience for us as well as it works with quite a lot of indirections (EntityManager -> CriteriaBuilder, CriteriaQuery etc.) so that you end up with mocks returning mocks and so on.
With Spring Boot + Spring Data it has become quite easy:
#RunWith(SpringRunner.class)
#DataJpaTest
public class MyRepositoryTest {
#Autowired
MyRepository subject;
#Test
public void myTest() throws Exception {
subject.save(new MyEntity());
}
}
The solution by #heez brings up the full context, this only bring up what is needed for JPA+Transaction to work.
Note that the solution above will bring up a in memory test database given that one can be found on the classpath.
This may come a bit too late, but I have written something for this very purpose. My library will mock out the basic crud repository methods for you as well as interpret most of the functionalities of your query methods.
You will have to inject functionalities for your own native queries, but the rest are done for you.
Take a look:
https://github.com/mmnaseri/spring-data-mock
UPDATE
This is now in Maven central and in pretty good shape.
If you're using Spring Boot, you can simply use #SpringBootTest to load in your ApplicationContext (which is what your stacktrace is barking at you about). This allows you to autowire in your spring-data repositories. Be sure to add #RunWith(SpringRunner.class) so the spring-specific annotations are picked up:
#RunWith(SpringRunner.class)
#SpringBootTest
public class OrphanManagementTest {
#Autowired
private UserRepository userRepository;
#Test
public void saveTest() {
User user = new User("Tom");
userRepository.save(user);
Assert.assertNotNull(userRepository.findOne("Tom"));
}
}
You can read more about testing in spring boot in their docs.
In the last version of spring boot 2.1.1.RELEASE, it is simple as :
#RunWith(SpringRunner.class)
#SpringBootTest(classes = SampleApplication.class)
public class CustomerRepositoryIntegrationTest {
#Autowired
CustomerRepository repository;
#Test
public void myTest() throws Exception {
Customer customer = new Customer();
customer.setId(100l);
customer.setFirstName("John");
customer.setLastName("Wick");
repository.save(customer);
List<?> queryResult = repository.findByLastName("Wick");
assertFalse(queryResult.isEmpty());
assertNotNull(queryResult.get(0));
}
}
Complete code:
https://github.com/jrichardsz/spring-boot-templates/blob/master/003-hql-database-with-integration-test/src/test/java/test/CustomerRepositoryIntegrationTest.java
When you really want to write an i-test for a spring data repository you can do it like this:
#RunWith(SpringRunner.class)
#DataJpaTest
#EnableJpaRepositories(basePackageClasses = WebBookingRepository.class)
#EntityScan(basePackageClasses = WebBooking.class)
public class WebBookingRepositoryIntegrationTest {
#Autowired
private WebBookingRepository repository;
#Test
public void testSaveAndFindAll() {
WebBooking webBooking = new WebBooking();
webBooking.setUuid("some uuid");
webBooking.setItems(Arrays.asList(new WebBookingItem()));
repository.save(webBooking);
Iterable<WebBooking> findAll = repository.findAll();
assertThat(findAll).hasSize(1);
webBooking.setId(1L);
assertThat(findAll).containsOnly(webBooking);
}
}
To follow this example you have to use these dependencies:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.197</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.9.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
With JUnit5 and #DataJpaTest test will look like (kotlin code):
#DataJpaTest
#ExtendWith(value = [SpringExtension::class])
class ActivityJpaTest {
#Autowired
lateinit var entityManager: TestEntityManager
#Autowired
lateinit var myEntityRepository: MyEntityRepository
#Test
fun shouldSaveEntity() {
// when
val savedEntity = myEntityRepository.save(MyEntity(1, "test")
// then
Assertions.assertNotNull(entityManager.find(MyEntity::class.java, savedEntity.id))
}
}
You could use TestEntityManager from org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager package in order to validate entity state.
I solved this by using this way -
#RunWith(SpringRunner.class)
#EnableJpaRepositories(basePackages={"com.path.repositories"})
#EntityScan(basePackages={"com.model"})
#TestPropertySource("classpath:application.properties")
#ContextConfiguration(classes = {ApiTestConfig.class,SaveActionsServiceImpl.class})
public class SaveCriticalProcedureTest {
#Autowired
private SaveActionsService saveActionsService;
.......
.......
}
you can use #DataJpaTest annotation that focuses only on JPA components. By default, it scans for #Entity classes and configures Spring Data JPA repositories annotated with #Repository annotation.
By default, tests annotated with #DataJpaTest are transactional and roll back at the end of each test.
//in Junit 5 #RunWith(SpringRunner.class) annotation is not required
#DataJpaTest
public class EmployeeRepoTest {
#Autowired
EmployeeRepo repository;
#Test
public void testRepository()
{
EmployeeEntity employee = new EmployeeEntity();
employee.setFirstName("Anand");
employee.setProject("Max Account");
repository.save(employee);
Assert.assertNotNull(employee.getId());
}
}
Junit 4 Syntax will be along with SpringRunner class.
//Junit 4
#RunWith(SpringRunner.class)
#DataJpaTest
public class DataRepositoryTest{
//
}
springboot 2.4.5
import javax.persistence.EntityManager;
import javax.persistence.ParameterMode;
import javax.persistence.PersistenceContext;
import javax.persistence.StoredProcedureQuery;
#Repository
public class MyRepositoryImpl implements MyRepository {
#Autowired
#PersistenceContext(unitName = "MY_JPA_UNIT")
private EntityManager entityManager;
#Transactional("MY_TRANSACTION_MANAGER")
#Override
public MyEntity getSomething(Long id) {
StoredProcedureQuery query = entityManager.createStoredProcedureQuery(
"MyStoredProcedure", MyEntity.class);
query.registerStoredProcedureParameter("id", Long.class, ParameterMode.IN);
query.setParameter("id", id);
query.execute();
#SuppressWarnings("unchecked")
MyEntity myEntity = (MyEntity) query.getResultList().stream().findFirst().orElse(null);
return myEntity;
}
}
import org.junit.jupiter.api.*;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
import javax.persistence.EntityManager;
import javax.persistence.StoredProcedureQuery;
import java.util.List;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
#RunWith(MockitoJUnitRunner.Silent.class)
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class MyRepositoryTest {
#InjectMocks
MyRepositoryImpl myRepository;
#Mock
private EntityManager entityManager;
#Mock
private StoredProcedureQuery storedProcedureQuery;
#BeforeAll
public void init() {
MockitoAnnotations.openMocks(this);
Mockito.when(entityManager.createStoredProcedureQuery(Mockito.any(), Mockito.any(Class.class)))
.thenReturn(storedProcedureQuery);
}
#AfterAll
public void tearDown() {
// something
}
#Test
void testMethod() throws Exception {
Mockito.when(storedProcedureQuery.getResultList()).thenReturn(List.of(myEntityMock));
MyEntity resultMyEntityList = myRepository.getSomething(1l);
assertThat(resultMyEntityList,
allOf(hasProperty("id", org.hamcrest.Matchers.is("1"))
. . .
);
}
}
In 2021 with a new initalized springboot 2.5.1 project, I'm doing it like:
...
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
#ExtendWith(MockitoExtension.class)
#DataJpaTest
public class SomeTest {
#Autowired
MyRepository repo;
#Test
public void myTest() throws Exception {
repo.save(new MyRepoEntity());
/*...
/ Actual Test. For Example: Will my queries work? ... etc.
/ ...
*/
}
}
I have prepared a testng.xml file where I put number of test classes to run, for example:
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<!-- REMOTE PROJECT -->
<suite name="Suite1" preserve-order="true">
<test name="Test1">
<parameter name="browsers" value="Chrome">
</parameter>
<classes>
<class name="com.project.live.Class1" />
<class name="com.project.live.Class2" />
<class name="com.project.live.Class3" />
...
...
...
<class name="com.project.live.Class...Nth" />
</classes>
</test>
<!-- Test -->
</suite> <!-- Suite -->
There are test cases in these classes with #Test Annotation, I want to run selected test cases only i.e. I will skip some tests of these classes.
1. One way to do this is put #Ignore Annotation and remove #Test Annotation from tests which I don't want to run (but that's lengthy work, very time consuming)
2. Another way is to use groupsbut again it is lengthy to select tests and put them in groups.
Query:Is there any optimal way to achieve this, may be some customized config file?
One way can be to use Iannotationtransformer.
Put another file which contains the list of cases to exclude (or include whichever list is shorter). Implement the transform method to check whether the current method falls in this exclude list, if yes, then set the enabled property to false for the annotation and it would be excluded.
Could you try IMethodInterceptor for this purpose?
Configure your test class with a listener implementing IMethodInterceptor and decide the tests to be run dynamically
A sample listener below.
public class MyTestListener implements IMethodInterceptor {
#Override
public List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context) {
List<IMethodInstance> methodlist = new ArrayList<IMethodInstance>();
// Read these flags from somewhere, system var / test context / file or
// where ever
Boolean shouldRunTest1 = Boolean.valueOf(context.getAttribute("SHOULD_RUN_TEST1")
.toString());
Boolean shouldRunTest2 = Boolean.valueOf(context.getAttribute("SHOULD_RUN_TEST2")
.toString());
for (IMethodInstance iMethodInstance : methods) {
// decide based on method name / group / priority / description or
// what ever
String methodName = iMethodInstance.getMethod().getMethodName();
if (iMethodInstance.getMethod().isTest()) {
if (shouldRunTest1 && methodName.equals("testCase1")) {
methodlist.add(iMethodInstance);
} else if (shouldRunTest2 && methodName.equals("testCase2")) {
methodlist.add(iMethodInstance);
}
}
}
// Here we return the test cases to be run
return methodlist;
}
see the below example ,when u wanted to run only "sanity3" ,you can comment the other methods(sanity0 ....sanity2) and run using xml file (right click and Run as ->testng)
Eg:
<run>
<!-- <include name="Sanity0"/>
<include name="Sanity1"/>
<include name="Sanity2"/> -->
<include name="Sanity3"/>
I'm trying to use for the first time TestNG with #Factory and for me doesn't work, I'll say you why.
I have a class called Extend in which I have some tests, "launch site", "login", "check if the useris in his own dashboard" and so on and I wanted that for all datas passed from the factory the order of theese test are always the same "launch site">>"login">>"check user is in his dashboard">>"logout" ok? So I have the following extend.xml file and classes:
<suite name="ExtendFactory" group-by-instances="true">
<test name="Factory" preserve-order="true" group-by-instances="true">
<classes>
<class name="net.whaooo.ExtendFactory">
<methods>
<include name="launchSite"></include>
<include name="loginTest" />
<include name="userIsInHisOwnDashboardTest" />
<include name="logoutTest" />
</methods>
</class>
</classes>
</test>
</suite>
Extend class:
public class Extend extends BaseTest{
protected static FirefoxDriver driver;
private String a_driver;
private String password;
public Extend(String a_driver, String pwd){
this.a_driver = a_driver;
this.password = pwd;
}
#BeforeTest
public void stDriver() {
DesiredCapabilities caps = DesiredCapabilities.firefox(); caps.setCapability(CapabilityType.ForSeleniumServer.ENSURING_CLEAN_SESSION, true);
driver = new FirefoxDriver(caps);
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
}
#AfterTest
public void stopDriver() {
driver.close();
}
#Test
public void launch() {
launchSite(driver);
}
#Test (description = "Enter a valid login as driver")
public void loginTest() {
login(driver, a_driver, password);
}
#Test (description = "Check the driver is in his own dashboard")
public void userIsInHisOwnDashboardTest(){
userIsInHisOwnDashboardTest(driver, a_driver, password);
}
#Test(description="logout")
public void logout(){
logoutTest(driver);
}
}
Semplified Factory:
public class ExtendFactory {
#Factory
public Object[] createInstances() {
Object[] result = new Object[2];
result[0] = new Extend("test1#test.com","tester");
result[1] = new Extend("test2#test.com","tester");
return result;
}
}
But my problem is that the order in which the tests are launched doesn't follow the one specified in the xml file even if I insert the clause preserve-order="true" group-by-instances="true", I tryed also with order-by-instances="true". Can anyone help me?
I see many issues... first of all #Factory with group-by-instance="true" messes up whole test (it executes just one instance and only non-dependent methods).
#Factory works without group-by-instance but it executes all non-dependent methods first irrespective of number of instances. Eg.. Class A {#Test public void a() {} #Test(dependsOnMethod="a") public void b() {}}... along with #Factory that returns two instances.. then the execution is ref1.a, ref2.a, ref1.b, ref2.b. this has serious issue.. say class A uses large amount of memory then sure it will run out before executing all.
ps: not sure if it is eclipse issue. I am using testng 6.8.1
ps2: seems like testng intends for regression.. but it is still not there.. nor its regression (#Factory) is supported by its own classes (like #Listeners who will read only #Parameters.. but #Factory cannot set same) or by third party.
I think what you need to use is dependsOnMethods in your testcases, coz the flow that you mention, if the first method doesn't execute, there is no point in executing the second testcase. i.e. if "launch site" fails, there's no need to execute "login". This would also ensure order of execution. Check out Dependent Methods
I've been using the
#Test(dependsOnMethods = "TestName")
Where "TestName" is the prerequisite test to run. So for your login test, it should have the following annotation:
#Test(dependsOnMethods = "launchSite")
I'm running 9 tests in a row, and since adding the dependsOnMethods, all have ran in order with no issue
Thank you for your answer, I ended up to use a #Factory specifing "order-by-instances="true"" and than in the dynamic object I insert the dependencies!
Using depends in the TestClass file is not a solution as the functions which are not dependent on any other functions are still been executed randomly.
I need to execute the Test Cases in the order which i have mentioned. This can be achieved using "preserve-order" when executed using TestNG but it fails when grouping is used in TestNG.
If anyone can help in this concern, please revert.