使用PowerMock進行Mock測試

jopen 8年前發布 | 52K 次閱讀 Python開發

安裝

下載地址:https://github.com/jayway/powermock/wiki/Downloads。下載" Mockito and JUnit including dependencies"版本。當前版本為”powermock-mockito-junit-1.6.3.zip"。 

IntelliJ IDEA的設置如下:

右擊工程,選擇“Open Module Settings”

按下“ALT + Insert”,選擇“Jars or directories...", 插入jar包:

點擊OK。

在”Module Settings”對話框中點擊“Sources”標簽,右擊右邊底部面板,選擇“New Folder...", 命名為test。

在”Module Settings”對話框中選擇test,標識為Test Sources,關閉”Module Settings”對話框

Eclipse中只需要上述jar包放在工程下的lib目錄即可。

Maven在pom.xml添加如下內容:

<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>1.6.3</version>
    <scope>test</scope>
</dependency>

快速入門

下面創建EmployeeController類用于給Employee類執行Create, Read, Update, and Delete (CRUD)。實際工作由EmployeeService完成。getProjectedEmployeeCount方法預計公司員工每年增加20%,并返回近似取整。

 

public class EmployeeController {
    
    private EmployeeService employeeService;
    
    public EmployeeController(EmployeeService employeeService) {
    
        this.employeeService = employeeService;
    }
    
    public int getProjectedEmployeeCount() {
    
        final int actualEmployeeCount = employeeService.getEmployeeCount();
        return (int) Math.ceil(actualEmployeeCount * 1.2);
    }
    
    public void saveEmployee(Employee employee) {
    
        employeeService.saveEmployee(employee);
    }    
}
public class EmployeeService {
    
    public int getEmployeeCount() {
        throw new UnsupportedOperationException();
    }
    
    public void saveEmployee(Employee employee) {
        throw new UnsupportedOperationException();
    }    
}

 

由于getEmployeeCount等方法沒有真正實現,我們需要mock:

public class Employee {

}</pre>

import static org.junit.Assert.*;

import org.junit.Test; import org.mockito.Mockito; import org.powermock.api.mockito.PowerMockito;

public class EmployeeControllerTest {

    @Test     public void shouldReturnProjectedCountOfEmployeesFromTheService() {                  EmployeeService mock = PowerMockito.mock(EmployeeService.class);         PowerMockito.when(mock.getEmployeeCount()).thenReturn(8);         EmployeeController employeeController = new EmployeeController(mock);         assertEquals(10, employeeController.getProjectedEmployeeCount());     }          @Test     public void     shouldInvokeSaveEmployeeOnTheServiceWhileSavingTheEmployee() {                  EmployeeService mock = PowerMockito.mock(EmployeeService.class);             EmployeeController employeeController = new EmployeeController(mock);         Employee employee = new Employee();         employeeController.saveEmployee(employee);         Mockito.verify(mock).saveEmployee(employee);     }         }</pre>

 

 注意如果上述代碼出現莫名其妙的錯誤,建議先確認所有文件已經保存,再不行重啟Eclipse。

上面的saveEmployee(Employee)沒有返回值,我們只需要用verify確認有調用即可。如果注釋掉employeeController.saveEmployee(employee);就會有如下報錯:

Wanted but not invoked:
employeeService.saveEmployee(
    Employee@51081592
);
-> at EmployeeControllerTest.shouldInvokeSaveEmployeeOnTheServiceWhileSavingTheEmployee(EmployeeControllerTest.java:27)
Actually, there were zero interactions with this mock.
    at EmployeeControllerTest.shouldInvokeSaveEmployeeOnTheServiceWhileSavingTheEmployee(EmployeeControllerTest.java:27)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

