I am trying unit tests where an aspect class works well while unit testing.
Situtation.
upgrade Spring Boot from 1.5.9 -> 2.3.1.
Mockito, Junit frameworks are bumped up (mockito-core 1.10.19 -> 3.3.3)
I have a problem that unit tests for Spring AOP (internally AspectJ) will not pass while upgrading. The cause is from where AbstractAspectJAdvisorFactory class validates whether a given aspect class is a real aspect class or not.
My code
#Mock
private RepositoryAspect aspect;
#InjectMocks
private NicknameRepository nicknameRepository;
#Before
public void setup() {
AspectJProxyFactory aspectJProxyFactory = new AspectJProxyFactory(nicknameRepository);
aspectJProxyFactory.addAspect(aspect); // fails
nicknameRepository = aspectJProxyFactory.getProxy();
}
https://github.com/spring-projects/spring-framework/blob/0819a9fcc9a1168521995e0bac7de5633a819780/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactory.java#L105-L110
public void validate(Class<?> aspectClass) throws AopConfigException {
// If the parent has the annotation and isn't abstract it's an error
if (aspectClass.getSuperclass().getAnnotation(Aspect.class) != null &&
!Modifier.isAbstract(aspectClass.getSuperclass().getModifiers())) {
throw new AopConfigException("[" + aspectClass.getName() + "] cannot extend concrete aspect [" +
aspectClass.getSuperclass().getName() + "]");
}
...
It worked as normal. However, after upgrading from mockito-core 1.10.19 to 3.3.3, I found the validation fails due to different wrapped Mock class.
1.10.19
<instance>$$EnhancerByMockitoWithCGLIB => not classified as a Mock class
3.3.3
<instance>$$MockitoMock => classified wrapped Mock class, not an aspect class
My research is below. I just found Spring AOP does not use Mockito as an injected aspect, but a real instance and its variables. I couldn't find a right way expect the code. How can I try with Mockito?
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/aop/aspectj/annotation/AspectJProxyFactory.html
http://useof.org/java-open-source/org.springframework.aop.aspectj.annotation.AspectJProxyFactory
https://github.com/spring-projects/spring-framework/blob/3aa2605fdaa56d5c007f476f3f9bd1c550ec368c/spring-context/src/test/java/org/springframework/aop/aspectj/BeanNamePointcutAtAspectTests.java
Replacing
aspectJProxyFactory.addAspect(aspect);
with
aspectJProxyFactory.addAspect(RepositoryAspect.class);
Gets past this problem and the aspect is triggered (though it doesn't help resolve verifying the aspect itself in test, if that's the goal)
Related
Is there a more concise / elegant way to reach this functionality in Micronaut?
#MicronautTest
class ControllerTest {
#Inject
#field:Client("/")
lateinit var client: RxHttpClient
#Inject
lateinit var redeemService: RedeemService
#MockBean(RedeemService::class)
fun redeemService(): RedeemService = mockk {
every { validate(any()) } returns true
}
#Test
fun test() {
// some logic triggering a call to redeemService
verify(exactly = 1) {
redeemService.validate("123")
}
}
}
To me it looks like redundant work to have to declare the #MockBean and then also having to #Inject the previously declared #MockBean.
From what I remember, in Spring Boot this is just an annotation on a lateinit var.
Did I misunderstand or overlook something?
You need the mock bean, for replacing the service and inject it everywhere in your application .. if you want to do some verification you eighter have to inject it back or create instance within your class and return that with the mock bean
private val redeemService: RedeemService = mockk {
every { validate(any()) } returns true
}
#MockBean(RedeemService::class)
fun redeemService() = redeemService
Usually, MockBean are NO-OP collaborators that are intended to be DI-ed into dependent beans instead of relying on concrete bean implementations.
If within a test fixture ~ your tested feature scope ~ you have to verify collaborator calls or provide custom response to other collaborators calls, then you have to inject it.
This may seem, as stated, like redundancy but it's not and it SHOULD be done in such a way otherwise Micronaut (or any other framework doing so) would have a little design flaw.
Relying on custom implementations (annotations in this case) to act as bean providers and injection points within tests would result in non-deterministic behavior and internal tight-coupling to framework specificities as you will be injecting your dependencies in your test fixture differently from the way you'll be doing it in your actual application code, and that should be avoided.
In short, if your bean (mock or concrete) make it to the your test implementation through the #Inject annotation, then you are assured it will work the same when injected into real application code since a test fixture is a bean itself.
I am moving from Thorntail to Quarkus.
In my tests I used to create a #deployment method in which I put only what was needed by the tests. In particular I didn't put a Class having a #Startup annotation (because I didn't want to test that ...).
When I moved to QUARKUS, I suppress de #deployment static method, then when I launch the tests #Startup is ... started and a lot of bad things happen which prevent me from testing what I want to test (well, it crashes because it tries to connect to services which are not available).
So the question is : is there a way to exclude some package or class when lauching a test with quarkusTest ?
I finally created a class :
#ApplicationScoped
public class ApplicationLifeCycle {
private final Logger log = Logger.getLogger(getClass());
#Inject
Startup startup;
void onStart(#Observes StartupEvent ev) {
log.info("The application is starting with profile " + ProfileManager.getActiveProfile());
if (!ProfileManager.getActiveProfile().equalsIgnoreCase("test")) {
startup.getModel();
}
}
void onStop(#Observes ShutdownEvent ev) {
log.info("The application is stopping...");
startup.stopMQ();
}
}
A bit ugly isn't it ?
Is there a better way to do it ?
Quarkus adds a few capabilities to handle such scenarios regarding CDI, which can be found here: https://quarkus.io/guides/cdi-reference#enable_build_profile
For instance:
#ApplicationScoped
#UnlessBuildProfile("test")
class ConnectsToExternalService implements ExternalConnector {}
Will prevent CDI from injecting this implementation when the quarkus profile is "test", which quarkus sets when running a #QuarkusTest.
So if you inject "ExternalConnector" in a #QuarkusTest, you'll get an Unsatisfied dependency exception, which can be fixed with:
#ApplicationScoped
#IfBuildProfile("test") // not required if class is in "test" dir
class MockExternalService implements ExternalConnector {}
Using this approach you can prevent external connections but also mock them.
To top things off, one can now disable all StartupEvent and ShutdownEvent observers declared on application using a QuarkusTestProfile in combination with #TestProfile (See Quarkus Testing with Different Profiles). You have to return true in disableApplicationLifecycleObservers in that case.
I'm porting an app across from JDBC / REST to spring-data-rest and my one and only unit test with fails with the error
NoSuchBeanDefinitionException:
No qualifying bean of type 'com.xxx.repository.ForecastRepository' available
The app was retro-fitted with spring-boot just previously, and now I'm trying to put a new layer in place with spring-data-rest on top of spring-data-jpa.
I'm attempting to work out the correct Java-config according to
Custom Test Slice with Spring Boot 1.4
but I had to deviate from the idiomatic approach because
the #WebMvcTest annotation doesn't suppress the security module which causes the test to fail
the #MockMvcAutoConfiguration fails due to missing dependencies unless I specify #SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK) (see here)
#WebMvcTest and #SpringBootTest are mutually exclusive since they both specify #BootstrapWith and can't run together
So this is the closest I've got but Spring can't locate my #RepositoryRestResource repository:
Repository
#RepositoryRestResource(collectionResourceRel = "forecasts", path = "forecasts")
public interface ForecastRepository extends CrudRepository<ForecastExEncoded,
Long> {
JUnit Test
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK,
classes = {TestRestConfiguration.class})
public class ForecastRestTests {
#Autowired
private MockMvc mockMvc;
#Autowired
private ForecastRepository forecastRepository;
#Before
public void deleteAllBeforeTests() throws Exception {
forecastRepository.deleteAll();
}
#Test
public void shouldReturnRepositoryIndex() throws Exception {
mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk()).andExpect(
jsonPath("$._links.forecasts").exists());
}
}
Configuration
#OverrideAutoConfiguration(enabled = false)
#ImportAutoConfiguration(value = {
RepositoryRestMvcAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
WebMvcAutoConfiguration.class,
MockMvcAutoConfiguration.class,
MockMvcSecurityAutoConfiguration.class
})
#Import({PropertySpringConfig.class})
public class TestRestConfiguration {}
Also tried...
I tried to configure the unit test with just #WebMvcTest and this #ComponentScan below from How to exclude AutoConfiguration from Spring Boot), in an attempt to simplify it all, however the excludeFilters had no effect.
#ComponentScan(
basePackages="com.xxx",
excludeFilters = {
#ComponentScan.Filter(type = ASSIGNABLE_TYPE,
value = {
SpringBootWebApplication.class,
JpaDataConfiguration.class,
SecurityConfig.class
})
})
I've set Spring's logging to trace because all I can do at this point is try to find clues as to what is happening from log output. So far though without any luck.
I can see in the logging that RepositoryRestConfiguration is loading, but obviously it isn't fed with the right info and I am unable to work out how that is done, after googling and pouring over the Spring docs and API. I think I must have read every relevant question here on SO .
Update 2016-11-16 10:00
One thing I see in the logs which concerns me is this:
Performing dependency injection for test context [DefaultTestContext#2b4a2ec7 [snip...]
classes = '{class com.xxx.TestRestConfiguration,
class com.xxx.TestRestConfiguration}',
i.e. the context lists the configuration class twice. I specified the config class (once only) on the #SpringBootTest#classes annotation. But if I leave off the #classes from the annotation, Spring Boot finds and pulls in all the config via the #SpringBootApplication class.
So is that a hint that I am specifying the configuration in the wrong place? How else would I do it?
After way too much time, I settled on this approach.
Custom Test Slice with Spring Boot 1.4 looked promising but I couldn't get anywhere with it.
While going over and over
Accessing JPA Data with REST
I realised I had to include the JPA setup because spring-data-rest is using them directly - no chance to mock them or run unit tests without an embedded database.
At least not as far as I understand it. Maybe it is possible to mock them and have spring-data-rest run on the mocks against test data, but I think spring-data-rest and spring-data are probably too tightly coupled.
So integration testing it must be.
In the Spring source code provided with the articles above
gs-accessing-data-rest/ApplicationTests.java
the logging shows Spring Boot pulling in the whole configuration for the application context.
So that my SpringBootApplication class is avoided and the security module isn't loaded up, I set up my tests like this:
#RunWith(SpringRunner.class)
#SpringBootTest
#ContextConfiguration(classes = {
JpaDataConfiguration.class,
TestJpaConfiguration.class,
TestRestConfiguration.class,
PropertySpringConfig.class})
public class ForecastRestTests {
#SuppressWarnings("SpringJavaAutowiringInspection")
#Autowired
private MockMvc mockMvc;
#Autowired
private ForecastRepository forecastRepository;
#Before
public void deleteAllBeforeTests() throws Exception {
forecastRepository.deleteAll();
}
#Test
public void shouldReturnRepositoryIndex() throws Exception {
mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk()).andExpect(
jsonPath("$._links.forecasts").exists());
}
}
with these configuration classes:
#Configuration
#EnableJpaRepositories(basePackages = {"com.bp.gis.tardis.repository"})
#EntityScan(basePackages = {"com.bp.gis.tardis.type"})
public class JpaDataConfiguration {
and
#Configuration
#OverrideAutoConfiguration(enabled = false)
#ImportAutoConfiguration(value = {
CacheAutoConfiguration.class,
JpaRepositoriesAutoConfiguration.class,
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class,
TransactionAutoConfiguration.class,
TestDatabaseAutoConfiguration.class,
TestEntityManagerAutoConfiguration.class })
public class TestJpaConfiguration {}
and
#Configuration
#OverrideAutoConfiguration(enabled = false)
#ImportAutoConfiguration(value = {
RepositoryRestMvcAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
WebMvcAutoConfiguration.class,
MockMvcAutoConfiguration.class,
MockMvcSecurityAutoConfiguration.class
})
public class TestRestConfiguration {}
so the TL;DR summary is: use #ContextConfiguration to specify the configuration files that specify #OverrideAutoConfiguration and #ImportAutoConfiguration
I am using ninject to inject dependencies in my production environment. I see two options when it comes to writing unit tests. I can either create concrete classes and inject them using ninject, Or I can use a mocking framework like just mock.
My thought process is to just use both and have the deciding factor be whether or not the TestInterface can be constructed in a reusable way. This way we dont waste time writing the same Mocked method to return an empty list over and over again.
Is there a best practice for this type of thing?
With unit tests on class, it doesn't make a lot of sense to include the DI container in the "system under test" (SUT).
by principle, a class unit test should test the class and the class only
usually you can't "reuse" the bindings in the unit test, you have to create them unit-test specific. Therefore you're only re-testing ninject, but not how you're applying it. Ninject is already tested. So no real benefit for you.
If you do acceptance testing / unit testing on component or application level, then it makes perfectly sense to include Ninject in the SUT.
For a class-level unit test one usually takes a dynamic proxy based mocking framework like MOQ or FakeItEasy.
given an implementation:
public interface IDependency {
void Foo(string bar);
}
public class SomeClass
{
private readonly IDependency dependency;
public SomeClass(IDependency dependency)
{
this.dependency = dependency;
}
public void SomeMethod(string arg)
{
this.dependency.Foo(arg);
}
}
a test would look like (xUnit flavor):
public class SomeClassTest
{
private readonly Mock<IDependency> dependency;
private SomeClass testee;
public SomeClassTest()
{
this.dependency = new Mock<IDependency>();
this.testee = new SomeClass(this.dependency.Object);
}
[Fact]
public void SomeMethod_MustPassArgumentToFoo()
{
const string expectedArgument = "AnyArgument;
this.testee.SomeMethod(expectedArgument);
this.dependency.Verify(x => x.Foo(expectedArgument));
}
}
JustMock has NInject built into it plus a mocking container based on it.
Injecting mocks of dependencies is done automatically when you fetch the instance for the first time:
var container = new MockingContainer<ClassUnderTest>();
var testee = container.Instance;
Plus, you can use NInject's syntax for fine-grained control of the injection behavior, as well as JustMock's syntax for configuring mock behavior.
This is a follow-on question to that posted here:
How to initialise/wire beans in Grails Spock unit tests?
I haven't managed to find an example of where a spring bean is written in java within src/java and which is then unit tested within Grails using Spock.
Given:
// MyBean.java
// this needs to be in java as it is playing with spring-data-neo4j
package com.me;
public class MyBean {
#Autowired
def someNeo4jBeanThatCannotBeTestedByItself
String readFromDb() {
// this will execute code to actually read from a DB
return "Hello from DB";
}
}
Note that "someNeo4jBeanThatCannotBeTestedByItself" is a bean that is associated with spring-data-neo4j and I want to see that my code actually writes stuff here, so I want my unit/integration test to load spring beans (I don't want to mock this out).
What does the test case look like, is it an Integration test? I've tried a couple of variations, but can't get the spring beans to be initialised by Grails test-app.
I know this is old post. As this is not answered yet, i'm trying to answer. If you figured out already, you can ignore.
I believe for this you can write Unit Test. But if this class is under, src/groovy or src/java spring autowire doesn't work. You need to add bean,someNeo4jBeanThatCannotBeTestedByItself and MyBean, in `resources.groovy' file as shown below:
beans={
someNeo4jBean (Neo4jBeanClass)
myBean(MyBean){
someNeo4jBeanThatCannotBeTestedByItself = ref(someNeo4jBean)
}
}
You can remove #Autowired annotation in your bean,MyBean
Now for spec to work , you need to add this property to your spec, static loadExternalBeans = true, then it loads beans present in resources.groovy file.
Sample Spec:
#TestMixin(GrailsUnitTestMixin)
class MyBeanSpec extends Specification {
static loadExternalBeans =true//this loads beans present in resources.groovy
def myBean
def setup() {
myBean= applicationContext.getBean("myBean")
//now you can use this `myBean` in test cases..
}
}
I also had similar situation and i got rid of with this approach.
Hope this helps.