您如何断言在 JUnit 测试中引发了某个异常?

问:

如何以惯用方式使用 JUnit 来测试某些代码是否引发异常?

虽然我当然可以做这样的事情:

@Test
public void testFooThrowsIndexOutOfBoundsException() {
  boolean thrown = false;

  try {
    foo.doStuff();
  } catch (IndexOutOfBoundsException e) {
    thrown = true;
  }

  assertTrue(thrown);
}

我记得有一个注释或一个 Assert.xyz 或其他东西,在这种情况下,JUnit 的精神远不那么笨拙,而且更符合 JUnit 的精神。

huntsbot.com洞察每一个产品背后的需求与收益,从而捕获灵感

答1:

打造属于自己的副业,开启自由职业之旅,从huntsbot.com开始!

这取决于 JUnit 版本和您使用的断言库。

对于 JUnit5 和 4.13,请参阅答案 https://stackoverflow.com/a/2935935/2986984

如果您使用 assertJ 或 google-truth,请参阅答案 https://stackoverflow.com/a/41019785/2986984

JUnit <= 4.12 的原始答案是:

@Test(expected = IndexOutOfBoundsException.class)
public void testIndexOutOfBoundsException() {

    ArrayList emptyList = new ArrayList();
    Object o = emptyList.get(0);

}

虽然答案 https://stackoverflow.com/a/31826781/2986984 对于 JUnit <= 4.12 有更多选择。

参考 :

JUnit 测试-FAQ

如果您希望仅在代码中的某个地方出现异常,而不是像这样的毯子,那么这段代码将不起作用。

@skaffman 这不适用于由 org.junit.experimental.theories.Theories 运行的 org.junit.experimental.theories.Theory

Roy Osherove 在《单元测试的艺术》中不鼓励这种异常测试,因为异常可能在测试内部的任何地方,而不仅仅是在被测单元内部。

我不同意@Kiview/Roy Osherove。在我看来,测试应该是针对行为的,而不是针对实现的。通过测试特定方法是否会引发错误,您可以将测试直接与实现联系起来。我认为上面显示的方法中的测试提供了更有价值的测试。我要补充的警告是,在这种情况下,我将测试自定义异常,以便我知道我得到了我真正想要的异常。

两者都不。我想测试班级的行为。重要的是,如果我尝试检索不存在的东西,我会遇到异常。响应 get() 的数据结构是 ArrayList 的事实是无关紧要的。如果我将来选择移动到原始数组,那么我将不得不更改此测试实现。应该隐藏数据结构,以便测试可以专注于 类 的行为。

答2:

huntsbot.com精选全球7大洲远程工作机会,涵盖各领域,帮助想要远程工作的数字游民们能更精准、更高效的找到对方。

编辑: 现在 JUnit 5 和 JUnit 4.13 已经发布,最好的选择是使用 Assertions.assertThrows()(对于 JUnit 5)和 Assert.assertThrows()(对于 JUnit 4.13+)。详见my other answer。

如果您尚未迁移到 JUnit 5,但可以使用 JUnit 4.7,则可以使用 ExpectedException 规则:

public class FooTest {
  @Rule
  public final ExpectedException exception = ExpectedException.none();

  @Test
  public void doStuffThrowsIndexOutOfBoundsException() {
    Foo foo = new Foo();

    exception.expect(IndexOutOfBoundsException.class);
    foo.doStuff();
  }
}

这比 @Test(expected=IndexOutOfBoundsException.class) 好得多,因为如果在 foo.doStuff() 之前抛出 IndexOutOfBoundsException,测试将失败

有关详细信息,请参阅 this article。

@skaffman - 如果我理解正确的话,看起来 exception.expect 只在一个测试中应用,而不是整个班级。

如果我们期望抛出的异常是已检查异常,我们应该添加 throws 或 try-catch 还是以其他方式测试这种情况?

@MartinTrummer foo.doStuff() 之后不应运行任何代码,因为抛出异常并退出该方法。在预期的异常之后(除了在 finally 中关闭资源的异常)有代码是没有帮助的,因为如果抛出异常,它永远不应该被执行。

