软件设计程序基础-Junit进阶篇

第1关:Junit参数化测试

100

  • 任务要求
  • 参考答案
  • 评论11

任务描述

根据所学内容,要求用户补全Junit的参数化测试代码。

相关知识
Junit参数化测试

如果测试代码大同小异,代码结构都是相同的,不同的只是测试的数据和预期值,那么Junit的参数化测试可以派上用场了:Junit的参数化测试允许开发人员使用不同的参数反复运行同一个测试。你将遵循以下 5 个步骤来创建参数化测试。

  1. 用 @RunWith(Parameterized.class) 来注释 test 类。
  2. 创建一个由 @Parameters 注释的公共的静态方法,它返回一个对象的集合(数组)来作为测试数据集合。
  3. 创建一个公共的构造函数,它接受和一行测试数据相等同的东西。
  4. 为每一列测试数据创建一个实例变量。
  5. 用实例变量作为测试数据的来源来创建你的测试用例。

在JUnit中,可以使用@RunWith@parameter这两个注解来为单元测试传递参数。

@RunWith注解:当类被@RunWith注解修饰,或者类继承了一个被该注解修饰的类,JUnit将会使用这个注解所指明的运行器(runner)来运行测试。

@Parameters注解:然后在该类提供数据的方法上加上一个@Parameters注解,这个方法必须是静态static的,不能带参数,并且返回一个集合Collection

在测试类的构造方法中为各个参数赋值,(构造方法是由JUnit调用的),最后编写测试类,它会根据参数的组数来运行测试多次。

注意:必须要为类的所有字段赋值,不管你是不是都用到!否则,Junit会出错。

代码示例

我们现在来看一个简单的 add 方法。

 
  1. //MathUtils.java
  2. package com.trustie.junittest;
  3. public class MathUtils {
  4. public static int add(int a, int b) {
  5. return a + b;
  6. }
  7. }

接下来我们创建一个为上面的JUnit测试类 MathUtilsTest.java

 
  1. //MathUtilsTest.java
  2. package com.trustie.junittest;
  3. import java.util.Arrays;
  4. import java.util.Collection;
  5. import org.junit.Assert;
  6. import org.junit.Test;
  7. import org.junit.runner.RunWith;
  8. import org.junit.runners.Parameterized;
  9. import org.junit.runners.Parameterized.Parameters;
  10. import com.trustie.junittest.MathUtils;
  11. /**
  12. * 参数化测试的类必须有Parameterized测试运行器修饰
  13. *
  14. */
  15. @RunWith(Parameterized.class)
  16. public class MathUtilsTest {
  17. private int input1;
  18. private int input2;
  19. private int expected;
  20. /**
  21. * 准备数据。数据的准备需要在一个方法中进行,该方法需要满足一定的要求:
  22. 1)该方法必须由Parameters注解修饰
  23. 2)该方法必须为public static的
  24. 3)该方法必须返回Collection类型
  25. 4)该方法的名字不做要求
  26. 5)该方法没有参数
  27. * @return
  28. 这里设置了四组参数,只有当四组参数都通过测试,才代表测试通过
  29. */
  30. @Parameters
  31. public static Collection prepareData(){
  32. Object [][] bject = {{-1,-2,-3},{0,2,2},{-1,1,0},{1,2,3}};
  33. return Arrays.asList(bject);
  34. }
  35. public MathUtilsTest(int input1,int input2,int expected){
  36. this.input1 = input1;
  37. this.input2 = input2;
  38. this.expected = expected;
  39. }
  40. @Test
  41. public void testAdd(){
  42. MathUtils add = new MathUtils();
  43. int result = add.add(input1, input2);
  44. Assert.assertEquals(expected,result);
  45. }
  46. }
  47. /********************【测试执行流程】************************************************************************/
  48. // 首先会执行prepareData()方法,将准备好的数据作为一个Collection返回;
  49. // 接下来根据准备好的数据调用构造方法。Collection中有几个元素,该构造方法就会被调用几次;
  50. // 我们这里Collection中有4个元素,所以MathUtilsTest()构造方法会被调用4次,于是会产生4个该测试类的对象;
  51. // 对于每一个测试类的对象,都会去执行testAdd()方法;
  52. // Collection中的数据是来自于JUnit传给MathUtilsTest(int input1,int input2,int expected)构造方法的
  53. // 于是testAdd()用到的三个私有参数,就被MathUtilsTest()构造方法设置好值了,而它们三个的值就来自于Collection。
  54. /************************************************************************************************************/
