是否应该对private的方法写UnitTest code

您所在的位置:网站首页 jmockit注解 是否应该对private的方法写UnitTest code

是否应该对private的方法写UnitTest code

2023-02-24 19:40| 来源: 网络整理| 查看: 265

最近在写UnitTest代码的时候遇到了点小问题,在解决过程当中有了点小心得,做一下记录。 完整代码在此处下载: https://download.csdn.net/download/zxm317122667/10652780

问题主要对于 private 方法的单元测试。

比如如下代码:有两个类,分别是Fish和Rod 鱼Fish.java

package com.example.dannyjiang.testprivatedemo; public class Fish { // 某一条鱼Fish的唯一标识 public long id; // Fish的X坐标 private int x; // Fish的Y坐标 private int y; // Fish的大小,默认为100 public int size = 100; public Fish() { } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } }

鱼钩Rod.java

package com.example.dannyjiang.testprivatedemo; public class Rod { // Rod的唯一标识 public long id; // Rod的X坐标 private int x; // Rod的Y坐标 private int y; // Fish的大小,默认为100 private int size = 100; public int getX() { return x; } public int getY() { return y; } public int getSize() { return size; } public void setX(int x) { this.x = x; } public void setY(int y) { this.y = y; } }

以及一个用来判断一个Rod是否钓到了一条鱼Fish的封装类Controller.java

package com.example.dannyjiang.testprivatedemo; import java.util.List; public class Controller { /** * 传入一个List,判断在此List中,是否有与Rod匹配的Fish对象 * @param fishList * @param rod */ public boolean hasFishMatched(List fishList, Rod rod) { for (Fish fish : fishList) { // 需要判断Fish是否与Rod重叠 if (fishOverlapWithRod(fish, rod)) { System.out.println(String.format("fish %d has been matched", fish.id)); return true; } } System.out.println("no fish found"); return false; } /** * 判断Fish是否与某一个Rod有重叠 * @param fish * @param rod * @return */ private boolean fishOverlapWithRod(Fish fish, Rod rod) { return fish.getX() rod.getX() && fish.getY() rod.getY(); } }

可以看到在Controller类中提供了一个public的方法给外部调用。但是在这个方法中还调用了一个private的fishOverlapWithRod方法。这样就造成很难去给hasFishMatched写单元测试代码。在网上查过很多资料如何去给一个private的方法写单元测试。网上主要介绍了有几种框架,例如:PowerMOckito、JMockit、或者微软的MSTest。但是一次很偶然的机会看到了Practical Object Oriented Design in Ruby这本书的作者Sandi Metz写的一句话:

The solution to the problem of costly tests, however, Getting good value from tests requires clarity of intention and knowing what, when, and how to test.

才发现如果想对一个private的方法去写UnitTest,则已经说明代码设计上是存在问题的。

问题主要是如下两点: 违反了类的单一职责原则(SRP) 这种写法属于Code Smell中的特性嫉妒(Feature envy)或者是数据簇 解决思路

就是使用代码重构中的 Extract Class 将判断Fish和Rod是否重叠的代码抽象到Fish中。 修改后的代码如下: Fish.java

package com.example.dannyjiang.testprivatedemo; public class Fish { // 某一条鱼Fish的唯一标识 public long id; // Fish的X坐标 private int x; // Fish的Y坐标 private int y; // Fish的大小,默认为100 public int size = 100; public Fish() { } public boolean overlap(Rod rod) { if (rod == null) { throw new RuntimeException("rod is null"); } return x rod.getX() && y rod.getY(); } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } }

可以看待在Fish.java中多了一个overlap方法专门用来判断它是否与传入的Rod重叠。 同时Rod.java不需要做其它的修改。这样修改后,Controller.java中只要改为如下即可:

package com.example.dannyjiang.testprivatedemo; import java.util.List; public class Controller { /** * 传入一个List,判断在此List中,是否有与Rod匹配的Fish对象 * @param fishList * @param rod */ public boolean hasFishMatched(List fishList, Rod rod) { for (Fish fish : fishList) { // 需要判断Fish是否与Rod重叠 if (fish.overlap(rod)) { System.out.println(String.format("fish %d has been matched", fish.id)); return true; } } System.out.println("no fish found"); return false; } } 单元测试

最后分别对Controller.java和Fish.java书写UnitTest代码即可实现功能代码的覆盖率,代码如下: ControllerTest.java

package com.example.dannyjiang.testprivatedemo; import org.junit.Test; import java.util.ArrayList; import java.util.List; import static org.junit.Assert.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class ControllerTest { @Test public void no_fish_match1() { Controller controller = new Controller(); List fishList = new ArrayList(); Fish fish1 = mock(Fish.class); Fish fish2 = mock(Fish.class); Fish fish3 = mock(Fish.class); fishList.add(fish1); fishList.add(fish2); fishList.add(fish3); Rod rod = mock(Rod.class); when(fish1.overlap(rod)).thenReturn(false); when(fish2.overlap(rod)).thenReturn(false); when(fish3.overlap(rod)).thenReturn(false); assertFalse(controller.hasFishMatched(fishList, rod)); } @Test public void has_fish_match1() { Controller controller = new Controller(); List fishList = new ArrayList(); Fish fish1 = mock(Fish.class); Fish fish2 = mock(Fish.class); Fish fish3 = mock(Fish.class); fishList.add(fish1); fishList.add(fish2); fishList.add(fish3); Rod rod = mock(Rod.class); when(fish1.overlap(rod)).thenReturn(false); when(fish2.overlap(rod)).thenReturn(true); when(fish3.overlap(rod)).thenReturn(false); assertFalse(controller.hasFishMatched(fishList, rod)); } }

FishTest.java

package com.example.dannyjiang.testprivatedemo; import org.junit.Before; import org.junit.Test; import static org.junit.Assert.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class FishTest { private Fish fish = null; @Before public void setUp() { fish = new Fish(); fish.id = 1; fish.setX(0); fish.setY(0); fish.size = 100; } @Test(expected = RuntimeException.class) public void process_fish_with_null() { fish.overlap(null); } @Test public void process_fish_not_overlapped() { Rod mockRod = mock(Rod.class); when(mockRod.getX()).thenReturn(101); when(mockRod.getY()).thenReturn(101); when(mockRod.getSize()).thenReturn(100); assertFalse(fish.overlap(mockRod)); } @Test public void process_fish_overlapped() { Rod mockRod = mock(Rod.class); when(mockRod.getX()).thenReturn(50); when(mockRod.getY()).thenReturn(50); when(mockRod.getSize()).thenReturn(100); assertTrue(fish.overlap(mockRod)); } }


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3