How to mock statement in #PostConstruct for Groovy Spock Test Case - java

I have #PostConstruct in my Service class for which I am writing groovy test case. I have mocked the call inside the init() method but it doesn't work:
#Service
#Transactional
public class ServiceImpl implements Service{
private Dao testDao;
#Autowired
private AbcDao abcDao;
#PostConstruct
public void init() {
testDao = abcDao.findByName("testString");
Assert.notNull(testDao, "testDao not found");
}
public method toBeTest(){
}
}
I have written groovy test case as:
public class ServiceSpec{
#Autowired
Service service
def 'Test method'() {
given:
Dao test=new Dao()
abcDao.findByName("testString") >> test
when:
service.toBeTest()
then:
true
}
}
But it doesn't work and fail in the Assert statement.

Related

Junit Test Pass even when the method called inside may fail

I am creating a Junit Unit Test to check the createAccount method in service that calls Service a helper method. Please find it below.
Service class
public class AccountServiceImpl {
#Autowired
AccountHelper accountHelper;
#Override
public Account createAccount(Account account) throws CustomerNotFoundException {
accountHelper.checkAccountTypeForCustomer(account);
return accountRepository.save(account);
}
}
Helper Class:
public void checkAccountTypeForCustomer(Account acc) throws CustomerNotFoundException {
Boolean customerExists = customerRepository.existsById(acc.getCustomerId());
if(!customerExists) {
throw new CustomerNotFoundException("604", Message.CUSTOMER_NOT_FOUND);
}
}
AccountServiceTest class
#ExtendWith(MockitoExtension.class)
public class AccountServiceTest {
#Mock
private AccountRepository accountRepository;
#Mock
private CustomerRepository customerRepository;
#Mock
private AccountHelper accountHelper;
#InjectMocks
private AccountService testService;
#Test
void testCreateAccount() throws CustomerNotFoundException {
Account account = Account.builder().
accountType(AccountType.SAVINGS).
openingBalance(BigDecimal.valueOf(3000)).
ifsc("IFSC1").
customerId(1).
build();
testService.createAccount(account);
}
}
Above Test is passing although the customer is not present in the database.
The test is incomplete. But still the statement: testService.createAccount(account);
must fail as per my understanding.
Kindly correct me if I am wrong. I am relatively new to Junit.
However if I place the implementation for checkAccountTypeForCustomer() inside the service method instead of in the helper, the test case fails as expected.
The reason is that accountHelper is mocked in your test, which means that invocation of accountHelper.checkAccountTypeForCustomer(account) doesn't execute your business code.
I recommend you to use Spring mocking in this case, and to specify how your repository is expected to behave. It would look something like this:
#ExtendWith(SpringExtension.class)
class AccountServiceTest {
#MockBean
private CustomerRepository repository;
#Autowired
private AccountService testService;
#Test
void testCreateAccount() throws CustomerNotFoundException {
Mockito.when(repository.existsById(anyInt())).thenReturn(false);
...
CustomerNotFoundException thrown = Assertions.assertThrows(CustomerNotFoundException.class, () -> testService.createAccount(account));
Assertions.assertEquals("the exception message", thrown.getMessage());
}
}

Mockito verify number of invocations in spring boot test