编程要求

给定一个减法函数Calculator.java如下:

 
  1. Calculator.java
  2. /**
  3. * 数学计算-->减法
  4. */
  5. public class Calculator {
  6. public int sub(int a, int b) {
  7. return a - b;
  8. }
  9. }

请按照上面索索的知识,补全题目中参数化测试 ParameterTest.javaprepareData()函数。

本关涉及的代码文件ParameterTest.java的代码如下:

 
  1. package step1;
  2. import static org.junit.Assert.assertEquals; //静态导入
  3. import java.util.Arrays;
  4. import java.util.Collection;
  5. import org.junit.Test;
  6. import org.junit.runner.RunWith;
  7. import org.junit.runners.Parameterized;
  8. import org.junit.runners.Parameterized.Parameters;
  9. import step1.Calculator;
  10. /**
  11. * JUnit4的参数化测试
  12. */
  13. @RunWith(Parameterized.class)
  14. public class ParameterTest {
  15. private int input11;
  16. private int input22;
  17. private int expected;
  18. public ParameterTest(int input11, int input22, int expected){
  19. this.input11 = input11;
  20. this.input22 = input22;
  21. this.expected = expected;
  22. }
  23. @Parameters
  24. public static Collection prepareData(){
  25. /**
  26. *该二维数组的类型必须是Object类型的
  27. *该二维数组中的数据是为测试Calculator中的sub()方法而准备的
  28. *该二维数组中的每一个元素中的数据都对应着构造方法ParameterTest()中的参数的位置
  29. *所以依据构造方法的参数位置判断,该二维数组中的第一个数减去第二个数等于第三个数
  30. *请在Begin/End内补全代码,要求为单元测试传递4组参数,来验证Calculator中的sub函数编写是否正确
  31. *提示:只需要补2行代码
  32. */
  33. /*********************************Begin*************************************************/
  34. /**********************************End**************************************************/
  35. }
  36. @Test
  37. public void testSub(){
  38. Calculator cal = new Calculator();
  39. assertEquals(cal.sub(input11, input22), expected);
  40. }
  41. }
评测说明

本关卡的测试文件是TestRunner.java,该文件进行了函数封装且学员不可见,用于验证学员的Junit测试代码是否正确。

具体测试过程如下:

1.平台自动编译生成TestRunner.exe; 2.平台运行TestRunner.exe; 3.获取TestRunner.exe输出,并将其输出与预期输出对比:如果一致则测试通过,否则测试失败。

预期输入: 预期输出:true

友情提示

1.请不要直接println最终输出,否则平台发现此类情况后,将一律扣掉本关经验值,并且追加处罚措施。

2.学员答题时请尽量手敲代码,请勿从实训讲解代码片段中复制代码段粘贴到答题区域作答,复制的内容会保留一些格式和字符,导致编译失败。

开始你的任务吧,祝你成功!

package step1;

import static org.junit.Assert.assertEquals; //静态导入
import java.util.Arrays;
import java.util.Collection;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import step1.Calculator;
/**
 * JUnit4的参数化测试
 */
@RunWith(Parameterized.class)
public class ParameterTest {
    private int input11;
    private int input22;
    private int expected;
    public ParameterTest(int input11, int input22, int expected){
        this.input11 = input11;
        this.input22 = input22;
        this.expected = expected;
    }

