I have two different aspects in my Selenium test automation project and I am using load time weaving with AspectJ. Both aspects run fine when running under test methods in that class.
The case where AspectA running on #Before("#annotation(ChangeToIFrameA)") on a method1 that is under a method0 that is already annotated with #Before class it overrides the second aspect call AspectB on method2 that is called under method1.
Aspect A class
package com.stackoverfow.FrameHandling;
#Aspect
public class AspectA {
#Before("#annotation(com.stackoverfow.FrameHandling.ChangeToIFrameA)")
public void switchIFrameA(WebDriver driver)) {
driver.switchTo().frame("IFrameA name");
}
}
Custom Annotated class for Aspect A to run
package com.stackoverfow.FrameHandling;
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface ChangeToIFrameA {
}
Aspect B class
package com.stackoverfow.FrameHandling;
#Aspect
public class AspectB {
#Before("execution(com.stackoverfow.NavigationBar.*(..))")
public void switchIFrameB(WebDriver driver)) {
driver.switchTo().frame("IFrameB name");
}
#After("execution(com.stackoverfow.NavigationBar.*(..))")
public void switchIFrameA(WebDriver driver)) {
driver.switchTo().frame("IFrameA name");
}
}
Navigation Bar Class
package com.stackoverfow.Navigation;
class NavigationBar{
class NavigationBar(){}
public void goToHomePage(){
//Navigates to Home Page
}
public void goToHelpPage(){
//Navigates to Help Page
}
public void goToUserAccount(){
//Navigates to User Account Page
}
}
Test Class that uses TestNG
package com.stackoverfow.NavigationTests;
public class AppBasicTests {
#BeforeClass(alwaysRun = true)
public void login(ITestContext context) {
//logs into the application
takeCareOfNavigationToHomePage();
}
#BeforeMethod(alwaysRun = true)
public void openSecondTab(ITestContext context) {
//Open a new tab
}
#AfterMethod(alwaysRun = true)
public void closeSecondTab() {
//closes the secondary tab
}
#AfterClass(alwaysRun = true)
public void logout() {
//logs out from the app
}
//Annotated to private method that is called inside login() method which is annotated by #Before class
#ChangeToIFrameA
private void takeCareOfNavigationToHomePage(){
NavigationBar navigationBar = new NavigationBar();
navigationBar.goToHomePage();// Assuming AspectB runs before and after this method call for switching frames
}
//Assuming it switches to FrameA before running the test
#ChangeToIFrameA
#Test(enabled = true)
public void testNavigation(){
NavigationBar navigationBar = new NavigationBar();
navigationBar.goToHelpPage();// Assuming AspectB runs
before and after this method call for switching frames
}
}
aop.xml
<?xml version="1.0" encoding="UTF-8"?>
<aspectj>
<aspects>
<aspect name="com.stackoverfow.FrameHandling.AspectA"/>
</aspects>
<aspects>
<aspect name="com.stackoverfow.FrameHandling.AspectB"/>
</aspects>
</aspectj>
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.stackoverflow</groupId>
<artifactId>UiTestAutomation</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>UiTestAutomation</name>
<properties>
<!-- Maven Plugins -->
<maven-compiler-version>3.6.0</maven-compiler-version>
<maven-surefire-version>2.22.2</maven-surefire-version>
<selenium-version>3.14.0</selenium-version>
<testng-version>7.4.0</testng-version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-version}</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<useIncrementalCompilation>${useIncrementalCompilation} </useIncrementalCompilation>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-version}</version>
<configuration>
<argLine>-javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/1.9.8/aspectjweaver-1.9.8.jar"</argLine>
</configuration>
<executions>
<execution>
<phase>process-sources</phase>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.8</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-report-plugin</artifactId>
<version>2.22.2</version>
</plugin>
</plugins>
</build>
<dependencyManagement>
</dependencies>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium-version}</version>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-firefox-driver</artifactId>
<version>${selenium-version}</version>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-htmlunit-driver</artifactId>
<version>${htmlunit-version}</version>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-support</artifactId>
<version>${selenium-version}</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng-version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.8</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.5</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
Here AspectA runs fine on method testNavigation()
AspectB runs fine on inner method call of NavigationBar.goToHelpPage() that is present under testNavigation() method.
When private method takeCareOfNavigationToHomePage() that is annotated #ChangeToIFrameA is called AspectA runs on that method and during the inner method call NavigationBar.goToHomePage() again AspectA is called instead of AspectB. AspectA call happens twice looks like an overriding is happen.
I see its happening because the private method takeCareOfNavigationToHomePage is called under login method that is already annotated with TestNG annotation #BeforeClass. Any idea how to overcome this? Please suggest a solution.
Related
Actually, it will be more complex question.
I want use AspectJ only in test purpose.
Have found suggestion to use if() JointPoint and some static boolean field.
Also, first I start using aspect as inner static class of my base test method.
After some experiments I replaced it to own class, but actually don’t got the result, that I want.
So, I just create some test project.
Maven pom.xml:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>Testing</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<mockito.version>3.11.2</mockito.version>
<aspectj.version>1.9.7</aspectj.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.7.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.junit.platform/junit-platform-surefire-provider
-->
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-surefire-provider</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>
<!--<configuration>
<argLine>-javaagent:${user.home}/.m2/repository/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar</argLine>
</configuration>-->
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.14.0</version>
<configuration>
<complianceLevel>${maven.compiler.source}</complianceLevel>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<showWeaveInfo>true</showWeaveInfo>
<verbose>true</verbose>
<Xlint>ignore</Xlint>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
<executions>
<execution>
<goals>
<!-- use this goal to weave all your main classes -->
<goal>compile</goal>
<!-- use this goal to weave all your test classes -->
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Classes:
A:
package classes;
public class A {
private String a = "classes.A";
public String getA()
{
return a;
}
public String getFromB()
{
return new B().getB();
}
}
B:
package classes;
public class B {
private String b = "classes.B";
public String getB() {
return b;
}
}
test class:
package aspectj;
import classes.A;
import classes.B;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class NewTest {
private static boolean useAspect = false;
public static boolean isUseAspect() {
return useAspect;
}
#BeforeEach
void init()
{
useAspect = true;
}
#Test
public void changeValue()
{
B b = new B();
System.out.println(b.getB());
}
#Test
public void changeValueInA()
{
A a = new A();
System.out.println(a.getFromB());
}
}
Aspect class
package aspectj;
import org.aspectj.lang.Aspects;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
#Aspect
public class AspectB {
#Pointcut("if()")
public static boolean useAspect()
{
return NewTest.isUseAspect();
}
#Pointcut("call(* classes.B.getB())")
public void callTestMethod() {}
#Around("callTestMethod()")
public String myAdvice(ProceedingJoinPoint point) throws Throwable {
return "You have been hacked!";
}
}
Main class:
package classes;
public class TestOutputHere {
public static void main(String[] args) {
System.out.println(new A().getFromB());
}
}
And I got result after running test and main method:
changeValue() -> You have been hacked!
changeValueInA() -> classes.B
main(String[] args) -> classes.B
Second result dont feet for me... So after some experiments and removing test scope for AspectJ dependencies, removing if() JointPoint (we can't use test classes from src) and placing Aspect class in src I got the result:
changeValue() -> You have been hacked!
changeValueInA() -> You have been hacked!
main(String[] args) -> You have been hacked!
Lust result dont feet to me too. And I really don’t want to use aspect for all project.
After that I just tried to use some Load-Time weaving with configuration for maven surefire plugin:
<configuration>
<argLine>
-javaagent:${user.home}/.m2/repository/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar
</argLine>
</configuration>
And I got result, that I want:
changeValue() -> You have been hacked!
changeValueInA() -> You have been hacked!
main(String[] args) -> classes.B
So, where the question after thousands of these letters?)
Questions are:
Can I get this result with compile weaving and without using AspectJ
classLoader?
As I have performance restrictions in real project - how can AspectJ
classLoader affect performance of non-test environment in this case?
In case of load-time weaving that I describe - all classes of the
project will recompile by AspectJ? Only test? How recompiling work in load-time?
I will greatfull for this answers!
You have several problems in your code:
You set useAspect = true before each test, but never reset to false after a test ends. This would bleed context out into other tests where you want the aspect inactive. You should clean that up.
The aspect has an if() pointcut depending on a static method in a test class. The test class is unavailable during application runtime under normal circumstances. The static field and its accessor methods (if any) should be in the aspect class itself.
package aspectj;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
#Aspect
public class AspectB {
private static boolean useAspect = false;
public static void setUseAspect(boolean useAspect) {
AspectB.useAspect = useAspect;
}
#Pointcut("call(* classes.B.getB()) && if()")
public static boolean callTestMethod() {
return useAspect;
}
#Around("callTestMethod()")
public String myAdvice(ProceedingJoinPoint point) throws Throwable {
return "You have been hacked!";
}
}
package aspectj;
import classes.A;
import classes.B;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class NewTest {
#BeforeEach
void init() {
AspectB.setUseAspect(true);
}
#AfterEach
void cleanUp() {
AspectB.setUseAspect(false);
}
#Test
public void changeValue() {
B b = new B();
System.out.println(b.getB());
}
#Test
public void changeValueInA() {
A a = new A();
System.out.println(a.getFromB());
}
}
Probably, your aspect is defined in src/test/java instead of src/main/java, which explains why it is only compiled into test classes and not into application classes. But the latter is what you expect, if a method call from one application class to another should be intercepted. Therefore, you need to move the aspect to the main sources and make aspectjrt have a compile scope, not a test scope.
But in this case where the aspect is supposed to only affect tests, I would recommend to not use compile-time weaving (CTW), because it would mean that the application always needs the AspectJ runtime on its class path (see above), even if the aspect is inactive. CTW only makes sense if at least sometimes during application runtime the aspect is meant to be active, too. Even then, it is debatable if load-time weaving (LTW) might not be the better solution, e.g. if it is a rarely used debugging aspect. CTW is ideal for production aspects. In this case, it seems to be fairly clear that LTW using the Java agent is the right approach. Like you said, you do not even need the ugly static field and the if() pointcut.
Whenever I used #MicronautTest in my project, the whole application is started as a whole and because certain things need to be done at startup by services, they are executed also.
I don't need the whole Singleton shebang when I'm testing a simple controller.
My question is how do I limit which controllers and singletons are loaded when testing Micronaut?
I have a small demo example which shows the problem
Pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>micronaut-test</artifactId>
<version>0.1</version>
<packaging>${packaging}</packaging>
<parent>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-parent</artifactId>
<version>2.5.1</version>
</parent>
<properties>
<packaging>jar</packaging>
<jdk.version>11</jdk.version>
<release.version>11</release.version>
<micronaut.version>2.5.1</micronaut.version>
<exec.mainClass>com.example.Application</exec.mainClass>
<micronaut.runtime>netty</micronaut.runtime>
</properties>
<repositories>
<repository>
<id>central</id>
<url>https://repo.maven.apache.org/maven2</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-inject</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-validation</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.micronaut.test</groupId>
<artifactId>micronaut-test-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-http-client</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-http-server-netty</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-runtime</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>io.micronaut.build</groupId>
<artifactId>micronaut-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<!-- Uncomment to enable incremental compilation -->
<!-- <useIncrementalCompilation>false</useIncrementalCompilation> -->
<annotationProcessorPaths combine.children="append">
</annotationProcessorPaths>
<compilerArgs>
<arg>-Amicronaut.processing.group=com.example</arg>
<arg>-Amicronaut.processing.module=micronaut-test</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
</project>
application.java
package com.example;
import io.micronaut.runtime.Micronaut;
public class Application {
public static void main(String[] args) {
Micronaut.run(Application.class, args);
}
}
A simple service that throws whenever it is instantiated to indicate the problem
#Singleton
public class SingletonService {
#EventListener
public void init(ApplicationStartupEvent event) {
throw new RuntimeException("I was initialized");
}
}
A simple controller
#Controller
public class TestController {
#Get
public Simple findAll() {
return new Simple("ATest");
}
public static class Simple {
private final String test;
public Simple(String test) {
this.test = test;
}
public String getTest() {
return test;
}
}
}
And a straightforward test
#MicronautTest
class TestControllerTest {
#Inject
#Client("/")
RxHttpClient rxHttpClient;
#Test
void controller() {
HttpResponse<ByteBuffer> byteBufferHttpResponse = rxHttpClient.exchange("/").blockingFirst();
assert byteBufferHttpResponse.getStatus().getCode() == 200;
}
}
The test results are
[INFO] Running com.example.controller.TestControllerTest
11:01:52.804 [main] INFO i.m.context.env.DefaultEnvironment - Established active environments: [test]
[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 7.232 s <<< FAILURE! - in com.example.controller.TestControllerTest
[ERROR] com.example.controller.TestControllerTest Time elapsed: 7.229 s <<< ERROR!
java.lang.RuntimeException: I was initialized
How do I prevent the test from starting that SingletonService? Stubbing is an option but keep in mind that this is a simple demo illustrating the issue for a bigger project. Is there no other straightforward way?
According to the docs of #MicronautTest, it shouldn't be scanning the classpath but it clearly is?
Here are some other configurations I've already attempted without any success:
Adding a ApplicationBuilder that disables eagerInit
#MicronautTest(application = TestControllerTest.TestApplication.class)
class TestControllerTest {
public static class TestApplication {
public static void main(String[] args) {
Micronaut.build(args)
.eagerInitAnnotated(null)
.eagerInitSingletons(false)
.mainClass(TestApplication.class);
}
}
#Inject
#Client("/")
RxHttpClient rxHttpClient;
#Test
void controller() {
HttpResponse<ByteBuffer> byteBufferHttpResponse = rxHttpClient.exchange("/").blockingFirst();
assert byteBufferHttpResponse.getStatus().getCode() == 200;
}
}
Passing along a contextbuilder
#MicronautTest(contextBuilder = TestControllerTest.TestContextBuilder.class)
class TestControllerTest {
public static class TestContextBuilder extends DefaultApplicationContextBuilder {
public TestContextBuilder() {
eagerInitSingletons(false);
eagerInitAnnotated(null);
eagerInitConfiguration(false);
}
}
#Inject
#Client("/")
RxHttpClient rxHttpClient;
#Test
void controller() {
HttpResponse<ByteBuffer> byteBufferHttpResponse = rxHttpClient.exchange("/").blockingFirst();
assert byteBufferHttpResponse.getStatus().getCode() == 200;
}
}
Which all yield the same response.
Hopefully someone knows how to limit the scope of bean instantation with the #MicronautTest or I'll most likely switch back to Spring boot
Thanks in advance
My question is how do I limit which controllers and singletons are
loaded when testing Micronaut?
At least there is the #Requires annotation that allows doing flexible configuration if a bean should be loaded within the current environment.
For example, in your case it should be something like:
import javax.inject.Singleton;
import io.micronaut.context.annotation.Requires;
#Singleton
#Requires(notEnv = {Environment.TEST})
public class SingletonService {
#EventListener
public void init(ApplicationStartupEvent event) {
throw new RuntimeException("I was initialized");
}
}
As a result, the exception from the service is not thrown.
However, I don't really know the purpose why you need to exclude the service. And it could be that other approaches might be more convenient.
For example, see Mocking Collaborators section from the micronaut test documentation: https://micronaut-projects.github.io/micronaut-test/latest/guide/
I'm very new to Springboot and I've searched for the solution to be able to fix the error but with no avail.
I still keep getting this error:
```
Description:
Parameter 0 of constructor in sample.ArticlesRepository required a bean of type 'org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate' in your configuration.
Here is my pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>sprinboot_project</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Sprinboot_Project</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.3.0.RELEASE</version>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>
Here is my ArticlesRepository:
Here is my ArticlesRepository:
#Repository
public class ArticlesRepository implements ArticledDao{
public ArticlesRepository(NamedParameterJdbcTemplate template) {
this.template = template;
}
NamedParameterJdbcTemplate template;
#Override
public List<Articles> findAll() {
return template.query("select * from article", new ArticlesRowMapper());
}
#Override
public void deleteArticles(Articles articles) {
final String sql = "delete from article where articleid=:articleid";
Map<String,Object> map=new HashMap<String,Object>();
map.put("articleid", articles.getId());
template.execute(sql,map,new PreparedStatementCallback<Object>() {
#Override
public Object doInPreparedStatement(PreparedStatement ps)
throws SQLException, DataAccessException {
return ps.executeUpdate();
}
});
}
}
Article Dao class:
package sample;
import org.springframework.stereotype.Repository;
import java.util.List;
#Repository
public interface ArticledDao {
List<Articles> findAll();
public void deleteArticles(Articles articles);
}
Here is my Main class:
package sample;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#SpringBootApplication(scanBasePackages="sample")
#EnableJpaRepositories("sample")
public class Main {
public static void main(String[] args) throws Exception {
SpringApplication.run(Main.class, args);
}
}
application.properties:
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=none
spring.jpa.hibernate.show-sql=true
spring.datasource.url=jdbc:postgresql://localhost:5432/articles
spring.datasource.username=*******
spring.datasource.password=*******
spring.datasource.initialization-mode=always
spring.datasource.initialize=true
spring.datasource.continue-on-error=true
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
I hope you can provide a solution that I've couldn't find in stackoverflow and I will be grateful for the help.
Github link: https://github.com/Editux/springboot/tree/main/sprinboot_project
The tutorial I was following : https://dzone.com/articles/bounty-spring-boot-and-postgresql-database
List of files:
Your class ArticlesRepository has a field NamedParameterJdbcTemplate Spring want to set dependency with type NamedParameterJdbcTemplate for correctly creating ArticlesRepository but he can't find any bean with type NamedParameterJdbcTemplate.
You should declare one or more beans with type NamedParameterJdbcTemplate in your Spring context by using any of the possible annotations #Component or #Bean in the configuration class.
DAO object should be an entity, not repository
also, in your repository class, you don't have to implement the default methods like findAll and delete...I'd completely rework your code to this:
Entity class
#Entity
#Table(name = "articles")
public class ArticleDAO {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private boolean active;
// getters and setters (or use Lombok)
}
Repository class
#Repository
public interface ArticleRepository extends CrudRepository<ArticleDAO, Integer> {
}
Apperantly I needed to remove this spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration from application.properties to fix the error.
I am working on a basic test-driven development learning of a simple java program which provides portfolio value for the stocks.
I am having 2 classes,Portfolio.java & Stock.java which depicts the portfolio & stock models. An interface StockService.java is used in abstract manner to get real-time stock price.
PortfolioTest.java is the class in which I am trying to write unit-tests for the features of portfolio by mocking this StockService using Mockito.
I am able to use the deprecated MockitoAnnotations.initMocks(this); & run my tests but getting null pointer exception if try to use #Rule or #RunWith annotations.
Stock.java
public class Stock {
private String name;
private int quantity;
public Stock(String name, int quantity) {
this.name = name;
this.quantity = quantity;
}
public String getName() { return name; }
public float getQuantity() { return quantity; }
}
Portfolio.java
import java.util.List;
public class Portfolio {
private List<Stock> stocks;
private StockService stockService;
private Float portfolioValue;
public Portfolio(List<Stock> stocks, Float portfolioValue) {
this.stocks = stocks;
this.portfolioValue = portfolioValue;
}
public void setStockService(StockService stockService) { this.stockService = stockService; }
public Float calculateMarketValue() {
Float marketValue = 0.0f;
for(Stock stock: this.stocks) {
marketValue += (stock.getQuantity()*stockService.getRealtimePrice(stock.getName()));
}
return marketValue;
}
public Boolean isInProfit() {
return (portfolioValue<calculateMarketValue()?true:false);
}
}
StockService.java
public interface StockService {
public float getRealtimePrice(String name);
}
pom.xml
<project>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-runner</artifactId>
<version>1.5.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.5.13</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<finalName>mockito-basic</finalName>
</configuration>
</plugin>
</plugins>
</build>
</project>
PortfolioTest.java
//#RunWith(MockitoJUnitRunner.class)
public class PortfolioTestMockAnnotations {
//#Rule public MockitoRule rule = MockitoJUnit.rule();
#Mock
StockService stockService;
#InjectMocks
Portfolio portfolio;
#BeforeAll
static void setUp() {
}
#BeforeEach
void init(){
MockitoAnnotations.initMocks(this);
System.out.println(stockService);
when(stockService.getRealtimePrice("infosys")).thenReturn(2200.0f);
when(stockService.getRealtimePrice("reliance")).thenReturn(3100.0f);
when(stockService.getRealtimePrice("indiamart")).thenReturn(4000.0f);
List<Stock> stocks = new ArrayList<>();
stocks.add(new Stock("infosys",10));
stocks.add(new Stock("reliance", 5));
portfolio = new Portfolio(stocks, 35000.0f);
portfolio.setStockService(stockService);
}
#Test
public void calculateMarketValueTest() {
Assertions.assertEquals(portfolio.calculateMarketValue(),37500);
}
#Test
public void calculateIsInProfitTest() {
Assertions.assertTrue(portfolio.isInProfit());
}
}
Using initmocks() in the PortfolioTest.java runs the test smoothly.
Using #Rule, throws NPE
Using #RunWith, throws NPE
Please suggest the correct way for using #Rule & #RunWith in a smooth. Also provide brief difference between these 3 mechanisms of instantiating mocks.
Your POM reveals that you might somehow mix JUnit5 and JUnit4. There are two annotations to declare method as test:
#org.junit.Test // for JUnit4
#org.junit.jupiter.api.Test // for JUnit5
If you annotate for JUnit4 and run test as JUnit4 test everything should be fine.
But if you annotate with JUnit5 and run as JUnit5 test things will be a bit different. JUnit5 does not use #Rule nor #RunWith but has its own way to initialize mocks. Article which might explain why something works and something not.
In a nutshell, for running as JUnit5 your class should have annotation:
#ExtendWith(MockitoExtension.class)
because #Rule or RunWith does not work anymore. This annotation needs dependency:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>2.23.0</version>
<scope>test</scope>
</dependency>
I run only JUnit5 and have these dependencies
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.5.2</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>2.23.0</version>
<scope>test</scope>
</dependency>
Related question
In Junit5, #RunsWith() is replaced with #ExtendWith({MockitoExtension.class}), where MockitoExntension is coming from
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-junit-jupiter -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>3.11.2</version>
<scope>test</scope>
</dependency>
3.11.2 is the current version at the time I am posting the solution. For more info you can visit: Mockito and Junit5
The maven plugin I am developing now injects some dependencies using Google Guice with JSR-330. For unit testing I am using maven-plugin-testing-harness. The plugin works perfectly. But there is a problem with tests. I want to inject mocked components into mojo, but still there are real objects in tests.
I've tried to write my custom test module as it's said in Google Guice Bound Field, but it didn't work. After some debugging I found out that Plexus container doesn't allow to use custom modules.
There are my mojo:
package my.maven.plugins.myplugin;
import my.maven.plugins.myplugin.component.MyComponent;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import javax.inject.Inject;
#Mojo(name = "resolve-property-value")
public class MyMojo extends AbstractMojo {
private final MyComponent component;
#Parameter(readonly = true, defaultValue = "${project}" )
private MavenProject project;
#Inject
public MyMojo(MyComponent component) {
this.component = component;
}
#Override
public void execute() {
String value = component.resolvePropertyValue();
project.getProperties().setProperty("some.property", value);
}
}
Component's interface:
package my.maven.plugins.myplugin.component;
public interface MyComponent {
String resolvePropertyValue();
}
And implementation
package my.maven.plugins.myplugin.component.impl;
import my.maven.plugins.myplugin.component.MyComponent;
import javax.inject.Named;
#Named
public class MyComponentImpl implements MyComponent {
#Override
public String resolvePropertyValue() {
return "someValue";
}
}
Test:
package my.maven.plugins.myplugin;
import my.maven.plugins.myplugin.component.MyComponent;
import org.apache.maven.plugin.Mojo;
import org.apache.maven.plugin.testing.MojoRule;
import org.apache.maven.project.MavenProject;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Properties;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doReturn;
public class MyMojoTest {
#Mock
private MyComponent component;
#Rule
public MojoRule mojoRule = new MojoRule();
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void should_set_some_property() throws Exception {
doReturn("testValue").when(component).resolvePropertyValue();
MavenProject project = new MavenProject();
Mojo goal = mojoRule.lookupConfiguredMojo(project, "resolve-property-value");
goal.execute();
Properties properties = project.getProperties();
assertTrue(properties.containsKey("some.property"));
assertEquals("testValue", properties.get("some.property"));
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>my.maven.plugins</groupId>
<artifactId>my-plugin</artifactId>
<version>0.1.0-SNAPSHOT</version>
<packaging>maven-plugin</packaging>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.version>3.6.0</maven.version>
<maven-test.version>3.3.0</maven-test.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
<version>${maven.version}</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>${maven.version}</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>${maven.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>21.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven.resolver</groupId>
<artifactId>maven-resolver-api</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-compat</artifactId>
<version>${maven.version}</version>
<scope>test</scope>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.apache.maven.plugin-testing</groupId>
<artifactId>maven-plugin-testing-harness</artifactId>
<version>${maven-test.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.23.4</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>${maven.version}</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
Is there any way to use mockito in plugin unit tests?
I suppose there is no "magic" ways to inject mocked components. So I've decided to put it into container explicitly in setUp section
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mojoRule.getContainer().addComponent(component, MyComponent.class, "");
}