When the client communicates with the server over HTTPS either browser or REST call, the server doesn’t care who the client is, as long as they have the correct credentials. This is what happens in Login forms(Asymmetric communication followed by Symmetric Communication).

Two-way SSL is used for places where you only want the server to accept connections from a restricted number of users. It helps to mitigate the risk of fraud in online transactions. Two-way SSL authentication (also known as “mutual authentication”, or “TLS/SSL with client certificates”) where two parties authenticate
each other through verifying provided digital certificates, so that both parties are assured of the other’s identity.

In One-way SSL, where the browser (the client) establishes an SSL connection to a secure web site and the server’s certificate is checked, creating SSL authentication in RESTful web services. The browser either relies on itself or the operating system providing a list of certs that have been designated as trusted Certificate Authorities (CA).

In context to java, Java Key Store is used to store the certs and keys.It must be password protected and entries in it must have an “alias” that is unique. If an alias is not specified, “mykey” is used by default.

KeyStores provide credentials, TrustStores verify credentials.Clients will use certificates stored in their TrustStores to verify identities of servers. They will present certificates stored in their KeyStores to servers requiring them.

The JDK ships with a tool called Keytool. It manages a JKS of cryptographic keys, X.509 certificate chains, and trusted certificates.

//Importing a truststore which could be used by client for server certificate verification
>> keytool -import -v -trustcacerts -keystore client_truststore.jks -storepass apassword -alias server -file foo.snaplogic.com.cert

//Importing a keystore which could be used by client when server asks of identity certificate
>> keytool -importkeystore -srckeystore client-certificate.p12 -srcstoretype pkcs12 -destkeystore client_keystore.jks -deststoretype jks -deststorepass apassword

// Viewing list of servcer certificates in Client Truststore  
>> keytool -list -v -keystore client_truststore.jks

We can now complete configuration of the REST SSL Account by uploading our client_truststore.jks and client_keystore.jks KeyStore files

One way SSL

  1. Client requests for some protected data from the server on HTTPS protocol. This initiates SSL/TLS handshake process.
  2. Server returns its public certificate to the client along with server hello message.
  3. Client validates/verifies the received certificate. Client verifies the certificate through certification authority (CA) for CA signed certificates.
  4. SSL/TLS client sends the random byte string that enables both the client and the server to compute the secret key to be used for encrypting subsequent message data. The random byte string itself is encrypted with the server’s public key.
  5. After agreeing on this secret key, client and server communicate further for actual data transfer by encrypting/decrypting data using this key.

Two way SSL

  1. Client requests a protected resource over HTTPS protocol and the SSL/TSL handshake process begins.
  2. Server returns its public certificate to the client along with server hello.
  3. Client validates/verifies the received certificate. Client verifies the certificate through certification authority (CA) for CA signed certificates.
  4. If Server certificate was validated successfully, client will provide its public certificate to the server.
  5. Server validates/verifies the received certificate. Server verifies the certificate through certification authority (CA) for CA signed certificates.
  6. After completion of handshake process, client and server communicate and transfer data with each other encrypted with the secret keys shared between the two during handshake.

} Below is a static util class that can be used to check the list of beans available in the application context

ExampleConfigurationTest.java

package com.mugil.org.utils;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;

@Service
public class SpringUtils implements ApplicationContextAware {
    private static ApplicationContext ctx;

    @Override
    public void setApplicationContext(ApplicationContext appContext)
            throws BeansException {
        ctx = appContext;
    }

    public static ApplicationContext getApplicationContext() {
        return ctx;
    }
}

SomeRandomClass.java

class SomeRandomClass
{
    @Autowired
    SpringUtils springUtils;

    
    public void getBeans() 
    {  
        String[] beans = springUtils.getApplicationContext().getBeanDefinitionNames();
        Arrays.sort(beans);
        for (String bean : beans)
        {
            System.out.println(bean + " of Type :: " + springUtils.getApplicationContext().getBean(bean).getClass());
        }
}
}

Output

DBConfig of Type :: class com.mugil.org.configs.DBConfig$$EnhancerBySpringCGLIB$$d32ee4ac
dbProperties of Type :: class com.mugil.org.configs.DBProperties
spring.datasource-com.mugil.org.configs.DBProperties of Type :: class com.mugil.org.configs.DBProperties
springUtils of Type :: class com.mugil.org.utils.SpringUtils

