同一文件含多个类时编写JUnit单元测试的问题
问题:跨包测试无法访问默认权限的顶级类B
问题场景
将编程挑战的题目和测试代码复制到本地后,测试无法正常运行。核心原因是:src.main.java包下的文件中包含两个顶级类——public权限的A和默认包权限的B,而测试类ATest位于src.test.java包下,跨包无法访问默认权限的B类。
原代码文件
文件1:src/main/java/A.java
package src.main.java; public class A{ public String doSomething(B objB){ return objB.getName(); } } class B{ private String name; public B(String name){ this.name=name; } public String getName(){ return name; } }
文件2:src/test/java/ATest.java
package src.test.java; import org.junit.Test; import static org.junit.Assert.assertEquals; import src.main.java.A; public class ATest{ @Test public void test01() { assertEquals("Name1", A.doSomething(new B("Name1"))); } }
已验证的有效解决方案
我尝试了几种调整方式,测试都能正常运行:
- 将B改为A的public静态内部类
修改后的A.java:
测试类调用方式调整:package src.main.java; public class A{ public String doSomething(B objB){ return objB.getName(); } // 将类B改为A的public静态内部类 public static class B{ private String name; public B(String name){ this.name=name; } public String getName(){ return name; } } }package src.test.java; import org.junit.Test; import static org.junit.Assert.assertEquals; import src.main.java.A; public class ATest{ @Test public void test01() { assertEquals("Name1", A.doSomething(new A.B("Name1"))); } } - 将B单独放在独立文件中
新建src/main/java/B.java存放B类,测试类中导入该类即可正常调用:package src.test.java; import org.junit.Test; import static org.junit.Assert.assertEquals; import src.main.java.A; import src.main.java.B; public class ATest{ @Test public void test01() { assertEquals("Name1", A.doSomething(new B("Name1"))); } } - 将测试类与目标类放在同包
把ATest移到src.main.java包下,无需修改任何类结构,就能直接访问默认权限的B类。
疑问解答
1. 出题人未做修改的原因
大概率是出题人将测试类和A、B放在了同一个包下。Java的默认包权限允许同包内的所有类互相访问,这种情况下测试类可以直接使用B,不需要调整类结构或权限。
2. 无需调整类结构或包路径的解决办法
没有符合Java规范且无需调整的合法方案。因为Java访问控制规则明确:默认包权限的类只能被同包内的类访问,跨包场景下无法直接引用。
如果非要绕过规则,只能用反射,但这属于非常规手段,破坏封装性且代码可读性极差,仅适合临时调试,绝对不推荐作为常规测试方案:
@Test public void test01() throws Exception { // 反射创建B实例 Class<?> bClass = Class.forName("src.main.java.B"); Constructor<?> constructor = bClass.getConstructor(String.class); Object bObj = constructor.newInstance("Name1"); // 反射调用A的方法 A a = new A(); Method doSomethingMethod = A.class.getMethod("doSomething", bClass); String result = (String) doSomethingMethod.invoke(a, bObj); assertEquals("Name1", result); }
内容的提问来源于stack exchange,提问作者Ivo Heberle
相关产品推荐
相关产品推荐