I'm trying to verify that a mocked method (fileSenderService.sendFile) was called exactly 2 times. For whatever reason Mockito never fails the test, no matter what number of invocations are expected. I'm using it like this:
verify(mockService, times(expectedNumberOfInvocations)).sendFile(any(String.class), any(byte[].class));
No matter what value I use in the times() method, the test always passes.
The MyService looks like this:
#Service
public class MyServiceImpl implements MyService {
#Autowired
private FileSenderService fileSenderService;
public void createAndSendFiles(){
//doSomeStuff, prepare fileNames and fileContents(byte arrays)
//execute the sendFile twice
fileSenderService.sendFile(aFileName, aByteArray); //void method; mocked for testing
fileSenderService.sendFile(bFileName, bByteArray); //void method; mocked for testing
}
The test class
#RunWith(SpringRunner.class)
#WebAppConfiguration
#SpringBootTest(classes = {Application.class, FileSenderServiceMock.class})
#ContextConfiguration
public class MyServiceTest{
#Autowired
private MyService myService;
#Autowired
FileSenderService mock;
#Test
public void shouldCreateAndSendFiles(){
myService.createAndSendFiles(); //inside this method sendFile() is called twice
verify(mock, times(999)).sendFile(any(String.class), any(byte[].class)); //THE PROBLEM - why times(999) does not fail the test?
}
}
The FileSenderService and its mock:
#Service
public class FileSenderServiceImpl implements FileSenderService {
#Override
public void sendFile(String name, byte [] content) {
//send the file
}
}
//used for testing instead of the actual FileSenderServiceImpl
public class FileSenderServiceMock {
#Bean
#Primary
public FileSenderService getFileSenderServiceMock(){
FileSenderServicemock = Mockito.mock(FileSenderService.class, Mockito.RETURNS_DEEP_STUBS);
doNothing().when(mock).sendFile(isA(String.class), isA(byte[].class));
return mock;
}
If you are using #SpringBootTest for integration test cases you don't need to define any test config classes for mocks, you can simply use #MockBean annotation to inject mock into test context
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyServiceTest{
#Autowired
private MyService myService;
#MockBean
FileSenderService fileSenderService;
#Test
public void shouldCreateAndSendFiles(){
myService.createAndSendFiles();
verify(fileSenderService, times(2)).sendFile(any(String.class), any(byte[].class));
}
}

#Repository instance is null when invoking a method using a #Service instance from a unit test

My goal is to use an in-memory database for these unit tests, and those dependancies are listed as:
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
runtimeOnly("com.h2database:h2")
So that the repository instance actually interacts with a DB, and I dont just mock return values.
The problem is that when I run my unit test, the repository instance inside the service instance is null.
Why is that? Am I missing some annotation on the unit test class to initialise the repository instance?
This is the console output when running my unit test:
null
java.lang.NullPointerException
at com.my.MyService.findAll(MyService.java:20)
at com.my.MyTest.testMy(MyTest.java:23)
My unit test class:
public class MyTest {
#MockBean
MyRepository myRepository;
#Test
void testMy() {
MyService myService = new MyService();
int size = myService.findAll().size();
Assertions.assertEquals(0, size);
}
}
My service class:
#Service
public class MyService {
#Autowired
MyRepository myRepository;
public List<MyEntity> findAll() {
System.out.println(myRepository); // null
return (List<MyEntity>) myRepository.findAll(); // throws NullPointerException
}
#Transactional
public MyEntity create(MyEntity myEntity) {
myRepository.save(myEntity);
return myEntity;
}
}
My repository class:
#Repository
public interface MyRepository extends CrudRepository<MyEntity, Long> {
}
My entity class:
#Entity
public class MyEntity {
#Id
#GeneratedValue
public Long id;
}
Why is that? Am I missing some annotation on the unit test class to initialise the repository instance?
Basically yes :)
You need to initialise a Spring Context by Annotating your Testclass with #SpringBootTest
The other Problem you have is that you create your MyService Object manually.
By doing so SpringBoot has no chance to inject any Bean for you. You can fix this by simply injecting your MyService in your Testclass. Your Code should look something like this:
#SpringBootTest
public class MyTest {
#Autowired
private MyService myService;
#Test
void testMy() {
int size = myService.findAll().size();
assertEquals(0, size);
}
}
To use #MockBean annotation, you have to use SpringRunner to run the test. Use #RunWith Annotation on top of your test class and pass SpringRunner.class.
#RunWith(SpringRunner.class)
public class MyTest {
#MockBean
MyRepository myRepository;
#Test
void testMy() {
MyService myService = new MyService();
int size = myService.findAll().size();
Assertions.assertEquals(0, size);
}
}
The problem here is your service implementation. Using #Autowired to inject the dependency will work when you run the whole app, but it do not allow you to inject a custom dependency when you'll need it, and a good example of this is testing.
Change your service implementation to:
#Service
public class MyService {
private MyRepository myRepository;
public MyService(MyRepository myRepository){
this.myRepository = myRepository;
}
public List<MyEntity> findAll() {
System.out.println(myRepository); // null
return (List<MyEntity>) myRepository.findAll(); // throws NullPointerException
}
#Transactional
public MyEntity create(MyEntity myEntity) {
myRepository.save(myEntity);
return myEntity;
}
}
This constructor will be called by spring. Then change your test to:
public class MyTest {
#Mock
MyRepository myRepository;
#Test
void testMy() {
MyService myService = new MyService(myRepository);
int size = myService.findAll().size();
Assertions.assertEquals(0, size);
}
}
Note I have replaced #MockBean to #Mock as the previous annotation is for injecting a mock bean into the spring context, which is not needed if you're doing unit testing. If you want to boot spring context (which I would not recommend you) you need to configure your test class with #SpringBootTest or some of the other available alternatives. That will convert your test into an integration test.
PD: This test will not work if you don't provide a mock to myRepository.findAll(). Mockito default behaviour is to return null, but you're expecting it to return 0, so you'll need to do something like given(myRepository.findAll()).willReturn(0).
I believe you wish to write an integration test. Here you could remove the MockBean annotation and simply autowire your repository. Also, run with The SpringRunner class.
#RunWith(SpringRunner.class)
public class MyTest {
#Autowired
MyRepository myRepository;
#Autowired
MyService myService
#Test
void testMy() {
int size = myService.findAll().size();
Assertions.assertEquals(0, size);
}
}
This should work

Spy a method from parent class in a class with injected mocks with Mockito

I'm using Mockito in a Java project with Spring and Struts and I'm having problems with testing actions.
I'm not using the Struts2 jUnit plugin to save time with the tests using this approach: Strut 2.3.1.2 Unit test, how to remove Spring codependency vs NPE with getContext().
The problem is when in my action, when getText() is called I've got a NullPointerException.
I'm trying to spy this method, that's inherited from ActionSupport but I don't find a way because the action is annotated with InjectMocks in the test.
Here's a simplied example of the classes:
Parent:
public class ActionSupport {
public String getText(String aTextName){
return this.getTextProvider().getText(aTextName);
}
}
My action:
public class MyAction extends ActionSupport {
#Autowired private IMyService myService;
public String execute(){
getText("SomeText");
myService.something();
return SUCCESS;
}
}
Test:
#RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
#Mock private IMyService myService;
#InjectMocks private MyAction myAction;
#Before
public void before() {
MockitoAnnotations.initMocks(this);
}
#Test
public void test() {
myAction.execute();
}
}
I'm getting this exception:
java.lang.NullPointerException
at com.opensymphony.xwork2.util.TextParseUtil.translateVariables(TextParseUtil.java:167)
at com.opensymphony.xwork2.util.TextParseUtil.translateVariables(TextParseUtil.java:126)
at com.opensymphony.xwork2.util.TextParseUtil.translateVariables(TextParseUtil.java:48)
at com.opensymphony.xwork2.util.LocalizedTextUtil.getDefaultMessage(LocalizedTextUtil.java:663)
at com.opensymphony.xwork2.util.LocalizedTextUtil.findText(LocalizedTextUtil.java:534)
at com.opensymphony.xwork2.util.LocalizedTextUtil.findText(LocalizedTextUtil.java:362)
at com.opensymphony.xwork2.TextProviderSupport.getText(TextProviderSupport.java:208)
at com.opensymphony.xwork2.TextProviderSupport.getText(TextProviderSupport.java:123)
at com.opensymphony.xwork2.ActionSupport.getText(ActionSupport.java:103)
at es.MyAction.execute(MyAction.java:148)
And if I annotate MyAction with #Spy maintaining #InjectMocks I've got an StackOverflowError.
How can I spy only ActionSupport.getText() and let mockito inject mocks on my action?
I would avoid using #InjectMocks as it fails silently.
Just add a constructor to your MyAction still using #Autowired i.e. constructor injection. This also helps guarantee required dependencies.
You dont need both initMocks and MockitoJUnitRunner.
public class MyAction extends ActionSupport {
private IMyService myService;
#Autowired
public MyAction(MyService myService) {
this.myService = myService;
}
public String execute(){
getText("SomeText");
myService.something();
return SUCCESS;
}
}
#RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
#Mock
private IMyService myService;
private MyAction myAction;
#Before
public void setup() {
myAction = spy(new MyAction(myService));
}
#Test
public void test() {
assertThat(myAction.execute(), equalTo(Action.SUCCESS));
verify(myAction, times(1)).getText();
verify(myService, times(1)).something();
}
}
InjectMocks fails silently
Constructor injection discussion