In case if you don’t want to create a separate class and wanted to access beans in the context you can implement the ApplicationContextAware interface and specify the for loop in overridden setApplicationContext method.

How to load a single class into ApplicationContext

.
.
ApplicationContextRunner context = new ApplicationContextRunner().withUserConfiguration(ExampleConfiguration.class);
.
.

What @Springbootapplication does?

@SpringBootApplication = @Configuration + @ComponentScan + @EnableAutoConfiguration

  1. @Configuration to enable Java-based configuration
  2. @ComponentScan – This annotation enables component-scanning so that the web controller classes and other components you create will be automatically discovered and registered as beans in Spring’s Application Context. All the@Controller classes you write are discovered by this annotation..
  3. @EnableAutoConfiguration annotation tells Spring Boot to “guess” how you will want to configure Spring, based on the jar dependencies that you have added. For example, If HSQLDB is on your classpath, and you have not manually configured any database connection beans, then Spring will auto-configure an in-memory database.For example, if you add spring-boot-starter-web dependency in your classpath, it automatically configures Tomcat and Spring MVC.
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    

What @springboottest annotation should do?

  1. Should check whether Java-based configuration are loaded
  2. Should do component scan and create beans.
  3. Should assign appropriate beans by resolving

@SpringBootTest annotation is used to bootstrap the entire container. The annotation works by creating the ApplicationContext that will be utilized in our tests. @SpringBootTest annotation works by creating the ApplicationContext used in our tests through SpringApplication.It starts the embedded server, creates a web environment and then enables @Test methods to do integration testing.

  1. Below we have two class DBConfig.java and DBProperties.java.
  2. DBConfig loads DBProperties bean. DBProperties bean gets its value from application.yml
  3. In the below integration test we are going to check whether all the classes are properly getting loaded into application context.
  4. Classes loaded using @SpringBootTest would be available only in the context of the class in which it is used. Just because you add the list of classes in TestConfigApplicationTests.java it wont be available in other test classes(DatasourceConfigTest.java, ExampleConfigurationTest.java)

DBProperties.java

package com.mugil.org.configs;

import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.ArrayList;
import java.util.List;

@ConfigurationProperties(prefix = "spring.datasource")
public class DBProperties {   
    private String userName;
    private String password;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

DBConfig.java

@Configuration
@EnableConfigurationProperties(DBProperties.class) // Tells from which class is needed for creation of Beans
public class DBConfig {

    @Bean
    public DBProperties dbProperties(){
        DBProperties objDBProperties = new DBProperties();
        return objDBProperties;
    }
}

application.yml

spring:
  profiles:
    active: dev
---
spring:
  config:
    activate:
      on-profile: dev
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    password: root
    url: jdbc:mysql://localhost:3306/db
    username: root
  servers:
    - www.abc.test.com
    - www.xyz.test.com

---
spring:
  config:
    activate:
      on-profile: production
  datasource:
    driver-class-name: org.h2.Driver
    password: sa
    url: jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
    username: sa
  servers:
    - www.abc.com
    - www.xyz.com

TestConfigApplication.java

package com.mugil.org;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class TestConfigApplication {
	public static void main(String[] args) {
		SpringApplication.run(TestConfigApplication.class, args);
	}
}

@SpringBootTest loads the DBConfig.java and creates DBProperties bean by reading values from application.yml file. The SpringBootTest ensures that all loading of beans defined in configuration file and all the values needed from application.yml and application.properties are available without any issue.

In case if you want to have any class to be autowired in @SpringBootTest class, the same should be defined in @SpringBootTest(classes = DBConfig.class) annotation.

The below @SpringBootTest takes classes which should be available by the time the application is bootstrapped. So we are expecting the properties to be read from application.yml and the bean gets created. Incase there is a problem the test case would fail below.

TestConfigApplication.java

package com.mugil.org;

import com.mugil.org.configs.DBConfig;
import com.mugil.org.configs.DBProperties;
import com.mugil.org.configs.ExampleConfiguration;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest(classes = {DBConfig.class})
class TestConfigApplicationTests {
	@Autowired
	DBProperties dbProperties;