这是最好的方法。与 skaffman 的解决方案相比,这里有两个优点。首先,ExpectedException 类具有匹配异常消息的方法,甚至可以根据异常类编写自己的匹配器。其次,你可以在你期望抛出异常的代码行之前设置你的期望——这意味着如果错误的代码行抛出异常,你的测试将会失败;而 skaffman 的解决方案无法做到这一点。

@MJafarMash 如果检查了您希望抛出的异常,那么您可以将该异常添加到测试方法的 throws 子句中。每当您测试声明为抛出已检查异常的方法时,您都会执行相同的操作,即使在特定测试用例中未触发异常也是如此。

答3:

保持自己快人一步,享受全网独家提供的一站式外包任务、远程工作、创意产品订阅服务–huntsbot.com

小心使用预期异常,因为它只断言该方法抛出了该异常,而不是测试中的特定代码行。

我倾向于使用它来测试参数验证,因为这些方法通常非常简单,但更复杂的测试可能会更好地使用:

try {
    methodThatShouldThrow();
    fail( "My method didn't throw when I expected it to" );
} catch (MyException expectedException) {
}

应用判断。

huntsbot.com汇聚了国内外优秀的初创产品创意,可按收入、分类等筛选,希望这些产品与实践经验能给您带来灵感。

也许我是老派,但我仍然更喜欢这个。它还为我提供了一个测试异常本身的地方:有时我有某些值的 getter 异常,或者我可能只是在消息中查找特定值(例如,在消息“无法识别的代码 'xyz' 中查找“xyz” ”)。

我认为 NamshubWriter 的方法可以让您两全其美。

使用 ExpectedException 你可以调用 N exception.expect 每个方法来测试这样的 exception.expect(IndexOutOfBoundsException.class); foo.doStuff1(); exception.expect(IndexOutOfBoundsException.class); foo.doStuff2(); exception.expect(IndexOutOfBoundsException.class); foo.doStuff3();

@user1154664 实际上,你不能。使用 ExpectedException 您只能测试一个方法抛出异常,因为当调用该方法时,测试将停止执行,因为它抛出了预期的异常!

你的第一句话是不正确的。使用 ExpectedException 时,通常的做法是在您希望引发异常的行之前设置期望值。这样,如果前面的行抛出异常,它不会触发规则,并且测试将失败。

答4:

HuntsBot周刊–不定时分享成功产品案例,学习他们如何成功建立自己的副业–huntsbot.com

如前所述,在 JUnit 中有多种处理异常的方法。但是在 Java 8 中还有另一个:使用 Lambda 表达式。使用 Lambda 表达式,我们可以实现如下语法:

@Test
public void verifiesTypeAndMessage() {
    assertThrown(new DummyService()::someMethod)
            .isInstanceOf(RuntimeException.class)
            .hasMessage("Runtime exception occurred")
            .hasMessageStartingWith("Runtime")
            .hasMessageEndingWith("occurred")
            .hasMessageContaining("exception")
            .hasNoCause();
}

assertThrown 接受一个函数式接口,其实例可以使用 lambda 表达式、方法引用或构造函数引用创建。接受该接口的 assertThrown 将期望并准备好处理异常。

这是一种相对简单但功能强大的技术。

查看描述此技术的这篇博文:http://blog.codeleak.pl/2014/07/junit-testing-exception-with-java-8-and-lambda-expressions.html

可在此处找到源代码:https://github.com/kolorobot/unit-testing-demo/tree/master/src/test/java/com/github/kolorobot/exceptions/java8

披露:我是博客和项目的作者。

我喜欢这个解决方案,但我可以从 maven repo 下载它吗?

@Airduster 在 Maven 上可用的这个想法的一个实现是 stefanbirkner.github.io/vallado

@CristianoFontes 此 API 的更简单版本计划用于 JUnit 4.13。请参阅github.com/junit-team/junit/commit/…

@RafalBorowiec 从技术上讲,new DummyService()::someMethod 是 MethodHandle,但这种方法同样适用于 lambda 表达式。

@NamshubWriter,似乎junit 4.13被抛弃了,取而代之的是junit 5:stackoverflow.com/questions/156503/…

答5:

huntsbot.com – 程序员副业首选,一站式外包任务、远程工作、创意产品分享订阅平台。

在junit中,有四种方法可以测试异常。

junit5.x

