Spring boot 2.2.4 spy bean integration test is not a Mockito mock - spring

In my free time I'm migrating from spring boot 1.5 to newsest (2.2.4). I think I'm pretty close to the end. Having this dependencies:
org.mockito:mockito-core:2.28.2
org.mockito:mockito-junit-jupiter:2.18.3
com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0
org.springframework.boot:spring-boot-starter-test:2.2.4.RELEASE
Currently forcing problem probably with #SpyBean
#RunWith(SpringRunner::class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class abc {
#SpyBean
lateinit var abcRepository: AbcRepository
enter code here
...
#Test
fun test {
...
val abcCaptor = ArgumentCaptor.forClass(Abc::class.java)
verify(abcRepository, times(2)).save(abcCaptor.capture())
}
And the error is. As I can see in debug department repository is Proxy -> SimpleJpaRepository
VerificationStartedEvent.setMock() does not accept parameter which is not a Mockito mock.
Received parameter: org.springframework.data.jpa.repository.support.SimpleJpaRepository#7f48943b.
See the Javadoc.

Related

autowore mockmvc is null in controller test after I upgrade spring framework to 6.0.1

using:
springboot version is 3.0.1
spring framework version 6.0.1
java version 17
after I upgrade my spring framework version, I refactor security configuration to this
#WebMvcTest(NominationController.class)
#AutoConfigureMockMvc
class NominationIsolationTest extends Specification {
#Autowired
MockMvc mockMvc
...
def "should return status created with response when post nominations given valid nomination"() {
given:
...
when:
def response = mockMvc")
.content(body).contentType(MediaType.APPLICATION_JSON))
then:
...
}
but it failed, and I debug find that the mockMvc' value is null, and I use spock to do the controller test
I have try to add #SpringBootTest annotation, but not work
before I upgrade, the controller test can run successfully
now I fixed this issue, by add #ContextConfiguration annotation on test class, and autowire WebApplicationContext then use mvcbuilder to create mockmvc, after that, it fixed
#WebMvcTest
#ContextConfiguration
class IsolationTest extends Specification {
#Autowired
WebApplicationContext wac
MockMvc mockMvc
def setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build()
}

Kotlin, Spring book, Mockito, #InjectMocks, Using different mocks than the ones created