	@Test
	void contextLoads() {
		System.out.println(dbProperties.getUserName());
	}
}

Below is a test which check if the value are loaded from yaml and is not null.
DatasourceConfigTest.java

package com.mugil.org.configs;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.assertNotNull;

@SpringBootTest(classes = DBConfig.class)
class DatasourceConfigTest{

    //This is same as using context.run method where the beans would be initialized
    //context.run method in our case is invoked because of @SpringBootTest defined class
    @Autowired
    DBProperties dbProperties;

    @Test
    public void dbConfigTest() throws Exception {
        final String name = dbProperties.getUserName();

        //We can check whether the property is getting loaded and its not null only not null
        assertNotNull(name);
    }
}
  1. The @SpringBootTest annotation tells Spring Boot to look for a main configuration class (one with @SpringBootApplication , for instance) and use that to start a Spring application context.
  2. Spring Boot Integration Testing is running an application in ApplicationContext and run tests.spring-boot-starter-test which will internally use spring-test and other dependent libraries
  3. @SpringBootTest annotation works by creating the ApplicationContext used in our tests through SpringApplication.It starts the embedded server, creates a web environment and then enables @Test methods to do integration testing.

ExampleConfiguration.java

package com.mugil.org.configs;

import com.mugil.org.services.ExampleService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ExampleConfiguration {

    @Bean
    ExampleService exampleService() {
        ExampleService exampleService = new ExampleService();
        return exampleService;
    }
}

Below is the testing of the same functionality in a different way. Instead of using @SpringBootTest we are loading the class with help of ApplicationContextRunner and checking whether the loaded class – ExampleConfiguration.java dependencies – ExampleService.java in turn gets loaded

How to check if bean from particular configuration is loaded
ExampleConfigurationTest.java

package com.mugil.org.configs;

import com.mugil.org.services.ExampleService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;

import static org.assertj.core.api.Assertions.assertThat;

class ExampleConfigurationTest {

    /*
     * I setup a context runner with the class ExampleConfiguration
     * in it. For that, I use ApplicationContextRunner#withUserConfiguration()
     * methods to populate the context.
     */
    ApplicationContextRunner context = new ApplicationContextRunner()
            .withUserConfiguration(ExampleConfiguration.class);

    @Test
    public void should_check_presence_of_example_service() {
        /*
         * We start the context, and we will be able to trigger
         * assertions in a lambda receiving a
         * AssertableApplicationContext
         */

        context.run(it -> {
            /*
             * I can use assertThat to assert on the context
             * and check if the @Bean configured is present
             * (and unique)
             */
            assertThat(it).hasSingleBean(ExampleService.class);
        });
    }
}

new ApplicationContextRunner() creates a new context and creates the bean by loading the configuration

For further reference check the code in the Link

Below is a static util class which can be used to check the list of beans available in the application context

ExampleConfigurationTest.java

package com.mugil.org.utils;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;

@Service
public class SpringUtils implements ApplicationContextAware {
    private static ApplicationContext ctx;

    @Override
    public void setApplicationContext(ApplicationContext appContext)
            throws BeansException {
        ctx = appContext;
    }

    public static ApplicationContext getApplicationContext() {
        return ctx;
    }
}

TestTraits.java

public interface TestTraits {
    String EMPLOYEE_ID = "1";
    Long EMPLOYEE_ID_LONG = 1L;

    default EmployeeRpy getEmployee(){
        EmployeeRpy objEmpRpy = new EmployeeRpy();
        objEmpRpy.setEmpName("Mugil");
        objEmpRpy.setEmpAge("34");
        return objEmpRpy;

     default EmployeeDetailsResource createMockedEmployeeDetails() 
          throws IOException(){
       EmployeeDetailsResource objEmployeeDetailsResource = 
              new Gson.fromJson(getReader("json-data/EmployeeDetails.json"), EmployeeDetails.class);    
       return objEmployeeDetailsResource; 
     } 

     default Reader getReader(String filePath) throws IOException{
       return new FileReader(new ClassPathResource(filePath).getFile());  
     }
}

EmployeeDetails.json

  {
     "empID" : "101",
     "empName" : "Mugilvannan",
     "empAge" : "30"
  } 

EmployeeMgmtTest.java

 @ExtendWith({MockitoExtension.class})
 class EmployeeMgmtTest implements TestTraits{
    .
    . 
    Code for Testing Goes Here
    .
    .
 }

How to Pass Interface or Abstract Class as Matcher?
Person.java

public interface Person {
}

StudentUtils.java

