I have created a wrapper for logging over log4j extending HandlerInterceptorAdapter keeping build type maven project. I am using this project as an external JAR in my Spring Boot application. Using Logger in my controllers. I have written JUnits for it which are working fine but got exception during mvn install.
DemoLogger is an interface exposing functions to log. DemoLogger is part of maven project which is an external jar.
Controller
#RestController #RequestMapping("/api/v1")
public class DemoController {
private DemoLogger logger = LoggerFactory.createLog(DemoController.class);
#GetMapping("/demo")
public ResponseEntity<String> getString() {
logger.info("This is test debug");
return new ResponseEntity("Hello World", HttpStatus.OK);
}
}
JUnit
public class DemoControllerTest {
#InjectMocks private DemoController demoController;
#Before public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test public void shouldReturnString()throws Exception {
final String body = demoController.getString().getBody().toString();
assertEquals("Hello World", body);
}
}
Error Log
Cannot instantiate #InjectMocks field named 'demoController' of type 'class dev.example.controller.DemoController'.
You haven't provided the instance at field declaration so I tried to construct the instance.
However the constructor or the initialization block threw an exception : dev/example2/logger/factory/LoggerFactory
at dev.example.controller.DemoControllerTest.setup(DemoControllerTest.java:18)
Caused by: java.lang.NoClassDefFoundError: dev/example2/logger/factory/LoggerFactory
This seems more like a Maven problem that Mockito.
Most likely you are using that jar without specifying it in your pom as a dependency.
It works in your local IDE as most likely you added it manually to the classpath.
Try to install that jar in your local .m2 (or ideally on your company Nexus or something similar) and then run the build:
mvn install:install-file
More info here.
Related
I have a Spring Boot project where I have an external dependency jar. In that jar there is an interface as below :
public interface Feature {
List<FeatureResponse> getFeatures(FeatureRequest req);
}
Its implementation is:
#Service
public class FeatureImpl implements Feature {
public List<FeatureResponse> getFeatures(FeatureRequest featureReq) {
// do something
return featureList;
}
}
Now in my Spring Boot project inside one my class I have used the request and response to object the feature list. However, though it compiles, it fails to run. The error is simple bean creation error at the injection point of Autowired that we get. Below is my class:
#Component
public class FeatureServiceImpl implements featureService {
#Autowired
Feature feature;
-----
}
The injection with #Autowired is not happening here.
In your configuration class or your SpringBootApplication class add the following
#ComponentScan({"base.package.of.your.currentJar","base.package.of.your.ImportedJar"})
It seems that #WebMvcTest and #MockBean are not working as expected. Maybe I'm missing something... I have a controller with some dependencies I'm mocking with #MockBean, but the application fails to start because it cannot find another bean that I think should not be required in this case.
CONTROLLER:
#RestController
public class ExchangeRateStoreController {
private AddExchangeRate addExchangeRate;
private AddExchangeRateRequestAdapter addExchangeRateRequestAdapter;
private GetExchangeRate getExchangeRate;
private GetExchangeRateRequestAdapter getExchangeRateRequestAdapter;
#Autowired
public ExchangeRateStoreController(ExchangeRateRepository exchangeRateRepository, ExchangeRateDateValidator exchangeRateDateValidator, ExchangeRateView exchangeRateView) {
addExchangeRate = new AddExchangeRate(exchangeRateRepository, exchangeRateDateValidator);
addExchangeRateRequestAdapter = new AddExchangeRateRequestAdapter();
getExchangeRate = new GetExchangeRate(exchangeRateView);
getExchangeRateRequestAdapter = new GetExchangeRateRequestAdapter();
}
#PostMapping
#ResponseStatus(HttpStatus.CREATED)
public void create(#RequestBody AddExchangeRateRequest addExchangeRateRequest) {
addExchangeRate.execute(addExchangeRateRequestAdapter.toCommand(addExchangeRateRequest));
}
}
TEST:
#RunWith(SpringRunner.class)
#WebMvcTest(ExchangeRateStoreController.class)
public class ExchangeRateStoreControllerTest {
#Autowired
private MockMvc mvc;
#MockBean
ExchangeRateRepository exchangeRateRepository;
#MockBean
ExchangeRateDateValidator exchangeRateDateValidator;
#MockBean
ExchangeRateView exchangeRateView;
#Test
public void givenValidExchangeRateCommand_whenCreate_thenOK() throws Exception {
String validRequestBody = "{\"from\":\"EUR\",\"to\":\"USD\",\"amount\":1.2345,\"date\":\"2018-11-19\"}";
doNothing().when(exchangeRateDateValidator).validate(any());
doNothing().when(exchangeRateRepository).save(any());
mvc.perform(post("/").content(validRequestBody).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isCreated());
}
APPLICATION:
#SpringBootApplication
#EnableJpaRepositories("com...exchangerate.store.infrastructure.persistence")
#EntityScan("com...exchangerate.store.infrastructure.persistence")
#ComponentScan(basePackages = {"com...exchangerate.store.infrastructure", "com...exchangerate.store.application"} )
public class ExchangeRateStoreApplication {
public static void main(String[] args) {
SpringApplication.run(ExchangeRateStoreApplication.class, args);
}
}
And the error I get when run the test:
APPLICATION FAILED TO START
Description:
A component required a bean named 'entityManagerFactory' that could
not be found.
Action:
Consider defining a bean named 'entityManagerFactory' in your
configuration.
But, as you can see, entityManagerFactory is not a controller's dependency. So, why is the test trying to load this bean? I'm mocking all the controller dependencies, so I think it shouldn't do this.
The problem's caused by your use of #EnableJpaRepositories on your application's main class. By placing it on the main class, you're indicating that JPA repositories must always be enabled, irrespective of which particular slice of functionality you're trying to test.
You can fix your problem by doing one of the following:
Move #EnableJpaRepositores and #EntityScan onto a separate JPA-specific configuration class
Remove #EnableJpaRepositories and #EntityScan and rely on the auto-configured defaults. For this to work, your repositories and entities will have to be in a sub-package of your main class's package.
There's some more information about this in Spring Boot's reference documentation where it says the following:
If you use a test annotation to test a more specific slice of your application, you should avoid adding configuration settings that are specific to a particular area on the main method’s application class.
In this particular case, the configuration setting that is specific to a particular area is #EnableJpaRepositories.
Hello there is an excellent link on this post: EntityManagerFactory not found in SpringBoot
You could check your spring boot jpa integration and have some good tips to set up your environment.
This method is empty in all my JUnit test cases. What is the use of this method?
Sonarqube is complaining
"Add a nested comment explaining why this method is empty, throw an UnsupportedOperationException or complete the implementation."
I can bypass this by adding some comment but I just want to know why it is necessary.
When you build a Spring boot application using Spring Initializer. It auto creates a test class for you with contextLoads empty method.
#SpringBootTest
class ApplicationContextTest {
#Test
void contextLoads() {
}
}
Note the use of #SpringBootTest annotation which tells Spring Boot to look for a main configuration class (one with #SpringBootApplication, for instance) and use that to start a Spring application context. Empty contextLoads() is a test to verify if the application is able to load Spring context successfully or not.
If sonarqube is complaining about the empty method then you can do something like this to verify your controller or service bean context:-
#SpringBootTest
public class ApplicationContextTest {
#Autowired
private MyController myController;
#Autowired
private MyService myService;
#Test
public void contextLoads() throws Exception {
assertThat(myController).isNotNull();
assertThat(myService).isNotNull();
}
}
Use different runner,
if you are using SpringRunner.class, use an alternative one such as MockitoJUnitRunner.class or MockitoJunitRunner.class rather then SpringRunner.class
#Runwith(SpringRunner.class)
#Runwith(JUnit4ClassRunner.class)
#Runwith(MockitoJUnit4Runner.class)
#Runwith(MockitoJUnitRunner.class)
I have a spring boot application with the cucumber and selenium test setup. I am trying to create a UI wrapper for running my cucumber test scenarios. I need to run the selected feature files for which i am using the cucumber.api.cli.Main.run method.
The problem is i am trying to pick properties through my application.yml files, but my step definition class is not able to pick the properties.
This is how my code looks like -
RunCukes Class
#RunWith(Cucumber.class)
#CucumberOptions(features = {"classpath:features"},
plugin = { "pretty", "html:target/cucumber-html-report","json:target/cucumber.json" },
tags = {"~#ignore"})
public class RunCukesTest {
}
The class from where the cucumber feature files are run
#Service
public class SeleniumLogic {
#Autowired
RunCukesTest runCukes;
public byte runTest(String[] argv) throws IOException{
byte result = cucumber.api.cli.Main.run(argv,runCukes.getClass().getClassLoader());
return result;
}
}
The stepdefinition class
#Component
public class LoginTestSteps {
#Autowired
private LoginPage loginPage;
#Value("${host.name}")
private String HOST_NAME;
#Given("^User is on the login page$")
public void user_is_on_the_login_page() throws Throwable {
loginPage.load(HOST_NAME);
}
}
Application.yml
host:
name: abc.com
The HOST_NAME is coming as null in the LoginTestSteps class.
Came across the same issue, my conclusion was that it's not possible to do it like that. The application.yml is a configuration file for Spring, therefore with a Cucumber project you'll have to do your own configuration file format and read from it. You can have a
#Value("${host.name:abc.com}")
So that abc.com is your default, and you can override it when running the project with a
java -jar -Dhost.name=myotherurl.com pathToYourJar/myJar.jar
Try this:
#Component
#PropertySource("classpath:application.properties")
public class LoginTestSteps {
I am writing tests with Arquillian embedded. But i am facing issue when my bean is in View Scope. I just posted my sample code. When my DataBean is in ViewScope it doesn't run and throws some exception. But when i changed it to RequestScope it worked fine.
#RunWith(Arquillian.class)
public class MockTest {
#Deployment
public static Archive<?> createDeployment() {
JavaArchive jar = ShrinkWrap.create(JavaArchive.class)
.addClass("pack.ui.DataBean")
.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
return jar;
}
#Inject
private DataBean dataBean;
#Test
public void testDataBean() throws Exception {
dataBean.checkSystemStatus();
Assert.assertEquals(status, true);
}
#ViewScoped
#Named("dataBean")
public class DataBean {
public boolean checkSystemStatus() {
return true;
}
}
Can someone please tell, Can we use ViewScope with Arquillian or anything else i have to do.
It's because the view scope is not active during the invocation of your test. To run it this way, you'll need to use something like drone/graphene. It's not active because the HTTP request that runs is against the arquillian test runner servlet, not the webpage of your application. ViewScope is specific to a page in your application.
You can mock JSF context of controllers and get rid of annoying exception "No active contexts for scope type ViewScoped" during Arquillian test execution.
Check the original project for JSF 2.0: https://github.com/it-crowd/mock-contexts-extension
or my upgrade for JSF 2.2: https://github.com/kesha/mock-contexts-extension
All you need is the additional annotation #ViewScopeRequired before the test method.