    @Parameters
    public static Collection prepareData(){
        /**
         *该二维数组的类型必须是Object类型的
         *该二维数组中的数据是为测试Calculator中的sub()方法而准备的
         *该二维数组中的每一个元素中的数据都对应着构造方法ParameterTest()中的参数的位置
         *所以依据构造方法的参数位置判断,该二维数组中的第一个数减去第二个数等于第三个数
         *请在Begin/End内补全代码,要求为单元测试传递4组参数,来验证Calculator中的sub函数编写是否正确
         *提示:只需要补2行代码
         */
        /*********************************Begin*************************************************/
		Object[][] bject ={{-1,-2,1},{0,2,-2},{-1,1,-2},{1,2,-1}};
		return Arrays.asList(bject);

        /**********************************End**************************************************/

    }
    @Test
    public void testSub(){
        Calculator cal = new Calculator();
        assertEquals(cal.sub(input11, input22), expected);
    }
}

第2关:Junit异常测试

100

  • 任务要求
  • 参考答案
  • 评论11

任务描述

学员写一个Junit异常测试,用来判断实例化的对象数据是否合法。

相关知识
Junit异常测试

Junit 用代码处理提供了一个追踪异常的选项。你可以测试代码是否它抛出了想要得到的异常。通过@Test元数据中的expected属性验证是否抛出期望的异常,expected属性的值是一个异常的类型,如果抛出了期望的异常,则测试通过,否则不通过。

现在让我们看下 @Test(expected=* .class)注解的具体使用。

代码示例
 
  1. //ExceptionTest.java
  2. public class ExceptionTest {
  3. @Test(expected = ArithmeticException.class)
  4. public void divisionWithException() {
  5. int i = 1/0;
  6. }
  7. }

在上述例子中,divisionWithException()方法将抛出ArithmeticException异常,因为这是一个预期的异常,因此单元测试会通过。其实,Junit的异常测试我们追踪到了预期的异常信息,所以代码会执行成功而不是中断

编程要求

本关的任务是写一个Junit异常测试,用来判断实例化的对象数据是否合法。具体在JunitException.java中补全异常测试代码:要求判断Person类中实例化的对象年龄是否合法,如果不合法则抛出IllegalArgumentException异常。

Person类代码如下:

 
  1. //Person.java
  2. public class Person {
  3. private String name;
  4. private int age;
  5. public String getName() {
  6. return name;
  7. }
  8. public void setName(String name) {
  9. this.name = name;
  10. }
  11. public int getAge() {
  12. return age;
  13. }
  14. public void setAge(int age) {
  15. if (age < 0 ) {
  16. throw new IllegalArgumentException("age is invalid");
  17. }
  18. this.age = age;
  19. }
  20. }

测试类JunitException.java的代码如下:

 
  1. package step2;
  2. import static org.junit.Assert.*;
  3. import org.junit.Rule;
  4. import org.junit.Test;
  5. import org.junit.rules.ExpectedException;
  6. import step2.Person;
  7. public class JunitException {
  8. /**
  9. *请在Begin/End内加一行注解,要求检查Person对象的年龄是否合法,不合法则
  10. *抛出IllegalArgumentException异常
  11. */
  12. /************************************Begin**********************************************/
  13. /************************************End************************************************/
  14. public void checkage() {
  15. Person person = new Person();
  16. person.setAge(-1);
  17. }
  18. }
评测说明

本关卡的测试文件是TestRunner.java,该文件进行了函数封装且学员不可见,用于验证学员的Junit测试代码是否正确。

具体测试过程如下:

1.平台自动编译生成TestRunner.exe; 2.平台运行TestRunner.exe; 3.获取TestRunner.exe输出,并将其输出与预期输出对比:如果一致则测试通过,否则测试失败。

预期输入: 预期输出:true

友情提示

1.请不要直接println最终输出,否则平台发现此类情况后,将一律扣掉本关经验值,并且追加处罚措施。

2.学员答题时请尽量手敲代码,请勿从实训讲解代码片段中复制代码段粘贴到答题区域作答,复制的内容会保留一些格式和字符,导致编译失败。

开始你的任务吧,祝你成功!

package step2;
import static org.junit.Assert.*;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import step2.Person;



public class JunitException {