 public String methodWithInterfaceArg(Person person)
    {
        //This method takes Interface Implementation as Parameter
        Somemethod(person);
        return "Test Success";
    }

    public void Somemethod(Person p){
        System.out.println("Hello There");
    }

StudentUtilsTest.java

@Test
    @DisplayName("Test for method which takes interface as argument")
    void methodWithInterfaceArg() 
    {
        Person objPerson = Mockito.mock(Person.class);

        // Using spy we are creating a new mock and call the method to be tested
        StudentUtils studentUtils= Mockito.spy(StudentUtils.class);

        doNothing().when(studentUtils).Somemethod(objPerson);

        // While testing we should call the method to be tested using new spyObject rather than systemUnderTest
        // This is because only in the Spy object studentUtils we have created a mock definition for Somemethod
        // Hence when methodWithInterfaceArg is called using systemUnderTest the mock  definition created using spy would be lost
        assertEquals("Test Success", studentUtils.methodWithInterfaceArg(objPerson));
    }

How to doNothing when internal method which returns void is gets called?
StudentUtils.java

    public String methodWithInterfaceArg(Person person)
    {
        //This method has void return type
        Somemethod(person);
        return "Test Success";
    }

    public void Somemethod(Person p){
        System.out.println("Hello There");
    }

StudentUtilsTest.java

    @Test
    @DisplayName("Test for internal method which is of void return type")
    void methodWithInterfaceArg() {
        Person objPerson = Mockito.mock(Person.class);

        // Using spy we are creating a new mock and call the method to be tested
        StudentUtils studentUtils= Mockito.spy(StudentUtils.class);

        //With Object Created using spy we can mock the actual method to do nothing
        doNothing().when(studentUtils).Somemethod(objPerson);

        assertEquals("Test Success", studentUtils.methodWithInterfaceArg(objPerson));
    }

How to doNothing when method which returns value is called?
StudentUtils.java

public class StudentUtils {
    private List arrStudents = new ArrayList();
    private String Name = "Mugil";

    public String dummymethod(Student objStudent)
    {
        System.out.println("Dummy Method Called");
        return "Dummy Method";
    }

    public String methodCallingAnotherMethodReturningVal()
    {
        String something = dummymethod(new Student("102" ,"abc"));
        return "Dummy Method returned "+ something;
    }
}

StudentUtilsTest.java

 @Test
    @DisplayName("Test for Method calling another method which returns value")
    void methodCallingAnotherMethodReturningVal() {
        StudentUtils studentUtils= Mockito.spy(StudentUtils.class);

        Mockito.when(studentUtils.dummymethod(any())).thenReturn("Test Student");
        Assert.assertEquals("Dummy Method returned Test Student", studentUtils.methodCallingAnotherMethodReturningVal());
    }

How to mock methods of private and public instance class variables?

  1. The below scenario we are going to mock instance of method which belongs to other class initialized over by public and private class variables
  2. We Create a new mock and initialize using reflectionUtils for the objDBConnectionFactory which is private whereas we use a spy to directly change the method definition of objDBConnectionFactory2

DBConnectionFactory.java

package com.mugil.org;

public class DBConnectionFactory {
    public DBConnectionFactory() {
    }

    public <T> T  readDetails(byte[] src, Class<People> valueType){
        return (T)new Student();
    }

    public <T> T  readDetails2(String src){
        return (T)new Student();
    }
}

MockPrivateFileds.java

package com.mugil.org;

import org.springframework.web.client.HttpStatusCodeException;

public class MockPrivateFileds {
    private DBConnectionFactory objDBConnectionFactory = new DBConnectionFactory();

    public DBConnectionFactory objDBConnectionFactory2 = new DBConnectionFactory();

    private Student getDetails(HttpStatusCodeException httpStatusCodeException) {
        return objDBConnectionFactory.readDetails(httpStatusCodeException.getResponseBodyAsByteArray(), People.class);
    }

    public Student getDetails2(HttpStatusCodeException httpStatusCodeException) {
        return objDBConnectionFactory2.readDetails2("String");
    }
}

MockPrivateFiledsTest.java

package com.mugil.org;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.client.HttpStatusCodeException;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.mockito.ArgumentMatchers.*;

@ExtendWith({MockitoExtension.class})
class MockPrivateFiledsTest {
    @InjectMocks
    MockPrivateFileds systemUnderTest;

