This code compiles and debugs well, but when I do a maven build in Eclipse the unit test and the build fails. I don't understand where is the misuse of the matchers here? Thanks.
[ERROR] Errors: [ERROR] Tests.MyTest() ยป InvalidUseOfMatchers
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {MapperFactory.class})
public class Tests {
#Mock private Bucket bucketMock;
#Mock private MutateInBuilder builderMock;
#InjectMocks private Repository couchbaseRepository;
private MapperFactory mapperFactory;
#Autowired
public void setMapperFactory(MapperFactory mapperFactory) {
this.mapperFactory = mapperFactory;
}
#Test
public void MyTest() throws MyException {
String jsonText = jsonSamples.getProperty("theJson");
Mapper mapper = mapperFactory.getMapper(JsonObject.fromJson(jsonText),repository);
when(bucketMock.mutateIn("1234")).thenReturn(builderMock);
mapper.execute();
verify(builderMock).execute();
}
}
Thanks for your help, that right, that was all the exception was saying. The solution was to update the artifactId of Mockito in the pom.xml file:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
</dependency>
was replaced with:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.10.19</version>
</dependency>
Related
I have created a springboot project splitted into three maven modules, the domain layer, the core layer (contains persistence and business logic) and the web layer. I try to unit test my repository ProductRepository (located in the core layer)
#RunWith(SpringRunner.class) // provide bridge between SpringBoot test features and JUnit, for usage of springboot tsting features
#DataJpaTest
class ProductRepositoryTest {
#Autowired
private TestEntityManager em;
#Autowired
private ProductRepository repository;
#Test
void shouldReturnProduct() {
// given
Product p = Product.builder().id(1).designation("Test").reference("TEST").unitPrice(150).build();
this.em.persistAndFlush(p);
// when
Product found = repository.findByReference(p.getReference());
// then
assertThat(found.getReference()).isEqualTo(p.getReference());
}
}
But the repository is always instanciated to null. I run this test as JUnit Test in eclipse and i got a nullpointerexception.
Here is my pom.xml file
<dependencies>
<!--<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId>
</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-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
You say you try to unit test the controller but you use #RunWith(SpringRunner.class), this is used for integration tests. This annotation starts the complete application. And you just want to test the repository. What you can do is create an abstract DAO which you can implement in your unit tests.
public abstract class AbstractRepository<RepositoryType> {
RepositoryType repository;
private DBI dbi;
protected abstract RepositoryType createRepository(final DBI dbi);
#Before
public final void setUpDataSource() throws Exception {
final JdbcDataSource jdbcDataSource = new JdbcDataSource();
// DB_CLOSE_DELAY=-1 ==> h2 will keep its content as long as the vm lives otherwise the content of the database
// is lost at the moment the last connection is closed.
jdbcDataSource
.setURL("jdbc:h2:mem:play;MODE=MySQL;DB_CLOSE_DELAY=-1L;INIT=RUNSCRIPT FROM 'classpath:path/to/file/init_test.sql';");
final Flyway flyway = new Flyway();
flyway.setDataSource(jdbcDataSource);
flyway.setLocations("/path/to/locations");
flyway.migrate();
dbi = new DBI(jdbcDataSource);
runDbScript("/data.sql");
repository = createRepository(dbi);
}
private void runDbScript(final String scriptPath) throws Exception {
try (InputStreamReader reader = new InputStreamReader(AbstractDaoTest.class.getResourceAsStream(scriptPath),
Charsets.UTF_8); Handle h = dbi.open()) {
RunScript.execute(h.getConnection(), reader);
}
}
}
Now you can overwrite the createRepository method in your test class.
public class ProductRepositoryTest() {
#Override
protected ProductRepository createRepository(Dbi dbi) { return new ProductRepository(dbi); }
#Test
public void testGetProductById() {
Product response = repository.getProductById(1);
assertThat(response).isEqualTo(someObject);
}
}
If you need a framework to mock objects you can use Mockito and if you need to mock static or void methods you can use PowerMock.
Hope this helps.
Add these annotations to your test classes.
#SpringBootTest
#TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
DbUnitTestExecutionListener.class,
TransactionalTestExecutionListener.class })
public class YourTestClass {....
here's a working version of your example - hope this helps. I think you may have some conflicting configuration or dependencies in your own project. https://github.com/tndavidson/springbootjparepositorytest
I am creating a application that should be able to create an account using a REST api. When I wrote the AccountController.java class everything worked fine. I used postman to verify that I got the right response.
So my AccountController.java works correctly but the unit test I wrote with it don't work. When the unit test is executed it returns a NullPointerException which results in a InternalServerError within the REST API. While debugging i found out that if I remove the use of UriInfo in AccountController.java and return Response.ok() instead of created the test is successfull.
So my question is. How do i properly test/mock a method that returns and URI?
Bellow are all relevant classes and dependencies. The Account.java class is in another package. But both the main application as the project that contains Account.java are part of the same parent pom. I am not sure if this is relevant to my question.
AccountController.java
#Stateless
#Path("/accounts")
public class AccountController {
#Inject
private AccountService accountService;
#Context
private UriInfo uriInfo;
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response create(Account account) {
final Account newAccount = accountService.create(account);
System.out.println("Pre uriinfo message");
//Removing this line and returning Response.ok() removes the exception
final URI uri = uriInfo.getAbsolutePathBuilder().path(newAccount.getId() + "").build();
System.out.println("Post uriinfo message");
return Response.created(uri).entity(newAccount).build();
}
}
AccountControllerTest.java
#RunWith(MockitoJUnitRunner.class)
public class AccountControllerTest extends JerseyTest {
#Mock
private AccountService service;
#Override
protected Application configure() {
final ResourceConfig config = new ResourceConfig(AccountController.class);
config.register(new AbstractBinder() {
#Override
protected void configure() {
bind(service).to(AccountService.class);
}
});
return config;
}
#Test
public void createUserTest() throws Exception {
final Account testAccount = new Account("testName", "test#mail.nl");
when(service.create(testAccount))
.thenReturn(testAccount);
System.out.println("Create account test");
//Create a new Account
final Response correctResult = target("/accounts")
.request(MediaType.APPLICATION_JSON)
.post(Entity.json(testAccount));
Assert.assertEquals(HTTP_CREATED, correctResult.getStatus());
Assert.assertEquals(testAccount, correctResult.readEntity(Account.class));
}
}
Account.java
#Entity
#XmlRootElement
public class Account {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(nullable = false)
private String fullName;
#Column(unique = true, nullable = false)
private String mail;
private boolean active;
public Account() { }
public Account(String fullName, String mail) {
this.fullName = fullName;
this.mail = mail;
}
//Contains getters&setters
}
Test dependencies
<!-- jersey version for "Jersey Media Json Jackson" & "Jersey Test Framework Provider Jetty" -->
<properties>
<jersey.version>2.26-b03</jersey.version>
</properties>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.9.0</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.test-framework.providers/jersey-test-framework-provider-jetty -->
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-jetty</artifactId>
<version>${jersey.version}</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.media/jersey-media-json-jackson -->
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey.version}</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.jboss.resteasy/resteasy-jaxrs -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>3.1.4.Final</version>
<scope>test</scope>
</dependency>
So I finally figured it out.
Somewhere on this line in AccountController.java a NullPointerException was thrown:
final URI uri = uriInfo.getAbsolutePathBuilder().path(newAccount.getId() + "").build();
This only happened when using my JerseyUnitTest so i guess Jersey must change some behavior of UriInfo.
Eventually I solved the problem by overriding equals in Account.java.
In my spring boot application, I have the following dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>1.5.2.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
I have a method in the below class which i need to test:
#Service
public class DataExtractorService {
#Autowired
LinksWriterService writer;
Kinkester kinkester;
public DataExtractorService(){
kinkester=new Kinkester();
}
public Kinkester extractor(String rowData){
String pattern = "(\\d+)(\\D*)";
Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(rowData);
if (m.find( )) {
System.out.println("Found value: " + m.group(0) );
System.out.println("Found value: " + m.group(1) );
}else
System.out.println("NO MATCH");
kinkester.setAge(Integer.parseInt(m.group(0)));
kinkester.setRole(m.group(1));
return kinkester;
}
}
and then the test class is:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = SeleniumApplication.class)
#WebAppConfiguration
public class DataExtractorTest {
#Autowired
DataExtractorService dataExtractorService;
#Test
public void extractor(){
Kinkester kinkester = dataExtractorService.extractor("45M");
System.out.println(kinkester.getAge());
//assertEquals(kinkester.getAge(),45);
}
}
But unfortunately the test does not run. it complains with
Initialization Error (Runner: Junit 4 )
I have tried the below code, but still no answer got:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {SeleniumApplication.class})
Your test class should be annotated with:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = SeleniumApplication.class)
This JUnit runner requires JUnit version >= 4.12. Your dependency on spring-boot-starter-test will bring the required version of JUnit in transitively so you can remove the dependency on JUnit. Your explcit dependency on JUnit 4.11 is clashing with the Spring runner's JUnit expectations.
Have a class in the package com.conf
#Configuration
public class WebConfTest {
#Autowired
private Environment environment;
}
and unit test into com.service
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { WebConfTest.class })
public class DMSServiceImplTest {
#Autowired
WebConfTest webConfTest;
#Test
public void testConnect() throws Exception {
}
}
test dependency :
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${springframework.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
In the IDEA navigation between beans work. But WebConfTest== null if I run test.
What is wrong?
Thanks.
#RunWith is for junit runner.
If you want to run tests with TestNG, you need to extends AbstractTestNGSpringContextTests.
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/integration-testing.html#testcontext-support-classes-testng
I can't understand why my code can't execute TestNG tests. At the same time when I use JUnit test (with corresponding JUnit dependencies) instead of TestNG all works fine. Even when I recreated in my IDEA all test classes with proper test framework (TestNG) it doesn't work well. Actually the problem is in PowerMock.
Here is my simple code for testing
public class Employee {
public static int count(){
throw new UnsupportedOperationException();
}
}
My service class under test
public class EmployeeService {
public int getEmployeeCount(){
return Employee.count();
}
}
And finally my test code
#PrepareForTest(Employee.class)
public class EmployeeServiceTestNGTest {
#Test
public void should_return_count_of_employee_using_the_domain_class() {
PowerMockito.mockStatic(Employee.class);
PowerMockito.when(Employee.count()).thenReturn(1000);
EmployeeService employeeService = new EmployeeService();
Assert.assertEquals(1000, employeeService.getEmployeeCount());
}
}
And my pom.xml with all dependencies:
<properties>
<powermock.version>1.5.5</powermock.version>
</properties>
<dependencies>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-testng</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>${powermock.version}</version>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-testng-common</artifactId>
<version>1.4.11</version>
</dependency>
</dependencies>
My stacktrace
java.lang.UnsupportedOperationException
at il.arri.powermock.example.Employee.count(Employee.java:9)
at il.arri.powermock.example.EmployeeServiceTestNGTest.should_return_count_of_employee_using_the_domain_class(EmployeeServiceTestNGTest.java:14)
UPD
I changed my test class to
#PrepareForTest(Employee.class)
public class EmployeeServiceTestNGTest extends PowerMockTestCase {
#Test
public void should_return_count_of_employee_using_the_domain_class() {
PowerMockito.mockStatic(Employee.class);
PowerMockito.when(Employee.count()).thenReturn(1000);
EmployeeService employeeService = new EmployeeService();
Assert.assertEquals(1000, employeeService.getEmployeeCount());
}
#ObjectFactory
public IObjectFactory getObjectFactory() {
return new PowerMockObjectFactory();
}
}
But it still has an error:
org.testng.TestNGException:
An error occurred while instantiating class il.arri.powermock.example.EmployeeServiceTestNGTest: null
at org.testng.internal.ClassHelper.createInstance1(ClassHelper.java:398)
at org.testng.internal.ClassHelper.createInstance(ClassHelper.java:299)
at org.testng.internal.ClassImpl.getDefaultInstance(ClassImpl.java:115)
at org.testng.internal.ClassImpl.getInstances(ClassImpl.java:200)
at org.testng.internal.TestNGClassFinder.<init>(TestNGClassFinder.java:120)
at org.testng.TestRunner.initMethods(TestRunner.java:409)
at org.testng.TestRunner.init(TestRunner.java:235)
at org.testng.TestRunner.init(TestRunner.java:205)
at org.testng.TestRunner.<init>(TestRunner.java:160)
at org.testng.remote.RemoteTestNG$1.newTestRunner(RemoteTestNG.java:141)
at org.testng.remote.RemoteTestNG$DelegatingTestRunnerFactory.newTestRunner(RemoteTestNG.java:271)
at org.testng.SuiteRunner$ProxyTestRunnerFactory.newTestRunner(SuiteRunner.java:575)
at org.testng.SuiteRunner.init(SuiteRunner.java:159)
at org.testng.SuiteRunner.<init>(SuiteRunner.java:113)
at org.testng.TestNG.createSuiteRunner(TestNG.java:1299)
at org.testng.TestNG.createSuiteRunners(TestNG.java:1286)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1140)
at org.testng.TestNG.run(TestNG.java:1057)
at org.testng.remote.RemoteTestNG.run(RemoteTestNG.java:111)
at org.testng.remote.RemoteTestNG.initAndRun(RemoteTestNG.java:204)
at org.testng.remote.RemoteTestNG.main(RemoteTestNG.java:175)
at org.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:125)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Caused by: java.lang.NullPointerException
at org.powermock.core.classloader.MockClassLoader.addClassesToModify(MockClassLoader.java:133)
at org.powermock.modules.testng.internal.PowerMockClassloaderObjectFactory.newInstance(PowerMockClassloaderObjectFactory.java:81)
at org.powermock.modules.testng.PowerMockObjectFactory.newInstance(PowerMockObjectFactory.java:42)
at org.testng.internal.ClassHelper.createInstance1(ClassHelper.java:387)
... 26 more
UPD (Solution):
When I removed this dependency
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-testng-common</artifactId>
<version>1.4.11</version>
</dependency>
All works green :)
Frankly to solve my problem I need to change a pom.xml file which has a redundant dependencies such as
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-testng-common</artifactId>
<version>1.4.11</version>
</dependency>
I need to add PowerMockObjectFactory support as well
#ObjectFactory
public IObjectFactory getObjectFactory() {
return new PowerMockObjectFactory();
}
And the last thing is to extends from PowerMockTestCase class.
#PrepareForTest(Employee.class)
public class EmployeeServiceTestNGTest extends PowerMockTestCase {...}
Only in this case it works fine and green.
Isn't that UnsupportedOperationException supposed to be thrown?
You're telling it to throw that exception in your static count() method.
You can have the unit test expect that a specific exception will be thrown.