How to inject mock into #Service that has #Transactional

I have any issue in my unit test where I have something along the lines of this. The mock injection get overridden on the someService if the blargh function is annotated with Transactional. If I remove the Transactional the mock stays there. From watching the code it appears that Spring lazily loads the services when a function in the service is annotated with transactinal, but eagerly loads the services when it isn't. This overrides the mock I injected.
Is there a better way to do this?
#Component
public class SomeTests
{
#Autowired
private SomeService someService;
#Test
#Transactional
public void test(){
FooBar fooBarMock = mock(FooBar.class);
ReflectionTestUtils.setField(someService, "fooBar", fooBarMock);
}
}
#Service
public class someService
{
#Autowired FooBar foobar;
#Transactional // <-- this causes the mocked item to be overridden
public void blargh()
{
fooBar.doStuff();
}
}
Probably you could try to implement your test in the following way:
#Component
#RunWith(MockitoJUnitRunner.class)
public class SomeTests
{
#Mock private FooBar foobar;
#InjectMocks private final SomeService someService = new SomeService();
#Test
#Transactional
public void test(){
when(fooBar.doStuff()).then....;
someService.blargh() .....
}
}
I could not try it right now as don't have your config and related code. But this is one of the common way to test the service logic.
Use the Spring #Profile functionality - beans can be associated to a certain group, and the group can be activated or deactivated via annotations.
Check this blog post and the documentation for more detailed instructions, this is an example of how to define production services and two groups of mock services:
#Configuration
#Profile("production")
public static class ProductionConfig {
#Bean
public InvoiceService realInvoiceService() {
...
}
...
}
#Configuration
#Profile("testServices")
public static class TestConfiguration {
#Bean
public InvoiceService mockedInvoiceService() {
...
}
...
}
#Configuration
#Profile("otherTestServices")
public static class OtherTestConfiguration {
#Bean
public InvoiceService otherMockedInvoiceService() {
...
}
...
}
And this is how to use them in the tests:
#ActiveProfiles("testServices")
public class MyTest extends SpringContextTestCase {
#Autowired
private MyService mockedService;
// ...
}
#ActiveProfiles("otherTestServices")
public class MyOtherTest extends SpringContextTestCase {
#Autowired
private MyService myOtherMockedService;
// ...
}
I have the exact same problem and I solve it by using Mockito.any() for the arguments
eg:
when(transactionalService.validateProduct(id)).thenReturn("")
=> when(transactionalService.validateProduct(Mockito.any())).thenReturn("")

Categories

Resources