I am trying to test a class like
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#SpringBootTest(classes = [TestConfig::class])
#ExtendWith(MockitoExtension::class)
class TotalCalculatorTests {
#Mock
lateinit var jdbcRepo: JDBCRepo
#Mock
lateinit var userCache : LoadingCache<ApproverLevel, List<Approver>>
#InjectMocks
lateinit var calculator: TotalCalculator
#Test
fun totalOrder() {
// val calculator = TotalCalculator(userCache,jdbcRepo)
calculator.total(ItemA(),ItemB())
verify(jdbcRepo).getTotal()
}
}
I get an error Actually, there were zero interactions with this mock. but if I uncomment the // val calculator = TotalCalculator(userCache,jdbcRepo) it works. I assumed mockito would be using the mocks from the test class but that appears to not be true. How can I get the instance of JDBCRepo being used by the #InjectMocks?
It looks like you would like to run the full-fledged spring boot test with all the beans but in the application context you would like to "mock" some real beans and provide your own (mock-y) implementation.
If so, the usage of #Mock is wrong here. #Mock has nothing to do with spring, its a purely mockito's thing. It indeed can create a mock for you but it won't "substitute" the real implemenation with this mock implementation in the spring boot's application context.
For that purpose use #MockBean annotation instead. This is something from the spring "universe" that indeed creates a mockito driven mock under the hood, but substitutes the regular bean in the application context (or even just adds this mock implementation to the application context if the real bean doesn't even exist).
Another thing to consider is how do you get the TotalCalculator bean (although you don't directly ask this in the question).
The TotalCalculator by itself is probably a spring been that spring boot creates for you, so if you want to run a "full fledged" test you should take the instance of this bean from the application context, rather than creating an instance by yourself. Use annotation #Autowired for that purpose:
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#SpringBootTest(classes = [TestConfig::class])
#ExtendWith(MockitoExtension::class) // you don't need this
class TotalCalculatorTests {
#MockBean
lateinit var jdbcRepo: JDBCRepo
#MockBean
lateinit var userCache : LoadingCache<ApproverLevel, List<Approver>>
#Autowired // injected by the spring test infrastructure
lateinit var calculator: TotalCalculator
#Test
fun totalOrder() {
// val calculator = TotalCalculator(userCache,jdbcRepo)
calculator.total(ItemA(),ItemB())
verify(jdbcRepo).getTotal()
}
}
Now, all this is correct if your purpose to indeed running the full microservice of spring boot and make an "integration" testing
If alternatively you just want to check the code of your calculator, this might be an overkill, you can end up using mockito and plain old unit testing. In this case however you don't need to even start the spring (read create an application context) during the test, and of course there is no need to use #SpringBootTest / #MockBean/#Autowired.
So it pretty much depends on what kind of test is really required here

Spring Boot 2.1/2.2 testing - how to test a single controller without creating beans for everything else?

Before in Spring Boot 2.0 I had something like:
#RunWith(SpringRunner::class)
#DataJpaTest
#AutoConfigureMockMvc
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
#ActiveProfiles("unit-test")
#SpringBootTest
#WithUserDetails
class MyControllerTest {
#InjectMocks
lateinit var myController: MyController
lateinit var mvc: MockMvc
#Before
fun setup() {
mvc = MockMvcBuilders.standaloneSetup(myController).build()
}
...
But after trying to upgrade to Spring Boot 2.1, I get various random errors, such as:
WithUserDetails not working: java.lang.IllegalStateException: Unable to create SecurityContext using #org.springframework.security.test.context.support.WithUserDetails(value=user, userDetailsServiceBeanName=, setupBefore=TEST_METHOD)
Irrelevant beans were (attempting) to be created: kotlin.UninitializedPropertyAccessException: lateinit property <property> has not been initialized - this was from a #ConfigurationProperties class.
and a few more other stuff that just didn't make sense to me (in 2.2, I can't have both #DataJpaTest and #SpringBootTest together either).
Does anyone have an idea for what I would need to do to correctly update these unit tests?
You use either a sliced test #WebMvcTest or a full integration test with #SpringBootTest. Hence it has no sense in using them together. In your case you want to test a controller, then use the #WebMvcTest and mock the dependencies.
#RunWith(SpringRunner::class)
#WebMvcTest(MyController.class)
#WithUserDetails
class MyControllerTest {
#Autowired
lateinit var myController: MyController
#Autowired
lateinit var mvc: MockMvc
#MockBean
var myService: MyServiceForController
Use #MockBean to mock the service dependencies for the controller and register behavior on it. You can now also simply wire the controller and a pre-setup MockMvc instance instead of wiring that yourself.

Spring boot cucumber testing

I try to perform some Cucumber tests of a Spring boot application.
It seems like Spring Boot is not started before tests are running.
What am i missing?
https://bitbucket.org/oakstair/spring-boot-cucumber-example
My Cucumber repo still runs without doing all the above steps:
https://github.com/BarathArivazhagan/Cucumber-spring-integration
Docs : https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html
To add more:
#SpringBootTest takes care of loading the application context in that case #ContextConfiguration is reductant.
Spring test automatically provides a bean of TestRestTemplate which can be autowired but still it should work with RestTemplate also.
It still runs without RANDOM_PORT but RANDOM port can be also used in conjunction for testing.
Let's say you have a feature file, feature1, and glueCode in org.xyz.feature1
#RunWith(Cucumber.class)
#CucumberOptions(
plugin = {"pretty"},
features = "src/test/java/resources/feature/feature1",
glue = {"org.xyz.feature1"})
public class CucumberTest {
}
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {Application.class},
webEnvironment = WebEnvironment.RANDOM_PORT)
#ContextConfiguration
#Ignore
#Transactional
public class FeatureTest extends CucumberTest {
#LocalServerPort
int randomServerPort;
#Given("........")
public void test_1 {
}
}
I found the problem and has updated the repo.
I did the following to get it working:
Added RANDOM_PORT to #SpringBootTest
Added #ContextConfiguration
Switched from RestTemplate to TestRestTemplate

How to get spring context into testcases

I am using maven project, with spring jpa.
how to test DAO methods in test cases.
And how to get spring context into the test cases
Thanks in advance.
You can use the SpringJUnit4ClassRunner JUnit runner to setup spring in JUnit tests.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"/applicationContext-1.xml", "/test-config.xml"})
#TestExecutionListeners({DependencyInjectionTestExecutionListener.class})
public class MyTest {
#Autowired
private MySpringBean bean;
}
With the DependencyInjectionTestExecutionListener you can inject spring beans in your test classes.
Have a look at the very extensive Spring Testing documentation.
Take help from below code to inject application context
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = TestConfig.clas)
public class ControllerIntegrationTests {
#Autowired
private ApplicationContext applicationContext;
}
If you are using Java based configuration then take help from the above code, if xml is being used as configuring app then replace classes with locations example as below
#ContextConfiguration(classes = TestConfig.class)
or
#ContextConfiguration(locations = "/test-context.xml")

Resources