    @Test
    void getDetailsTest() {
        HttpStatusCodeException httpStatusCodeException = Mockito.mock(HttpStatusCodeException.class);

        /*The Below two lines of code are needed because the objDBConnectionFactory created doesnot contain any definition of methods
        * So the definition could be set by creating new mock object as below. If there is Setter we can use setter to set the newly created mock object
        * Since there is no setter and its private we are going to use reflectionUtils */
        DBConnectionFactory dbConnectionFactory = Mockito.mock(DBConnectionFactory.class);
        ReflectionTestUtils.setField(systemUnderTest, "objDBConnectionFactory", dbConnectionFactory);
        Mockito.when(dbConnectionFactory.readDetails(nullable(byte[].class), eq(People.class))).thenReturn(new Student());

        assertThat(ReflectionTestUtils.invokeMethod(systemUnderTest, "getDetails", httpStatusCodeException), instanceOf(Student.class));
    }

    @Test
    void getDetailsTest2() {
        HttpStatusCodeException httpStatusCodeException = Mockito.mock(HttpStatusCodeException.class);
        systemUnderTest = Mockito.spy(MockPrivateFileds.class);

        DBConnectionFactory dbConnectionFactory2 = Mockito.spy(DBConnectionFactory.class);
        ReflectionTestUtils.setField(systemUnderTest, "objDBConnectionFactory2", dbConnectionFactory2);

        /*Below is a mocking of a public method through its instance by using spy. The below case is similar to above
        * scenario with the only difference we can use spy to gain access to readDetails2 */
        Mockito.when(systemUnderTest.objDBConnectionFactory2.readDetails2(anyString())).thenReturn(new Student());

        assertThat(systemUnderTest.getDetails2(httpStatusCodeException), instanceOf(Student.class));
    }
}

Below is a class StudentUtils which contains only two Method – doNothingWhenCalled and dummymethod. We are going to unit test doNothingWhenCalled method.

The conclusion would be to use @Spy when we mock methods of same class tested to doNothing and use @Mock to mock method to doNothing call to other classes.

StudentUtils.java

package com.mugil.org;

import java.util.ArrayList;
import java.util.List;

public class StudentUtils {
    private List arrStudents = new ArrayList();
    private String Name = "Mugil";

    public StudentUtils(){
        arrStudents.add(new Student("101", "Mugil"));
    }

    public Student doNothingWhenCalled()
    {
        dummymethod(new Student("102" ,"abc"));
        return new Student("103", "StudentName");
    }

    public String dummymethod(Student objStudent)
    {
        System.out.println("Dummy Method Called");
        return "Dummy Method";
    }

@InjectMocks
StudentUtilsTest.java

@ExtendWith({MockitoExtension.class})
class StudentUtilsTest {
    @InjectMocks
    StudentUtils systemUnderTest;

    @Test
    void doNothingCalled() {
        Assertions.assertThat(systemUnderTest.doNothingWhenCalled()).isInstanceOf(Student.class);
    }
}

Output

Dummy Method Called

@Mocks
StudentUtilsTest.java

@ExtendWith({MockitoExtension.class})
class StudentUtilsTest {
    @InjectMocks
    StudentUtils systemUnderTest;

    @Test
    void doNothingCalled() {
        systemUnderTest = Mockito.mock(StudentUtils.class);
        Mockito.when(systemUnderTest.dummymethod(any())).thenReturn("Dummy Method");

        Assertions.assertThat(systemUnderTest.doNothingWhenCalled()).isInstanceOf(Student.class);
    }
}

You can see below we are trying to call method to be tested under empty mock instance which doesnot have default method definition. There is no point in mocking method definition since systemUnderTest.doNothingWhenCalled() would try to invoke test method which has no idea about.
Output

java.lang.AssertionError: 
Expecting actual not to be null

@Spy
StudentUtilsTest.java

@ExtendWith({MockitoExtension.class})
class StudentUtilsTest {
    @InjectMocks
    StudentUtils systemUnderTest;