另外有個非常用的MockSettings功能,用于設置mock名、實現額外接口(參見https://groups.google.com/forum/?fromgroups=#!topic/mockito/YM5EF0x90_4)、開啟詳細日志、注冊listener用于mock時通知消息調用。比如:

EmployeeService mock = PowerMockito.mock(EmployeeService.class, Mockito.withSettings().name("EmployeeServiceMock").verboseLogging());

 注意:Eclipse如果看不到lib,請選中工程目錄,按F5刷新。lib中的每個jar,需要右鍵點擊,選擇"Build Path"->"Add to Build Path", 添加完畢的效果圖如下:

模擬靜態方法

修改類Employee:

public class Employee {
    
    public static int count() {
        throw new UnsupportedOperationException();
    }
}

修改EmployeeService類的方法:

    public int getEmployeeCount() {
        return Employee.count();
    }

新建EmployeeServiceTest類:

import static org.junit.Assert.*;

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

@RunWith(PowerMockRunner.class) @PrepareForTest(Employee.class) public class EmployeeServiceTest {

    @Test     public void shouldReturnTheCountOfEmployeesUsingTheDomainClass() {                      PowerMockito.mockStatic(Employee.class);             PowerMockito.when(Employee.count()).thenReturn(900);                      EmployeeService employeeService = new EmployeeService();             assertEquals(900, employeeService.getEmployeeCount());              } }</pre>

@RunWith(PowerMockRunner.class)語句告訴JUnit用PowerMockRunner執行測試。
@PrepareForTest(Employee.class)語句告訴PowerMock準備Employee類進行測試。適用于模擬final類或有final, private, static, native
方法的類。


注意這里使用的是mockStatic而不是上面的mock。

下面我們模擬下返回void的靜態方法。在Employee添加加薪方法:

    public static void giveIncrementOf(int percentage) {
    throw new UnsupportedOperationException();
    }

EmployeeService添加相應方法:

   

public boolean giveIncrementToAllEmployeesOf(int percentage) {
        try{
                Employee.giveIncrementOf(percentage);
                return true;
        } catch(Exception e) {
                return false;
        }
    }

修改EmployeeServiceTest類

import static org.junit.Assert.*;
 
import org.junit.Test;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
 
public class EmployeeControllerTest {
 
    @Test
    public void shouldReturnProjectedCountOfEmployeesFromTheService() {
         
        EmployeeService mock = PowerMockito.mock(EmployeeService.class);
        PowerMockito.when(mock.getEmployeeCount()).thenReturn(8);
        EmployeeController employeeController = new EmployeeController(mock);
        assertEquals(10, employeeController.getProjectedEmployeeCount());
    }
     
    @Test
    public void
    shouldInvokeSaveEmployeeOnTheServiceWhileSavingTheEmployee() {
         
        EmployeeService mock = PowerMockito.mock(EmployeeService.class);    
        EmployeeController employeeController = new EmployeeController(mock);
        Employee employee = new Employee();
        employeeController.saveEmployee(employee);
        Mockito.verify(mock).saveEmployee(employee);
    }        
}

PowerMockito.doNothing方法告訴PowerMock下一個方法調用時什么也不做。

PowerMockito.doThrow方法告訴PowerMock下一個方法調用時產生異常。

PowerMock使用自定義類加載器和字節碼操作來模擬靜態方法。對于實例中沒有mock的方法,也有默認返回值,比如返回int類型的方法,默認返回0。


PowerMockito.doNothing和PowerMockito.doThrow的語法可用于實例方法

先在Employee類添加方法save:

    public void save() {
        throw new UnsupportedOperationException();
    }

創建測試EmployeeTest 類:

import static org.junit.Assert.*;

import org.junit.Test; import org.powermock.api.mockito.PowerMockito;

public class EmployeeTest {          @Test()     public void shouldNotDoAnythingIfEmployeeWasSaved() {              Employee employee = PowerMockito.mock(Employee.class);         PowerMockito.doNothing().when(employee).save();         try {             employee.save();         } catch(Exception e) {             fail("Should not have thrown an exception");         }     }          @Test(expected = IllegalStateException.class)     public void shouldThrowAnExceptionIfEmployeeWasNotSaved() {              Employee employee = PowerMockito.mock(Employee.class);         PowerMockito.doThrow(new IllegalStateException()).when(employee).save();         employee.save();     } }</pre>

注意這里doThrow和doNothing方法不會對下一行產生影響。

驗證方法調用

驗證斷言方法是否調用。

修改EmployeeService類的saveEmployee方法。

public void saveEmployee(Employee employee) {
        if(employee.isNew()) {
            employee.create();
            return;
        }
        employee.update();
    }

修改Employee類,新增如下方法:

    public boolean isNew() {
        throw new UnsupportedOperationException();
    }
    
    public void update() {
        throw new UnsupportedOperationException();
    }
    
    public void create() {
        throw new UnsupportedOperationException();
    }

在EmployeeServiceTest類中新增shouldCreateNewEmployeeIfEmployeeIsNew方法, 并新增導入import org.mockito.Mockito;:

    @Test
    public void shouldCreateNewEmployeeIfEmployeeIsNew() {
    
        Employee mock = PowerMockito.mock(Employee.class);
        PowerMockito.when(mock.isNew()).thenReturn(true);
        EmployeeService employeeService = new EmployeeService();
        employeeService.saveEmployee(mock);
        Mockito.verify(mock).create();
        Mockito.verify(mock, Mockito.never()).update();
    }

 Mockito.verify(mock).create()驗證調用了create方法。 Mockito.verify(mock, Mockito.never()).update();驗證沒有調用update方法。

 

下面驗證靜態方法,在EmployeeServiceTest類添加shouldInvoke_giveIncrementOfMethodOnEmployeeWhileGivingIncrement方法:

   

@Test
    public void shouldInvoke_giveIncrementOfMethodOnEmployeeWhileGivingIncrement() {
    
        PowerMockito.mockStatic(Employee.class);
        PowerMockito.doNothing().when(Employee.class);
        Employee.giveIncrementOf(9);
        EmployeeService employeeService = new EmployeeService();
        employeeService.giveIncrementToAllEmployeesOf(9);
        PowerMockito.verifyStatic();
        Employee.giveIncrementOf(9);
    }

同樣,靜態驗證也要分兩步走。

其他驗證模式可以驗證調用次數:

  • Mockito.times(int n) : This verification mode asserts that the mocked method was invoked exactly 'n' times

  • Mockito.atLeastOnce() : This verification mode asserts that the mocked method was invoked at least once

  • Mockito.atLeast(int n) : This verification mode asserts that the mocked method was invoked at least 'n' times

  • Mockito.atMost(int n) : This verification mode asserts that the mocked method was invoked at most 'n' times

使用Mockito.inOrder還可以驗證調用的順序,注意要導入import org.mockito.InOrder;

   

@Test
    public void shouldInvokeIsNewBeforeInvokingCreate() {
        
        Employee mock = PowerMockito.mock(Employee.class);
        PowerMockito.when(mock.isNew()).thenReturn(true);
        EmployeeService employeeService = new EmployeeService();
        employeeService.saveEmployee(mock);
        InOrder inOrder = Mockito.inOrder(mock);
        inOrder.verify(mock).isNew();
        Mockito.verify(mock).create();
        Mockito.verify(mock, Mockito.never()).update();
    }

 

模擬final類或方法

新增EmployeeIdGenerator類:

public final class EmployeeIdGenerator {

    public final static int getNextId() {
        throw new UnsupportedOperationException();
    }
}

在Employee類新增方法:

public void setEmployeeId(int nextId) {

        throw new UnsupportedOperationException();        
    }


修改EmployeeService類的saveEmployee方法:

    public void saveEmployee(Employee employee) {
        if(employee.isNew()) {
            employee.setEmployeeId(EmployeeIdGenerator.getNextId());
            employee.create();
            return;
        }
        employee.update();
    }

修改EmployeeServiceTest類:

import static org.junit.Assert.*;
 
import org.junit.Test;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
 
public class EmployeeControllerTest {
 
    @Test
    public void shouldReturnProjectedCountOfEmployeesFromTheService() {
         
        EmployeeService mock = PowerMockito.mock(EmployeeService.class);
        PowerMockito.when(mock.getEmployeeCount()).thenReturn(8);
        EmployeeController employeeController = new EmployeeController(mock);
        assertEquals(10, employeeController.getProjectedEmployeeCount());
    }
     
    @Test
    public void
    shouldInvokeSaveEmployeeOnTheServiceWhileSavingTheEmployee() {
         
        EmployeeService mock = PowerMockito.mock(EmployeeService.class);    
        EmployeeController employeeController = new EmployeeController(mock);
        Employee employee = new Employee();
        employeeController.saveEmployee(employee);
        Mockito.verify(mock).saveEmployee(employee);
    }        
}

可見final和static的在類頭部處理方法類似, 在測試方法中final和普通方法類似。

 處理構造方法

現在創建新職員的時候要發送歡迎郵件。

新增類WelcomeEmail:

public class WelcomeEmail {

    public WelcomeEmail(final Employee employee, final String message) {
        throw new UnsupportedOperationException();
    }
    
    public void send() {
        throw new UnsupportedOperationException();
    }
}

 

修改EmployeeService類的saveEmployee方法:

    public void saveEmployee(Employee employee) {
        if(employee.isNew()) {
            employee.setEmployeeId(EmployeeIdGenerator.getNextId());
            employee.create();
            WelcomeEmail emailSender = new WelcomeEmail(employee,
            "Welcome to Mocking with PowerMock How-to!");
            emailSender.send();
            return;
        }
        employee.update();
    }
import static org.junit.Assert.*;
 
import org.junit.Test;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
 
public class EmployeeControllerTest {
 
    @Test
    public void shouldReturnProjectedCountOfEmployeesFromTheService() {
         
        EmployeeService mock = PowerMockito.mock(EmployeeService.class);
        PowerMockito.when(mock.getEmployeeCount()).thenReturn(8);
        EmployeeController employeeController = new EmployeeController(mock);
        assertEquals(10, employeeController.getProjectedEmployeeCount());
    }
     
    @Test
    public void
    shouldInvokeSaveEmployeeOnTheServiceWhileSavingTheEmployee() {
         
        EmployeeService mock = PowerMockito.mock(EmployeeService.class);    
        EmployeeController employeeController = new EmployeeController(mock);
        Employee employee = new Employee();
        employeeController.saveEmployee(employee);
        Mockito.verify(mock).saveEmployee(employee);
    }        
}

注意PowerMockito.verifyNew的第2個參數支持前面提到的驗證模式。PowerMockito.whenNew().withArguments(...).thenReturn()是對構造方法的mock模式,PowerMockito.verifyNew().withArguments()是驗證模式。

參數匹配

 PowerMock使用equals方法驗證參數。matcher可更加靈活的處理參數。

為EmployeeController類添加如下方法:

   

    public Employee findEmployeeByEmail(String email) {    
        return employeeService.findEmployeeByEmail(email);
    }
        
    public boolean isEmployeeEmailAlreadyTaken(String email) {
        Employee employee = new Employee();
        return employeeService.employeeExists(employee);
    }

為EmployeeService類添加如下方法:

   

    public Employee findEmployeeByEmail(String email) {
        throw new UnsupportedOperationException();
    }
    
    public boolean employeeExists(Employee employee) {
        throw new UnsupportedOperationException();
    }

  

為EmployeeControllerTest類新增測試

    @Test
    public void shouldFindEmployeeByEmail() {
    
        final EmployeeService mock = PowerMockito.mock(EmployeeService.class);
        final Employee employee = new Employee();
        PowerMockito.when(mock.findEmployeeByEmail(Mockito.startsWith("deep"))).thenReturn(employee);
        final EmployeeController employeeController = new EmployeeController(mock);
        assertSame(employee, employeeController.findEmployeeByEmail("deep@gitshah.com"));
        assertSame(employee, employeeController.findEmployeeByEmail("deep@packtpub.com"));
        assertNull(employeeController.findEmployeeByEmail("noreply@packtpub.com"));
    }
    
    @Test
    public void shouldReturnNullIfNoEmployeeFoundByEmail() {
        
        final EmployeeService mock = PowerMockito.mock(EmployeeService.class);
        PowerMockito.when(mock.findEmployeeByEmail(Mockito.anyString())).thenReturn(null);
        final EmployeeController employeeController = new EmployeeController(mock);
        assertNull(employeeController.findEmployeeByEmail("deep@gitshah.com"));
        assertNull(employeeController.findEmployeeByEmail("deep@packtpub.com"));
        assertNull(employeeController.findEmployeeByEmail("noreply@packtpub.com"));        
    }

 后面還有個基于argThat例子,因為沒有搞到源碼,意思暫時沒有揣度出來。先不涉及。

另外其他類似的內置匹配器如下:Mockito.eq、Mockito.matches、Mockito.any(anyBoolean , anyByte , anyShort , anyChar , anyInt ,anyLong , anyFloat , anyDouble , anyList , anyCollection , anyMap , anySet等等)、Mockito.isNull、Mockito.isNotNull、Mockito.endsWith、Mockito.isA。

 

回答(Answer)

在某些邊緣的情況下不可能通過簡單地通過PowerMockito.when().thenReturn()模擬,這時可以使用Answer接口。

在EmployeeControllerTest類中增加如下方法:

    @Test
    public void shouldFindEmployeeByEmailUsingTheAnswerInterface() {
        
        final EmployeeService mock = PowerMockito.mock(EmployeeService.class);
        final Employee employee = new Employee();
        PowerMockito.when(mock.findEmployeeByEmail(Mockito.anyString())).then(new Answer<Employee>() {
            @Override
            public Employee answer(InvocationOnMock invocation) throws Throwable {
                final String email = (String) invocation.getArguments()[0];
                if(email == null) return null;
                if(email.startsWith("deep")) return employee;
                if(email.endsWith("packtpub.com")) return employee;
                return null;
            }
        });
        final EmployeeController employeeController = new EmployeeController(mock);
        assertSame(employee, employeeController.findEmployeeByEmail("deep@gitshah.com"));
        assertSame(employee, employeeController.findEmployeeByEmail("deep@packtpub.com"));
        assertNull(employeeController.findEmployeeByEmail("hello@world.com"));
    }
    
    @Test
    public void shouldReturnCountOfEmployeesFromTheServiceWithDefaultAnswer() {
        
    EmployeeService mock = PowerMockito.mock(EmployeeService.class, new Answer() {
        @Override
        public Object answer(InvocationOnMock invocation) {
        return 10;
        }
    });
    EmployeeController employeeController = new EmployeeController(mock);
    assertEquals(12, employeeController.getProjectedEmployeeCount());
    }

注意要在頭部導入:

import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

Answer接口指定執行的action和返回值執。 Answer的參數是InvocationOnMock的實例,支持:

  • callRealMethod():調用真正的方法

  • getArguments():獲取所有參數

  • getMethod():返回mock實例調用的方法

  • getMock():獲取mock實例

  • </ul>

    第一個測試方法根據不同情況構造不同返回。第2個測試方法設定調用返回的默認值。

    使用spy進行部分模擬

    現在調整類EmployeeService,拆分saveEmployee為方法:saveEmployee和createEmployee:

        public void saveEmployee(Employee employee) {
            if(employee.isNew()) {
                createEmployee(employee);
                return;
            }
            employee.update();
        }  
        
        void createEmployee(Employee employee) {
            employee.setEmployeeId(EmployeeIdGenerator.getNextId());
            employee.create();
            WelcomeEmail emailSender = new WelcomeEmail(employee,
            "Welcome to Mocking with PowerMock How-to!");
            emailSender.send();
        }

    EmployeeServiceTest類添加測試方法shouldInvokeTheCreateEmployeeMethodWhileSavingANewEmployee:

        @Test
        public void shouldInvokeTheCreateEmployeeMethodWhileSavingANewEmployee() {
            
            final EmployeeService spy = PowerMockito.spy(new EmployeeService());
            final Employee employeeMock = PowerMockito.mock(Employee.class);
            PowerMockito.when(employeeMock.isNew()).thenReturn(true);
            PowerMockito.doNothing().when(spy).createEmployee(employeeMock);
            spy.saveEmployee(employeeMock);
            Mockito.verify(spy).createEmployee(employeeMock);      
        }

    注意spy只能使用PowerMockito.doNothing()/doReturn()/doThrow()。

    模擬私有方法

    現在我們修改EmployeeService.createEmployee為private,在EmployeeServiceTest類添加如下方法:

        @Test
        public void shouldInvokeTheCreateEmployeeMethodWhileSavingANewEmployee() {
            
            final EmployeeService spy = PowerMockito.spy(new EmployeeService());
            final Employee employeeMock = PowerMockito.mock(Employee.class);
            PowerMockito.when(employeeMock.isNew()).thenReturn(true);
            PowerMockito.doNothing().when(spy).createEmployee(employeeMock);
            spy.saveEmployee(employeeMock);
            Mockito.verify(spy).createEmployee(employeeMock);      
        }

    模擬私有方法還有另外一種相對較復雜的方法,這里不做介紹了。

     

    查看封裝內容

    添加 Department類

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

    public class Department {          private List<Employee> employees = new ArrayList<Employee>();         private long maxSalaryOffered;     public void addEmployee(final Employee employee) {         employees.add(employee);         updateMaxSalaryOffered();     }          /*      The private method that keeps track of      max salary offered by this department.     /     private void updateMaxSalaryOffered() {         maxSalaryOffered = 0;         for (Employee employee : employees) {             if(employee.getSalary() > maxSalaryOffered) {                 maxSalaryOffered = employee.getSalary();             }         }     } }</pre>

    修改Employee類的如下方法:

        public long getSalary() {
            return salary;      
        }
        
        public void setSalary(int i) {
            salary = i;        
        }

    新建DepartmentTest類,添加如下測試方法:

    import static org.junit.Assert.*;
    import java.util.ArrayList;
    import java.util.List;
    import org.junit.Test;
    import org.powermock.reflect.Whitebox;

    public class DepartmentTest {     @Test     public void shouldVerifyThatNewEmployeeIsAddedToTheDepartment() {                  final Department department = new Department();         final Employee employee = new Employee();         department.addEmployee(employee);         final List<Employee> employees = Whitebox.getInternalState(department, "employees");         assertTrue(employees.contains(employee));     }               @Test     public void shouldAddNewEmployeeToTheDepartment() {                  final Department department = new Department();         final Employee employee = new Employee();         final ArrayList<Employee> employees = new ArrayList<Employee>();         Whitebox.setInternalState(department, "employees", employees);         department.addEmployee(employee);         assertTrue(employees.contains(employee));     }          @Test     public void shouldVerifyThatMaxSalaryOfferedForADepartmentIsCalculatedCorrectly() throws Exception     {                  final Department department = new Department();         final Employee employee1 = new Employee();         final Employee employee2 = new Employee();         employee1.setSalary(60000);         employee2.setSalary(65000);         //Adding two employees to the test employees list.         final ArrayList<Employee> employees = new ArrayList<Employee>();         employees.add(employee1);         employees.add(employee2);         Whitebox.setInternalState(department, "employees", employees);         Whitebox.invokeMethod(department,"updateMaxSalaryOffered");         final long maxSalary = Whitebox.getInternalState(department, "maxSalaryOffered");         assertEquals(65000, maxSalary);     } }</pre>

     Whitebox.getInternalState(department, "employees")類似堆棧,查看變量的值。Whitebox.setInternalState(department, "employees",

    employees)設置變量的值。 Whitebox.invokeMethod(department, "updateMaxSalaryOffered")調用方法。

    更多參考:http://powermock.googlecode.com/svn/docs/powermock-1.5/apidocs/org/powermock/reflect/Whitebox.html。

     

    禁用非預期行為

    新增類BaseEntity:

    public class BaseEntity {

        static {         String x = null;         x.toString();     }          public BaseEntity() {         throw new UnsupportedOperationException();     }          protected void performAudit(String auditInformation) {         throw new UnsupportedOperationException();     } }</pre>

    修改類Department:

    public class Department extends BaseEntity {

        private int departmentId;     private String name;          public Department(int departmentId) {         super();         this.departmentId = departmentId;     }          public void setName(String name) {         this.name = name;         super.performAudit(this.name);     }              protected void performAudit(String auditInformation) {         throw new UnsupportedOperationException();     }          public Object getDepartmentId() {         return departmentId;     }          public Object getName() {         return name;     } }</pre>

    修改類DepartmentTest:

    import static org.junit.Assert.*;

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

    @RunWith(PowerMockRunner.class) @PrepareForTest(Department.class) @SuppressStaticInitializationFor("BaseEntity") public class DepartmentTest {          @Test     public void shouldSuppressTheBaseConstructorOfDepartment() {         PowerMockito.suppress(PowerMockito.constructor(BaseEntity.class));         assertEquals(10, new Department(10).getDepartmentId());     }          @Test     public void shouldSuppressThePerformAuditMethodOfBaseEntity() {         PowerMockito.suppress(PowerMockito.constructor(BaseEntity.class));         PowerMockito.suppress(PowerMockito.method(BaseEntity.class, "performAudit", String.class));         final Department department = new Department(18);         department.setName("Mocking with PowerMock");         assertEquals("Mocking with PowerMock", department.getName());     }          @Test         public void shouldSuppressTheInitializerForBaseEntity() {                      PowerMockito.suppress(PowerMockito.constructor(BaseEntity.class));             assertNotNull(new Department(18));     } }</pre>

    PowerMockito.suppress(PowerMockito.constructor(BaseEntity.class));表示禁用BaseEntity的構造函數。PowerMockito.suppress(PowerMockito.constructor(BaseEntity.class, String.class, Integer.class))后面表示帶字符串和整數參數。

    PowerMockito.suppress(PowerMockito.method(BaseEntity.class, "performAudit", String.class))表示禁用BaseEntity的performAudit方法。

    @SuppressStaticInitializationFor("BaseEntity")表示禁用BaseEntity的靜態初始化。注意引號部分通常需要全名,比如"com.gitshah.powermock.BaseEntity"。

    PowerMockito.suppress(PowerMockito.field(BaseEntity.class,"identifier")):禁用域。

    參考資料

    簡明教程: https://github.com/jayway/powermock

    https://github.com/jayway/powermock/wiki/MockitoUsage

    https://www.ibm.com/developerworks/cn/java/j-lo-powermock/

     書籍:Instant Mock Testing with PowerMock

    作者博客:http://my.oschina.net/u/1433482 python測試開發精華群 291184506 PythonJava單元白盒測試 144081101

     

    來自: http://my.oschina.net/u/1433482/blog/602318

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!