0

我正在考虑如何为我的项目编写测试。目前,测试结构是这样的:

RealClass 
{ 
      method1; 
      method2; 
      ...
}

和完全相同的测试类结构:

TestClass {
   testMethod1; 
   testMethod2; 
   ...
 }

但是,我不喜欢它,因为我在一种测试方法中放置了太多测试用例......

也许我应该使用这样的结构:

TestClass {
   testMethod1Opt1; 
   testMethod1Opt2; 
   ... 
   testMethod2Opt1; 
   ...}

你是如何编写单元测试的?

我的测试代码示例:(非常简单的测试)

public void testIsAppUser() {
    // My (Artem`s Zinnatullin) uId
    final long artemZinnatullinUId = 172672179;

    try {
        assertTrue(usersApi.isAppUser(artemZinnatullinUId));
    } catch (Exception e) {
        fail(e.getMessage());
    }

    // Pavel`s Durov uId
    final long durovUId = 1;

    try {
        assertFalse(usersApi.isAppUser(durovUId));
    } catch (Exception e) {
        fail(e.getMessage());
    }

    // By default uId == current user`s (who has authorized) uId 
    try {
        assertTrue(usersApi.isAppUser(null));
    } catch (Exception e) {
        fail(e.getMessage());
    }
}

我在想什么:

public void testIsAppUser1() {
    // My (Artem`s Zinnatullin) uId
    final long artemZinnatullinUId = 172672179;

    try {
        assertTrue(usersApi.isAppUser(artemZinnatullinUId));
    } catch (Exception e) {
        fail(e.getMessage());
    }
}

public void testIsAppUser2() {
    // Pavel`s Durov uId
    final long durovUId = 1;

    try {
        assertFalse(usersApi.isAppUser(durovUId));
    } catch (Exception e) {
        fail(e.getMessage());
    }
}

public void testIsAppUser3() {
    // By default uId == current user`s (who has authorized) uId
    try {
        assertTrue(usersApi.isAppUser(null));
    } catch (Exception e) {
        fail(e.getMessage());
    }
}

请给我建议。

4

4 回答 4

2

评论:

  1. 而不是try{} catch(){ fail() }仅仅添加throws Exception到测试方法中。JUnit 将自动为您测试失败保留堆栈跟踪。这将使错误修复变得更加容易。

  2. 创建小型测试方法。这就产生了一个名字问题:如何想出很多好名字?这里的解决方案是以逻辑测试的内容命名测试,而不是它调用的方法。

    如果您想查看调用了哪些方法,请使用JaCoCo 之类的代码覆盖工具。

    所以应该调用第一个测试testIsArtemsZinnatullinAppUser()。作为指导:每当您觉得需要注释来解释测试的作用时,测试名称是错误的。使用您在评论中写的任何内容来创建测试名称。

您应该进行较小的测试的原因是 JUnit 会因第一个问题而停止。因此,如果您在一个测试用例中有 20 个测试,而第 3 个测试失败,则 17 个测试将无法运行。但是这 17 项测试可能包含有价值的信息,有助于找出问题所在。

如果他们都成功了,那么这可能是一个特定的问题。如果许多测试都失败了,那么问题一定出在共享代码中。

于 2013-01-23T17:32:05.187 回答
1

仅当您遇到由于“异常”而可能发生的错误时才抛出,因为您可以抛出。以下假设您正在创建自己的测试环境。

我不知道您的断言方法是什么样的,但实际上它们应该是抛出的方法。您也不需要 try catch 来引发异常,您可以执行以下操作:

throw new Exception("msg"); // Or another type of Exception

所以实现:

public void AssertEqual(Object obj1, Object obj2) throws Exception
{
    if (!obj1.equals(obj2))
        throw new Exception("Objects are not equal");
}
于 2013-01-23T17:25:12.297 回答
1

您构建测试的第二种方式要好得多。这样,每种测试方法都涵盖了方法中断的不同方式,因此您不会出现修复方法中的一个错误,然后让测试失败的情况更进一步(这样一个错误会阻止您看到)其他)。与测试方法映射到被测对象的方法相比,测试方法要小且重点突出要重要得多。

另外,不要捕获异常,JUnit 会为您完成。添加throws Exception到每个测试方法的签名中。如果你想检查一个异常是否真的被抛出,那么你可以在测试中捕获它,比如:

try {
    objectUnderTest.doSomethingThatShouldThrowFooException();
    fail("should've thrown an exception before getting here");
}
catch (FooException fooEx) {
    // yay. my test passed
}

,但样板:

} catch (Exception e) {
    fail(e.getMessage());
}

是不必要的。

于 2013-01-23T17:30:17.820 回答
1

我不会重复其他回复中的内容。但只需添加以下内容:

  • 避免在测试类中重复代码构造。
  • 不要犹豫写明确的失败消息。

这里有一些东西可以说明我的意思:

public void testIsAppUser1() {
    // My (Artem`s Zinnatullin) uId
    assertAppUser(172672179,true,"artemZinnatullinUId");
}

public void testIsAppUser2() {
    // Pavel`s Durov uId
    assertAppUser(1,false,"Pavel`s Durov");
}

public void testIsAppUser3() {
    // By default uId == current user`s (who has authorized) uId
    assertAppUser(null,true,"current user");
}

private void assertAppUser(Long id, boolean isExpectedAppUser, String userName){
    boolean isAppUser = usersApi.isAppUser(id);
    if(isExpectedAppUser){
        assertTrue("User with id:"+id+"and named:"+userName+" must be an appUser" ,isAppUser);
    }else{
        assertFalse("User with id:"+id+"and named:"+userName+" cannot be an appUser" ,isAppUser);
    }
}
}
于 2013-01-23T17:46:05.950 回答