    @Test
    void doNothingCalled() {
        systemUnderTest = Mockito.spy(StudentUtils.class);
        Assertions.assertThat(systemUnderTest.doNothingWhenCalled()).isInstanceOf(Student.class);
    }
}

Output

Dummy Method Called

Below is the Debug of Object created using @Mock, @InjectMock and @Spy.As you can see the one with Mock contains only MockInterceptor which does not have definition of methods on its own where as @InjectMock created instance of TestClass. @Spy contains both MockInterceptor and Instance enabling partial mock.

While Using InjectMock you can see the MockitoInterceptor in scope

While Using InjectMock you can see the class variables getting initialized

While Using Spy you can see both class variables getting initialized and MockitoInterceptor in scope

Below we have class – ClassA which contains the actual method – MethodA to be tested. MethodA has call to two dependent
ClassA.java

package com.mugil.org;

public class ClassA {
    public ClassADep1 dependency1;
    public ClassADep2 dependency2;

    public ClassA(ClassADep1 dependency1, ClassADep2 dependency2) {
        this.dependency1 = dependency1;
        this.dependency2 = dependency2;
    }

    public boolean MethodA(){
        System.out.println("I am going to call Dep1Method");
        dependency1.Dep1Method();

        System.out.println("I am going to call Dep2Method");
        dependency2.Dep2Method();

        return true;
    }
}

ClassADep1.java

package com.mugil.org;

public class ClassADep1 {
    public void Dep1Method(){
        System.out.println("This is ClassADep1 Method getting called");
    }
}

ClassADep2.java

package com.mugil.org;

public class ClassADep2 {
    public void Dep2Method(){
        System.out.println("This is ClassADep2 Method getting called");
    }
}

Use @Mock when you want to just test the functionality externally without actually calling that method.If you want to be safe and avoid calling external services and just want to test the logic inside of the unit, then use mock.

Using @Mock annotation for Dependency
ClassATest.java

package com.mugil.org;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.junit.jupiter.api.Assertions.*;

@ExtendWith({MockitoExtension.class})
class ClassATest {
    @InjectMocks
    ClassA classA;

    @Mock
    ClassADep1 dependency1;

    @Mock
    ClassADep2 dependency2;

    @Test
    void methodA() {
        assertEquals(true, classA.MethodA());
    }
}

Output

I am going to call Dep1Method
I am going to call Dep2Method

Use @Spy when you want to test the functionality externally + internally with the very method being called. If you want to call an external service and perform calling of real dependencies, or simply say, you want to run the program as it is and just stub specific methods, then use spy.

Using @Spy annotation for Dependency
ClassATest.java

package com.mugil.org;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.junit.jupiter.api.Assertions.assertEquals;

@ExtendWith({MockitoExtension.class})
class ClassATest {
    @InjectMocks
    ClassA classA;

    @Spy
    ClassADep1 dependency1;

    @Spy
    ClassADep2 dependency2;

    @Test
    void methodA() {
        assertEquals(true, classA.MethodA());
    }
}

Output

I am going to call Dep1Method
This is ClassADep1 Method getting called
I am going to call Dep2Method
This is ClassADep2 Method getting called

Below we have a Student class with Private Class Variable(id and name), Service and Private Methods.

Student.java

public class Student {
    private Integer id;
    private String name;

    @Autowired
    private StudentService studentService;

    private String getStudentDetails(){
      return "id: " + getId() + "; name: " + getName();
    }  
}

Mocking a Private Field
we cannot access the private field id to assign a value for testing, because there isn’t a public setter method for it.
We can then use ReflectionTestUtils.setField method to assign a value to the private member id

StudentTest.java

@Test
public void setValueWithNoSetter() {
    Student student = new Student();
    ReflectionTestUtils.setField(student, "id", 101); 
    assertTrue(student.getId().equals(101));
}

Mocking a Private Method
In a similar way we can invoke private method – getStudentDetails() as below in student class
StudentTest.java

@Test
public void whenNonPublicMethod_thenReflectionTestUtilsInvokeMethod() {
    Student student= new Student ();
    ReflectionTestUtils.setField(student, "id", 101);
    ReflectionTestUtils.setField(student, "name", "Mugil");
 
    assertTrue(ReflectionTestUtils.invokeMethod(student, "employeeToString").equals
                 ("id: 101; name: Mugil"));
}

Mocking a Private Dependencies
StudentTest.java

@Test
public void whenNonPublicMethod_thenReflectionTestUtilsInvokeMethod() 
{
   Student student = new Student();
   StudentService studService = mock(StudentService.class);
   when(studService.getStudentStatus(student.getId())).thenReturn("Active");

   ReflectionTestUtils.setField(student, "studentService", studService);

.
.
}

To pass data between components at the same level under parent, I.E. passing data between two children within the same parent we use @Input and @Output and pass the value through the tags.

The Other option is using EventEmitter as variable in Service and emitting value from component1 and getting value in component2.