    /**
     *请在Begin/End内加一行注解,要求检查Person对象的年龄是否合法,不合法则
     *抛出IllegalArgumentException异常
     */
    /************************************Begin**********************************************/
 	@Test(expected = IllegalArgumentException.class)  

    /************************************End************************************************/

    public void checkage() {
    Person person = new Person();
    person.setAge(-1);

    }
}

第3关:Junit套件测试

100

  • 任务要求
  • 参考答案
  • 评论11

任务描述

根据所学内容,要求用户补全Junit的套件测试代码。 ####相关知识 #####本关必读

测试套件意味着捆绑几个单元测试用例并且一起执行他们。在 JUnit 中,@RunWith 和 @Suite 注释用来运行套件测试。

代码示例

对下面两个类进行单元测试 ,组成套件测试。

类代码:

 
  1. //Car.java
  2. public class Car {
  3. public int getWheels() {
  4. return 4;
  5. }
  6. }
 
  1. //Rectangle.java
  2. public class Rectangle{
  3. public int getArea(int width,int height){
  4. return width*height;
  5. }
  6. }

测试类代码:

 
  1. //CarTest.java
  2. public class CarTest {
  3. Car car;
  4. @Before
  5. public void setUp() throws Exception {
  6. car = new Car();
  7. }
  8. @Test
  9. public void testGetWheels() {
  10. int result = car.getWheels();
  11. assertEquals(4, result);
  12. }
  13. }
 
  1. //RectangleTest.java
  2. public class RectangleTest {
  3. Rectangle rectangle;
  4. @Before
  5. public void setUp() throws Exception {
  6. rectangle=new Rectangle();
  7. }
  8. @Test
  9. public void testGetArea() {
  10. int result = rectangle.getArea(12, 2);
  11. assertEquals(24, result);
  12. }
  13. }

测试套件代码:

 
  1. @RunWith(Suite.class)
  2. @Suite.SuiteClasses({ CalculateTest.class, CarTest.class, RectangleTest.class })
  3. public class AllTests {
  4. }

注意:套件测试代码需要紧靠测试类,不能有换行!

编程要求

本关卡的要求是让学员补全Junit的套件测试代码。具体要求如下:

给定一个类Calculate.java和对应的测试类CalculateTest.java,如下:

 
  1. //Calculate.java
  2. public class Calculate {
  3. public int add(int a, int b) {
  4. return a + b;
  5. }
  6. }
 
  1. //CalculateTest.java
  2. public class CalculateTest {
  3. Calculate calculate;
  4. @Before
  5. public void setUp() throws Exception {
  6. calculate = new Calculate();
  7. }
  8. @Test
  9. public void testAdd() {
  10. int result = calculate.add(12, 12);
  11. assertEquals(24, result);
  12. }

请在SuiteTest.java内补全CalculateTest和CarTest的套件测试代码。

本关涉及到的SuiteTest.java代码如下:

 
  1. package step3;
  2. import static org.junit.Assert.*;
  3. import org.junit.Test;
  4. import org.junit.runner.RunWith;
  5. import org.junit.runners.Suite;
  6. import step3.Calculate;
  7. import step3.CalculateTest;
  8. import step3.Car;
  9. import step3.CarTest;
  10. /*
  11. 请在星号后加两行注解,要求实现CalculateTest类和CarTest类的套件测试
  12. 套件测试代码需要紧靠SuiteTest这个类,不能有换行
  13. */
  14. //**************************************************************
  15. public class SuiteTest {
  16. }
评测说明

本关卡的测试文件是TestRunner.java,该文件进行了函数封装且学员不可见,用于验证学员的Junit测试代码是否正确。

具体测试过程如下:

1.平台自动编译生成TestRunner.exe; 2.平台运行TestRunner.exe; 3.获取TestRunner.exe输出,并将其输出与预期输出对比:如果一致则测试通过,否则测试失败。

预期输入: 预期输出:true

友情提示

1.请不要直接println最终输出,否则平台发现此类情况后,将一律扣掉本关经验值,并且追加处罚措施。

2.学员答题时请尽量手敲代码,请勿从实训讲解代码片段中复制代码段粘贴到答题区域作答,复制的内容会保留一些格式和字符,导致编译失败。

开始你的任务吧,祝你成功!

package step3;

import static org.junit.Assert.*;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import step3.Calculate;
import step3.CalculateTest;
import step3.Car;
import step3.CarTest;


/*
请在星号后加两行注解,要求实现CalculateTest类和CarTest类的套件测试
套件测试代码需要紧靠SuiteTest这个类,不能有换行
*/
//**************************************************************
@RunWith(Suite.class)
@Suite.SuiteClasses({ CalculateTest.class, CarTest.class, RectangleTest.class })
public class SuiteTest {


}

第4关:命令行下进行Junit测试

100

  • 任务要求
  • 参考答案
  • 评论11

任务描述

补全TestRunner.java中的代码,如果测试类JunitSubTest.java中的测试都通过,则main函数会打印true。

环境配置

环境配置:Linux主机+JDK 1.8+Junit 4.12。

首先确保自己在linux主机装好了java环境,配置好环境变量。

然后下载两个jar包:

junit 4.12 :Release JUnit 4.12 · junit-team/junit4 · GitHub

hamcrest-core-1.3.jar : http://central.maven.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar

让后把这两个包放到jdk安装的lib目录下,本机的JDK安装目录如下:

 
  1. /home/soft/jdk1.8.0_111/lib# ls
  2. amd64 hamcrest-core-1.3.jar jexec packager.jar
  3. ant-javafx.jar ir.idl junit-4.12.jar sa-jdi.jar
  4. ct.sym javafx-mx.jar missioncontrol tools.jar
  5. dt.jar jconsole.jar orb.idl

然后修改本机环境变量,

vim ~/.bashrc

修改成如下:

 
  1. export JAVA_HOME=/home/soft/jdk1.8.0_111
  2. export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/junit-4.12.jar:$JAVA_HOME/lib/hamcrest-core-1.3.jar:$CLASSPATH
  3. export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH:$HOME/bin

这里着重看下CLASSPATH,JAVA_HOME和PATH如果设置过,就不用修改了。

然后source ~/.bashrc 让环境变量生效。

新建两个java文件:Calculate.java和CalculateTest.java。

 
  1. //Calculate.java
  2. package com.trustie.junitest;
  3. public class Calculate {
  4. public int sum(int var1, int var2) {
  5. System.out.println("相加的值是: " + var1 + " + " + var2);
  6. return var1 + var2;
  7. }
  8. public static void main(String[] args) {
  9. System.out.println("hh");
  10. }
  11. }
 
  1. //CalculateTest.java
  2. package com.trustie.test;
  3. import org.junit.Test;
  4. import static org.junit.Assert.assertEquals;
  5. import org.junit.Test;
  6. import com.trustie.junitest.Calculate;
  7. public class CalculateTest {
  8. Calculate calculation = new Calculate();
  9. int sum = calculation.sum(2, 5);
  10. int testSum = 7;
  11. @Test
  12. public void testSum() {
  13. System.out.println("@Test sum(): " + sum + " = " + testSum);
  14. assertEquals(sum, testSum);
  15. }
  16. }

然后编译执行:

 
  1. javac -d . Calculate.java
  2. javac -d . CalculateTest.java
  3. java org.junit.runner.JUnitCore com.trustie.test.CalculateTest

就可以看到如下打印:

 
  1. JUnit version 4.12
  2. 相加的值是: 2 + 5
  3. .@Test sum(): 7 = 7
  4. Time: 0.003
  5. OK (1 test)

至此,Junit环境配置成功,可以在本机命令行下运行Junit测试。

其实在命令行中运行JUnit测试,使用了org.junit.runner.JUnitCore类。 这个类提供了runClasses()方法,它允许运行一个或多个测试类。runClasses()方法返回类型是org.junit.runner.Result对象类型。 这个对象可以被用来收集关于测试信息。此外,如果有一个失败的测试,可以用org.junit.runner.notification.Failure对象保存失败测试的描述。

代码示例

之前各个关卡中的TestRunner.java我们现在揭开谜底吧:

 
  1. //TestRunner.java
  2. import org.junit.runner.JUnitCore;
  3. import org.junit.runner.Result;
  4. import org.junit.runner.notification.Failure;
  5. public class TestRunner {
  6. public static void main(String[] args) {
  7. Result result = JUnitCore.runClasses(Test.class);
  8. for (Failure failure : result.getFailures()) {
  9. System.out.println(failure.toString());
  10. }
  11. System.out.println(result.wasSuccessful());
  12. }
  13. }

如果你的测试类Test.java中所有测试都通过,以上代码会打印true。

本关任务

补全TestRunner.java中的代码,如果测试类JunitSubTest.java中的测试都通过,则main函数会打印true。

本关涉及的代码文件TestRunner.java的代码如下:

 
  1. package step4;
  2. import org.junit.runner.JUnitCore;
  3. import org.junit.runner.Result;
  4. import org.junit.runner.notification.Failure;
  5. public class TestRunner {
  6. public static void main(String[] args) {
  7. //请在Begin/End内加一行代码,要求如果测试类JunitSubTest.java中的测试都通过,则main函数会打印true
  8. /******************************Begin**************************************************/
  9. /******************************End****************************************************/
  10. for (Failure failure : result.getFailures()) {
  11. System.out.println(failure.toString());
  12. }
  13. System.out.println(result.wasSuccessful());
  14. }
  15. }
评测说明

本关卡的测试文件是TestRunner.java,用于验证学员的Junit测试代码是否正确。

具体测试过程如下:

1.平台自动编译生成TestRunner.exe; 2.平台运行TestRunner.exe; 3.获取TestRunner.exe输出,并将其输出与预期输出对比:如果一致则测试通过,否则测试失败。

预期输入: 预期输出:true

友情提示

1.请不要直接println最终输出,否则平台发现此类情况后,将一律扣掉本关经验值,并且追加处罚措施。

2.学员答题时请尽量手敲代码,请勿从实训讲解代码片段中复制代码段粘贴到答题区域作答,复制的内容会保留一些格式和字符,导致编译失败。

开始你的任务吧,祝你成功!

package step4;


import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;

public class TestRunner {
   public static void main(String[] args) {
    //请在Begin/End内加一行代码,要求如果测试类JunitSubTest.java中的测试都通过,则main函数会打印true
    /******************************Begin**************************************************/
	  Result result = JUnitCore.runClasses(JunitSubTest.class);
    /******************************End****************************************************/
      for (Failure failure : result.getFailures()) {
         System.out.println(failure.toString());
      }
      System.out.println(result.wasSuccessful());
   }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
powermock-module-junit4-common是PowerMock框架中的一个模块,用于与JUnit4集成。这个模块可以在测试过程中模拟和修改静态方法、私有方法、构造函数和final类等。当我们需要对这些难以测试的代码进行单元测试时,可以使用这个模块来解决这个问题。 要下载powermock-module-junit4-common,需要先确保在项目的构建工具中引入了PowerMock框架的依赖。如果使用Maven进行构建,可以在pom.xml文件中添加以下依赖: <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4-common</artifactId> <version>版本号</version> <scope>test</scope> </dependency> 在Gradle中,可以在build.gradle文件的dependencies部分添加以下代码: testImplementation 'org.powermock:powermock-module-junit4-common:版本号' 在配置好依赖之后,再执行构建工具的相命令,即可自动下载并引入powermock-module-junit4-common模块。 通过使用powermock-module-junit4-common,我们可以更方便地编写单元测试,尤其适用于需要模拟静态方法、私有方法或者处理final类的情况。它为我们提供了更多的测试可能性,使得我们能够更全面地覆盖代码并提高测试覆盖率。同时,它也提供了一系列好用的工具类和注解,让测试代码编写更加简洁高效。 总之,下载并使用powermock-module-junit4-common模块可以为我们提供更强大的测试能力,帮助我们更好地进行单元测试

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值