对于junit5.x,您可以使用assertThrows,如下所示@Test public void testFooThrowsIndexOutOfBoundsException() { Throwable exception = assertThrows(IndexOutOfBoundsException.class, () -> foo.doStuff()); assertEquals(“预期消息”, exception.getMessage()); }

junit4.x

对于 junit4.x,使用 Test annonation @Test(expected = IndexOutOfBoundsException.class) public void testFooThrowsIndexOutOfBoundsException() { foo.doStuff(); }

对于 junit4.x,使用 ExpectedException 规则 public class XxxTest { @Rule public ExpectedException throw = ExpectedException.none(); @Test public void testFooThrowsIndexOutOfBoundsException() { throw.expect(IndexOutOfBoundsException.class) //可以像 throwed.expectMessage(“expected messages”); 一样测试异常消息foo.doStuff(); } }

您还可以使用在 junit 3 框架下广泛使用的经典 try/catch 方式 @Test public void testFooThrowsIndexOutOfBoundsException() { try { foo.doStuff(); fail(“预期的异常没有发生。”); } catch(IndexOutOfBoundsException e) { //如果执行到这里, //表示发生了这个异常。 //所以我们不需要处理它。 } }

因此,如果您喜欢 junit 5,那么您应该喜欢第一种方式,当您只想测试异常类型时使用第二种方式,如果您使用 junit 3,则在您想要进一步测试异常消息时使用第一种和最后两种方式,然后第4个是首选

如果你喜欢junit 5,那么你应该喜欢第一个

当您只想测试异常类型时使用第二种方法

当您想要进一步测试异常消息时使用第一个和最后两个

如果您使用 junit 3,则首选第 4 个

有关详细信息,您可以阅读此文档和 junit5 用户指南了解详细信息。

对我来说这是最好的答案,它非常清楚地涵盖了所有方式,谢谢!就我个人而言,即使使用 Junit4 以提高可读性,我也会继续使用第三个选项,为了避免空的 catch 块,您还可以捕获 Throwable 并断言 e 类型

是否可以使用 ExpectedException 来期望检查异常?

它只是前三个答案的积累。 IMO,如果没有添加任何新内容,甚至不应该发布此答案。只是为代表回答(一个流行的问题)。很没用。

当然,因为您可以将任何从 Trowable 派生的类型传递给方法 ExpectedException.expect。请参阅it's signature。 @miuser

我会指出 Junit 4.13(强调“.13”)...支持 assertThrows。我赞成,因为这是一个很好的“完整概述”答案,但我希望你会考虑 4.13 警告。

答6:

huntsbot.com精选全球7大洲远程工作机会,涵盖各领域,帮助想要远程工作的数字游民们能更精准、更高效的找到对方。

tl;博士

post-JDK8 :使用 AssertJ 或自定义 lambda 断言异常行为。

pre-JDK8:我会推荐旧的好 try-catch 块。 (不要忘记在 catch 块之前添加一个 fail() 断言)

不管是 Junit 4 还是 JUnit 5。

长篇大论

可以为自己编写一个自己动手 try-catch 块或使用 JUnit 工具(@Test(expected = …) 或 @Rule ExpectedException JUnit 规则功能)。

但是这些方法并不那么优雅,并且不能很好地将可读性与其他工具混合。此外,JUnit 工具确实存在一些缺陷。

try-catch 块您必须围绕测试的行为编写块并在 catch 块中写入断言,这可能很好,但许多人发现这种样式会中断测试的读取流程。此外,您需要在 try 块的末尾编写 Assert.fail。否则,测试可能会错过断言的一侧; PMD、findbugs 或 Sonar 会发现此类问题。 @Test(expected = …) 功能很有趣,因为您可以编写更少的代码,然后编写此测试据说不太容易出现编码错误。但这种方法在某些领域是缺乏的。如果测试需要检查异常的其他内容,例如原因或消息(好的异常消息非常重要,拥有精确的异常类型可能还不够)。此外,由于期望被放置在方法中,根据测试代码的编写方式,测试代码的错误部分可能会引发异常,导致误报测试,我不确定 PMD、findbugs 或 Sonar将提供有关此类代码的提示。 @Test(expected = WantedException.class) public void call2_should_throw_a_WantedException__not_call1() { // 初始化测试tested.call1(); // 可能会抛出 WantedException // 调用进行实际测试 test.call2(); // 应该引发异常的调用 } ExpectedException 规则也是尝试修复之前的警告,但是使用起来感觉有点别扭,因为它使用了期望样式,EasyMock 用户非常了解这种样式。这对某些人来说可能很方便,但是如果您遵循行为驱动开发 (BDD) 或安排行为断言 (AAA) 原则,ExpectedException 规则将不适合这些写作风格。除此之外,它可能会遇到与@Test 方式相同的问题,具体取决于您将期望放在哪里。 @Rule ExpectedException throw = ExpectedException.none() @Test public void call2_should_throw_a_WantedException__not_call1() { // 期望 throwed.expect(WantedException.class); throw.expectMessage(“boom”); // 初始化测试tested.call1(); // 可能会抛出 WantedException // 调用进行实际测试 test.call2(); // 应该引发异常的调用 } 即使预期的异常放在测试语句之前,如果测试遵循 BDD 或 AAA,它也会破坏您的阅读流程。另外,请参阅 ExpectedException 作者的 JUnit 上的此评论问题。 JUnit 4.13-beta-2 甚至弃用了这种机制: Pull request #1519: Deprecate ExpectedException Assert.assertThrows 方法为验证异常提供了更好的方法。此外,ExpectedException 的使用在与 TestWatcher 等其他规则一起使用时容易出错,因为在这种情况下规则的顺序很重要。

因此,上述这些选项有很多警告,显然不能避免编码错误。

在创建这个看起来很有希望的答案后,我意识到了一个项目,它是捕获异常。正如该项目的描述所说,它让编码人员用流畅的代码行编写捕获异常并为后一个断言提供此异常。您可以使用任何断言库,例如 Hamcrest 或 AssertJ。取自主页的快速示例: // 给出:一个空列表 List myList = new ArrayList(); // when: 我们尝试获取列表的第一个元素 when(myList).get(1); // then: 我们期望 IndexOutOfBoundsException then(caughtException()) .isInstanceOf(IndexOutOfBoundsException.class) .hasMessage(“Index: 1, Size: 0”) .hasNoCause();如您所见,代码非常简单,您在特定行捕获异常,然后 API 是使用 AssertJ API 的别名(类似于使用 assertThat(ex).hasNoCause()…)。在某些时候,该项目依赖于 AssertJ 的祖先 FEST-Assert。编辑:似乎该项目正在酝酿对 Java 8 Lambdas 的支持。目前,这个库有两个缺点: 在撰写本文时,值得注意的是这个库是基于 Mockito 1.x 的,因为它在后台创建了一个测试对象的模拟。由于 Mockito 仍未更新,因此该库无法与最终类或最终方法一起使用。即使它基于当前版本的 Mockito 2,这也需要声明一个全局模拟制造商(inline-mock-maker),这可能不是您想要的,因为这个模拟制造商与常规模拟有不同的缺点制作者。它需要另一个测试依赖项。一旦库支持 lambda,这些问题将不适用。但是,AssertJ 工具集将复制该功能。综合考虑如果不想使用catch-exception工具,我会推荐try-catch块的老好办法,至少到JDK7。对于 JDK 8 用户,您可能更喜欢使用 AssertJ,因为它提供的可能不仅仅是断言异常。在 JDK8 中,lambda 进入了测试场景,事实证明它们是一种断言异常行为的有趣方式。 AssertJ 已经更新,提供了一个很好的流畅的 API 来断言异常行为。和 AssertJ 的示例测试: @Test public void test_exception_approach_1() { … assertThatExceptionOfType(IOException.class) .isThrownBy(() -> someBadIOOperation()) .withMessage(“boom!”); } @Test public void test_exception_approach_2() { … assertThatThrownBy(() -> someBadIOOperation()) .isInstanceOf(Exception.class) .hasMessageContaining(“boom”); } @Test public void test_exception_approach_3() { … // 当 Throwable throw = catchThrowable(() -> someBadIOOperation()); // 然后 assertThat(throw).isInstanceOf(Exception.class) .hasMessageContaining(“boom”); } 随着对 JUnit 5 的近乎完全的重写,断言得到了一些改进,它们可能被证明是一种有趣的开箱即用的方式来正确断言异常。但实际上断言 API 还是有点差,除了 assertThrows 之外什么都没有。 @Test @DisplayName(“查看时抛出 EmptyStackException”) void throwsExceptionWhenPeeked() { Throwable t = assertThrows(EmptyStackException.class, () -> stack.peek()); Assertions.assertEquals(“…”, t.getMessage());正如您所注意到的,assertEquals 仍然返回 void,因此不允许像 AssertJ 那样链接断言。此外,如果您记得与 Matcher 或 Assert 的名称冲突,请准备好与 Assertions 遇到相同的冲突。

我想总结一下今天 (2017-03-03) AssertJ 的易用性、可发现的 API、快速的开发速度以及作为事实上的测试依赖是 JDK8 的最佳解决方案,无论测试框架(JUnit 与否)如何,以前的 JDK 都应该依赖 try-catch 块,即使它们感觉很笨重。

这个答案是从 another question 复制的,没有相同的可见性,我是同一作者。

添加 org.junit.jupiter:junit-jupiter-engine:5.0.0-RC2 依赖项(除了已经存在的 junit:junit:4.12 之外)能够使用 assertThrows 可能不是首选解决方案,但没有导致任何我的问题。

我是使用 ExpectedException 规则的粉丝,但它总是困扰着我,因为它与 AAA 冲突。你写了一篇很棒的文章来描述所有不同的方法,你肯定鼓励我尝试 AssertJ :-) 谢谢!

@PimHazebroek 谢谢。 AssertJ API 相当丰富。在我看来,JUnit 开箱即用的建议更好。

答7:

HuntsBot周刊–不定时分享成功产品案例,学习他们如何成功建立自己的副业–huntsbot.com

现在 JUnit 5 和 JUnit 4.13 已经发布,最好的选择是使用 Assertions.assertThrows()(对于 JUnit 5)和 Assert.assertThrows()(对于 JUnit 4.13)。请参阅 JUnit 5 User Guide。

下面是一个验证抛出异常并使用 Truth 对异常消息进行断言的示例:

public class FooTest {
  @Test
  public void doStuffThrowsIndexOutOfBoundsException() {
    Foo foo = new Foo();

    IndexOutOfBoundsException e = assertThrows(
        IndexOutOfBoundsException.class, foo::doStuff);

    assertThat(e).hasMessageThat().contains("woops!");
  }
}

与其他答案中的方法相比的优点是:

内置于 JUnit 如果 lambda 中的代码没有抛出异常,您会收到有用的异常消息,如果抛出不同的异常,则会收到堆栈跟踪 简洁 允许您的测试遵循 Arrange-Act-Assert 您可以准确地指出您是什么代码期望抛出异常您不需要在 throws 子句中列出预期的异常您可以使用您选择的断言框架来对捕获的异常进行断言

这种方法很干净,但我看不出这如何让我们的测试遵循“Arrange-Act-Assert”,因为我们必须将“Act”部分包装在“assertThrow”中,这是一个断言。

@Clockwork lambda 是“行为”。 Arrange-Act-Assert 的目标是使代码简洁明了(因此易于理解和维护)。正如您所说,这种方法很干净。

我仍然希望我可以在测试结束时断言抛出和异常,在“断言”部分。在这种方法中,您需要将动作包装在第一个断言中以首先捕获它。

这将需要在每个测试中使用更多代码来进行断言。那是更多的代码,而且容易出错。

答8:

huntsbot.com提供全网独家一站式外包任务、远程工作、创意产品分享与订阅服务!

怎么样:捕获一个非常普遍的异常,确保它脱离了 catch 块,然后断言异常的类是你所期望的。如果 a) 异常的类型错误(例如,如果您有一个空指针)并且 b) 从未抛出异常,则此断言将失败。

public void testFooThrowsIndexOutOfBoundsException() {
  Throwable e = null;

  try {
    foo.doStuff();
  } catch (Throwable ex) {
    e = ex;
  }

  assertTrue(e instanceof IndexOutOfBoundsException);
}

此外,当测试失败的那一天到来时,您不会在测试结果中看到什么样的 Exception ex。

这可以通过改变你最后断言的方式来改进。 assertEquals(ExpectedException.class, e.getClass()) 将在测试失败时向您显示预期值和实际值。

答9:

打造属于自己的副业,开启自由职业之旅,从huntsbot.com开始!

更新: JUnit5 对异常测试进行了改进:assertThrows。

以下示例来自:Junit 5 User Guide

@Test
void exceptionTesting() {
    IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
        throw new IllegalArgumentException("a message");
    });
    assertEquals("a message", exception.getMessage());
}