  1. In the below code we are passing data from list-person.component.ts to add-person.component.ts
  2. We are calling instance of personcrudService to call a function which emits event which is received by ListPersonComponent OnInit() method
  3. In add-person.component.ts we subscribe to the event by using instance of personcrudService

personcrud.service.ts

import {EventEmitter, Injectable, OnInit} from '@angular/core';
import {LoggingServiceService} from './logging-service.service';

@Injectable({
  providedIn: 'root'
})
export class PersoncrudService{  
  statusUpdated: EventEmitter<string> = new EventEmitter<string>();

  constructor(public loggingServiceService: LoggingServiceService) {}

  updatePerson(id: number, status: boolean){
    .
    . 
    this.statusUpdated.emit(this.arrPersons[id].name);
    .
    .
  }
}

list-person.component.ts

@Component({
  selector: 'app-list-person',
  templateUrl: './list-person.component.html',
  styleUrls: ['./list-person.component.css'],
  providers: [LoggingServiceService]
})
export class ListPersonComponent implements OnInit {
  constructor(private personcrudService: PersoncrudService,
              private loggingServiceService: LoggingServiceService) { }

  ngOnInit(): void {
  }

  updateStatus(i, status){
    this.personcrudService.updatePerson(i, status);
    this.loggingServiceService.logStatusOfPerson(status);
  }
}

add-person.component.ts

@Component({
  selector: 'app-add-person',
  templateUrl: './add-person.component.html',
  styleUrls: ['./add-person.component.css']
})
export class AddPersonComponent implements OnInit {
  constructor(private personcrudService: PersoncrudService) {
  }

  ngOnInit(): void {
    this.personcrudService.statusUpdated.subscribe(
      (name: string) => alert(name + ' has been updates')
    );
  }
}

Custom Attribute Directive

  1. Below is a Custom Attribute Directive which applies Background color to HTML Elements
  2. However we could not apply anylogic based on which directive would apply like checking row is even or odd
  3. Entry should be made in app.modules.ts for new directives created so it would be available

EvenrowHighlighterDirective.ts

import {AfterViewInit, Directive, ElementRef, OnInit} from '@angular/core';

@Directive({
  selector: '[appevenrow]'
})
export class EvenrowHighlighterDirective implements  OnInit, AfterViewInit{
  constructor(public elementRef: ElementRef) {
  }

  ngOnInit(): void {
    this.elementRef.nativeElement.style.backgroundColor = 'gray';
  }

  ngAfterViewInit(){
    //Add the above code here if ng-content and @child-content is used
  }
}

Custom Structural Directive

  1. In below code we replace the functionality of ngIf using custom structural directive which checks for condition based on value set in isPresent
  2. Similar to ElementRef, TemplateRef gives access to template
  3. ViewContainer is a reference to the place where the Custom Structural Directive is placed
  4. One of the key thing while using structural directive is selector, @Input should have the same name. This reason behind this is due to the fact that *ngIf would be convered to ng-template before rendering.ng-template works on refernce basis within html. So when reference and directive name are different it would end up in error

test.component.ts

import {Directive, Input, TemplateRef,  ViewContainerRef} from '@angular/core';

@Directive({
  selector: '[appDounless]'
})
export class DounlessDirective {

  constructor(private templateRef: TemplateRef<any>, private vcRef: ViewContainerRef) { }

  @Input() set appDounless(condition: boolean){
    if(condition) {
      this.vcRef.createEmbeddedView(this.templateRef);
    }else{
      this.vcRef.clear();
    }
  }
}

test.component.ts

@Component({
  selector: 'app-test',
  templateUrl: './test.component.html',
  styleUrls: ['./test.component.css']
})

export class testComponent implements OnInit {

  public isPresent = true;

  ngOnInit() {
  }
}

test.component.html

<p appevenrow> This is Custom Attribute Directive</p>
<p *appDounless="isPresent">This is Custom Structural Directive</p>

Output