Как смоделировать метод, который вызывается из объекта уровня класса - PullRequest
1 голос
/ 10 октября 2019

Я пишу модульный тест для класса A, я хочу смоделировать метод, но этот метод вызывается доброкачественно из объекта уровня класса. Как я буду это высмеивать.

Позвольте мне объяснить это на примере

Класс A, который находится на стадии тестирования.

public class ClassA {
    ClassB objectOfB = new ClassB();
    public int add(int a, int b) {
        int addition = objectOfB.performCalculation(a,b);
        return addition;
    }
}

Класс B, который имеет некоторую бизнес-логику.

  public class ClassB {
    public int performCalculation(int a, int b) {
        int c = a+b;
        System.out.println("I am not mocked, I am actual call");
        System.out.println("Returning " + c + " From ClassB");
        return c;
    }
  }

Test Written

import static org.junit.Assert.*;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest({ClassA.class, ClassB.class})
public class ClassATest {
    @InjectMocks
    ClassA objA = new ClassA();

    @Test
    public void testAddFromClassA() throws Exception {
        ClassB objB = Mockito.mock(ClassB.class);
        Mockito.when(objB.performCalculation(5, 10)).thenReturn(15);
        int result = objA.add(5, 10);
        assertEquals(result, 15);
    }

}

Результат теста:

Этот тест проходит успешно, но он не является насмешливым методом ClassB, вместо этого он выполняет фактический вызов.


Требование:

Во время написания теста я хочу смоделировать строку: objectOfB.performCalculation (a, b);из класса А, но, как вы можете видеть, объект класса B () создается на уровне класса.

Как я могу это высмеять?

Что я должен написать в своем тестовом классе.

enter image description here

Ответы [ 2 ]

1 голос
/ 10 октября 2019

Смоделируйте инициализацию класса так, чтобы макет использовался при выполнении теста

@RunWith(PowerMockRunner.class)
@PrepareForTest({ClassA.class}) //prepare the class creating the new instance of ClassB for test, not the ClassB itself.
public class ClassATest {
    @Test
    public void testAddFromClassA() throws Exception {
        int expected = 15;
        ClassB objB = Mockito.mock(ClassB.class);
        Mockito.when(objB.performCalculation(5, 10)).thenReturn(expected);

        //mocking initialization of ClassB class withing ClassA class
        PowerMockito.whenNew(ClassB.class).withNoArguments().thenReturn(objB);

        ClassA objA = new ClassA();

        //Act
        int actual = objA.add(5, 10);

        //Assert
        assertEquals(expected, actual);
    }
}

Ссылка Как смоделировать построение новых объектов

Теперь сПри этом в идеале целевой класс должен следовать явному принципу зависимости через внедрение конструктора

public class ClassA {
    final ClassB objectOfB;

    public ClassA(ClassB objectOfB) {
        this.objectOfB = objectOfB;
    }

    public int add(int a, int b) {
        int addition = objectOfB.performCalculation(a,b);
        return addition;
    }
}

. Позволяет классу явно указывать, от чего он зависит, для выполнения своей разработанной функции.

Это также позволяетдля инверсии управления и слабой связи, что делает класс более гибким в обслуживании и тестировании

@RunWith(PowerMockRunner.class)
public class ClassATest {
    @Test
    public void testAddFromClassA() throws Exception {
        int expected = 15;
        ClassB objB = Mockito.mock(ClassB.class);
        Mockito.when(objB.performCalculation(5, 10)).thenReturn(expected);

        ClassA objA = new ClassA(objB);

        //Act
        int actual = objA.add(5, 10);

        //Assert
        assertEquals(expected, actual);
    }
}

То, что PowerMockito допускает фиктивное строительство новых объектов, не означает, что мы должны.

Если соблюдаются правильные принципы проектирования, тогда такие хаки действительно не нужны.

0 голосов
/ 10 октября 2019

Я бы посоветовал прочитать немного больше о насмешках и о том, как это делается. Посмотрите в тестовом коде, который вы пытались запустить:

  ClassB objB = Mockito.mock(ClassB.class);
  Mockito.when(objB.performCalculation(5, 10)).thenReturn(15);
  int result = objA.add(5, 10);
  assertEquals(result, 15);

Что происходит .
Вы издеваетесь над ClassB, но не используете смоделированный экземпляр objB.
Когда вы издеваетесь над классом, вы возвращаете имитированный экземпляр , это не значит, что класс теперь подвергается насмешке в ходе вашего теста, это просто означает, что вы можете использовать Mockito для манипулирования этим конкретным инстаграмом.

Что должно происходить .
Если вы не можете использовать этот экземпляр для тестирования метода, зависит от него, как в вашем случае, это означает, что у вас очень сильныйзависимость между этими двумя классами, и если это проблема для ваших тестов, это обычно означает, что ваш дизайн неисправен. вы должны иметь возможность внедрить ClassA в ClassB, либо подставив ему аргумент в конструкторе

public ClassA(ClassB bInstance){
   this.bIntance = bInstance
}

, либо, если это необходимо для конкретного действия, в качестве аргумента функции.

public add(ClassB classBInstance, int a, int b){
    classBInstance. performCalculation(a.b)
}

Это позволит вам смоделировать экземпляр и запустить функцию ClassA с конкретной реализованной вами реализацией:

ClassB classBInstance1 = Mockito.mock(ClassB.class)
ClassB classBInstance2 = Mockito.mock(ClassB.class)
Mockito.when(classBInstance1.performCalculation(5, 10)).thenReturn(15);
Mockito.when(classBInstance2.performCalculation(5, 10)).thenReturn(42); 
new ClassA(classBInstance1).add(5,10) //returns 15
new ClassA(classBInstance2).add(5,10) //returns 42
new ClassA().add(classBInstance1,5,10) //15
new ClassA().add(classBInstance2,5,10) //42
...