使用 JUnit 4 的原始答案。

有几种方法可以测试是否引发了异常。我还在帖子 How to write great unit tests with JUnit 中讨论了以下选项

设置 expected 参数 @Test(expected = FileNotFoundException.class)。

@Test(expected = FileNotFoundException.class) 
public void testReadFile() { 
    myClass.readFile("test.txt");
}

使用 try catch

public void testReadFile() { 
    try {
        myClass.readFile("test.txt");
        fail("Expected a FileNotFoundException to be thrown");
    } catch (FileNotFoundException e) {
        assertThat(e.getMessage(), is("The file test.txt does not exist!"));
    }
     
}

使用 ExpectedException 规则进行测试。

@Rule
public ExpectedException thrown = ExpectedException.none();

@Test
public void testReadFile() throws FileNotFoundException {
    
    thrown.expect(FileNotFoundException.class);
    thrown.expectMessage(startsWith("The file test.txt"));
    myClass.readFile("test.txt");
}

您可以在 JUnit4 wiki for Exception testing 和 bad.robot - Expecting Exceptions JUnit Rule 中阅读有关异常测试的更多信息。

答10:

一个优秀的自由职业者,应该有对需求敏感和精准需求捕获的能力,而huntsbot.com提供了这个机会

使用可与 JUnit 一起使用的 AssertJ 断言:

import static org.assertj.core.api.Assertions.*;

@Test
public void testFooThrowsIndexOutOfBoundsException() {
  Foo foo = new Foo();

  assertThatThrownBy(() -> foo.doStuff())
        .isInstanceOf(IndexOutOfBoundsException.class);
}

它比 @Test(expected=IndexOutOfBoundsException.class) 更好,因为它保证了测试中的预期行引发了异常,并让您可以更轻松地检查有关异常的更多详细信息,例如消息:

assertThatThrownBy(() ->
       {
         throw new Exception("boom!");
       })
    .isInstanceOf(Exception.class)
    .hasMessageContaining("boom");

Maven/Gradle instructions here.

最简洁的方式,没有人欣赏它,奇怪..我对 assertJ 库只有一个问题,assertThat 与junit的名称冲突。有关 assertJ throwby 的更多信息:JUnit: Testing Exceptions with Java 8 and AssertJ 3.0.0 ~ Codeleak.pl

@ycomp好吧,这是一个非常古老的问题的新答案,因此分数差异具有欺骗性。

如果可以使用 Java 8 和 AssertJ,这可能是最好的解决方案!

@ycomp 我怀疑这种名称冲突可能是设计使然:因此,AssertJ 库强烈建议您永远不要使用 JUnit assertThat,始终使用 AssertJ。 JUnit 方法也只返回一个“常规”类型,而 AssertJ 方法返回一个 AbstractAssert 子类......允许如上所述的方法字符串(或任何技术术语......)。

@weston 实际上我刚刚在 AssertJ 2.0.0 中使用了您的技术。毫无疑问,没有理由不升级,但尽管您可能想知道。

答11:

huntsbot.com提供全网独家一站式外包任务、远程工作、创意产品分享与订阅服务!

BDD 风格解决方案:JUnit 4 + Catch Exception + AssertJ

import static com.googlecode.catchexception.apis.BDDCatchException.*;

@Test
public void testFooThrowsIndexOutOfBoundsException() {

    when(() -> foo.doStuff());

    then(caughtException()).isInstanceOf(IndexOutOfBoundsException.class);

}

依赖项

eu.codearte.catch-exception:catch-exception:2.0

原文链接:https://www.huntsbot.com/qa/nbEm/how-do-you-assert-that-a-certain-exception-is-thrown-in-junit-tests?lang=zh_CN&from=csdn

huntsbot.com洞察每一个产品背后的需求与收益,从而捕获灵感

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值