JUnit vs Mockito

JUnit vs Mockito

referenceMOCKITO: What is it and how is it different from Junit

authorGhostCat

JUnit is a framework that helps with writing and running your unit tests.

JUnit是一个帮助编写和运行单元测试的框架。

Mockito (or any other mocking tool) is a framework that you specifically use to efficiently write certain kind of tests.

Mockito(或任何其他模拟工具)是一个专门用于有效编写特定类型测试的框架。

One core aspect in unit testing is the fact that you want to isolate your “class under test” from anything else in the world. In order to do that, you very often have to create “test doubles” that you provide to an object of your “class under test”. You could create all those “test doubles” manually; or you use a mocking framework that generates object of a certain class for you using reflection techniques. Interestingly enough, some people advocate to never use mocking frameworks; but honestly: I can’t imagine doing that.

单元测试的一个核心方面是,您希望将您的“被测类”与世界上的任何其他东西隔离开来。为了做到这一点,您经常需要创建“Test doubles”,并提供给“被测类”的对象。您可以手动创建所有这些“Test doubles”;或者使用模拟框架的反射技术为您生成特定类的对象。有趣的是,有些人主张永远不要使用mocking框架;但老实说:我无法想象这样做。

关于“Test double”: In automated unit testing, it may be necessary to use objects or procedures that look and behave like their release-intended counterparts, but are actually simplified versions that reduce the complexity and facilitate testing. A test double is a generic (meta) term used for these objects or procedures.

在自动化单元测试中,可能有必要使用外观和行为与预期版本相似的对象或过程,但实际上是简化版本,可以降低复杂性并方便测试。双重测试是用于这些对象或过程的通用(meta)术语。

In other words: you can definitely use JUnit without using a mocking framework. Same is true for the reverse direction; but in reality, there are not many good reasons why you would want to use Mockito for anything else but unit testing.

换言之:您完全可以使用JUnit而不使用mocking框架。反过来也是一样;但实际上,除了单元测试之外,没有多少好理由可以让您使用Mockito来做其他事情。

Remark

  • I am one of those that advocate to avoid using mocking libraries (even though I develop one!). After many years of experience, I realized that it usually leads to bad tests. People have a strong tendency to overuse/abuse/misuse the mocking tool. Instead, I prefer integration tests. Interestingly, well-known “gurus” (including the “creator” of TDD) also don’t like mocking; they write non-isolated unit tests - for reference, see this article. – Rogério Aug 3 '16 at 21:07

    我是提倡避免使用模拟库的人之一(即使我开发了一个)。经过多年的经验,我意识到这通常会导致糟糕的测试。人们有过度使用/滥用/误用mocking工具的强烈倾向。相反,我更喜欢集成测试。有趣的是,著名的“专家”(包括TDD的“创建者”)也不喜欢嘲笑;他们编写非独立的单元测试——参考本文。

  • @Rogério I think I am somehow on middle ground here. I absolutely prefer unit tests (in the more narrow definition) that do not require mocking. Because the test is just much more elegant; and you know, specifying the behavior of a mock - that is about testing the internal implementation of your class under test. But: a lot of the core classes/objects in our product only work reasonably when our whole product runs. If I need to use something from there, I have two choices: use mocking; or go for some “functional test” that takes me 10 minutes to prepare and that is hard to execute … – GhostCat Aug 4 '16 at 3:21

    我想我在这里有点中庸之道。我绝对喜欢不需要mocking的单元测试(在更狭义的定义中)。因为这样的测试要优雅得多;而且你知道,指定模拟的行为,也就是测试被测类的内部实现。但是:我们产品中的许多核心类/对象只有在整个产品运行时才能正常工作。如果我需要从那里使用一些东西,我有两个选择:使用mocking;或者进行一些“功能测试”,它需要我10分钟来准备,而且很难执行

  • @Rogério and that is almost impossible to automate, because we don’t have good means for automation of functional tests. So, you do the math; which of the two options gives me quicker feedback?! – GhostCat Aug 4 '16 at 3:22

    这几乎不可能实现自动化,因为我们没有很好的方法来实现功能测试的自动化。所以,你来算算,这两个选项中哪一个能给我更快的反馈?!

  • Oh, I completely agree that functional tests are not practical. I tried them both in C#.NET (using the WatiN tool) and in Java (using WebDriver). In both cases, it was a pain, particularly to run the tests. Instead, I write out-of-container integration tests which are fast and stable enough to not discourage the developer from running them at any time. – Rogério Aug 4 '16 at 14:57

    哦,我完全同意功能测试是不实际的。我在C#.NET(使用WatiN工具)和Java(使用WebDriver)中尝试了它们。在这两种情况下,这都是一种痛苦,尤其是运行测试。相反,我编写了容器外集成测试,这些测试足够快速和稳定,不会阻碍开发人员在任何时候运行它们。

authorAlexandru Marina

JUnit is the Java library used to write tests (offers support for running tests and different extra helpers - like setup and teardown methods, test sets etc.). Mockito is a library that enables writing tests using the mocking approach.

See here a nice article on the mocking vs non mocking tests: http://martinfowler.com/articles/mocksArentStubs.html

setup and teardown methods

Prepare initial state before tests run, and perform cleanup after tests complete.

referencesetUp/tearDown (@Before/@After) why we need them in JUnit?

Junit

How to write great tests?

reference:[New to unit testing, how to write great tests? [closed\]](https://stackoverflow.com/questions/3258733/new-to-unit-testing-how-to-write-great-tests)

authorMark Byers

A unit test should:

  • test one method
  • provide some specific arguments to that method
  • test that the result is as expected

It should not look inside the method to see what it is doing, so changing the internals should not cause the test to fail. You should not directly test that private methods are being called. If you are interested in finding out whether your private code is being tested then use a code coverage tool. But don’t get obsessed by this: 100% coverage is not a requirement.

If your method calls public methods in other classes, and these calls are guaranteed by your interface, then you can test that these calls are being made by using a mocking framework.

You should not use the method itself (or any of the internal code it uses) to generate the expected result dynamically. The expected result should be hard-coded into your test case so that it does not change when the implementation changes. Here’s a simplified example of what a unit test should do:

testAdd()
{
    int x = 5;
    int y = -2;
    int expectedResult = 3;
    Calculator calculator = new Calculator();
    int actualResult = calculator.Add(x, y);
    Assert.AreEqual(expectedResult, actualResult);
}

注意,不检查结果是如何计算的—只检查结果是正确的。不断添加越来越多的像上面那样的简单测试用例,直到您已经覆盖了尽可能多的场景。使用代码覆盖工具查看是否错过了任何有趣的路径。

Junit4参考手册

reference:https://wiki.jikexueyuan.com/project/junit/

1 hello world

功能类:

public class MessageUtil {
    private String message;
    public MessageUtil(String message){
        this.message=message;
    }

    public String printMessage(){
        System.out.println(message);
        return message;
    }
}

测试类:

public class MessageUtilTest {
    String message="Hello world";
    MessageUtil messageUtil=new MessageUtil(message);
    
    @Test
    public void printMessage() {
        assertEquals(message,messageUtil.printMessage());
    }
}

2 API

Assert类

这个类提供了一系列的编写测试的有用的声明方法。只有失败的声明方法才会被记录。Assert 类的重要方法列式如下,全部参考org.junit.Assert类。

序号方法和描述
1void assertEquals(Object expected, Object actual) 检查两个变量或者等式是否平衡
2void assertTrue(boolean condition) 检查条件为真
3void assertFalse(boolean condition) 检查条件为假
4void assertNotNull(Object object) 检查对象不为空
5void assertNull(Object object) 检查对象为空
6void assertSame(Object expected, Object actual) assertSame() 方法检查两个相关对象是否指向同一个对象
7void assertNotSame(Object unexpected, Object actual) assertNotSame() 方法检查两个相关对象是否不指向同一个对象
8void assertArrayEquals(expectedArray, resultArray) assertArrayEquals() 方法检查两个数组是否相等

What’s the actual use of ‘fail’ in JUnit test case?

authorsleske

Some cases where I have found it useful:

  • mark a test that is incomplete, so it fails and warns you until you can finish it

  • making sure an exception is thrown:

    try{
      // do stuff...
      fail("Exception not thrown");
    }catch(Exception e){
      assertTrue(e.hasSomeFlag());
    }
    

Note:

Since JUnit4, there is a more elegant way to test that an exception is being thrown: Use the annotation @Test(expected=IndexOutOfBoundsException.class)

However, this won’t work if you also want to inspect the exception, then you still need fail().

remark

3 编写测试

@Getter
@Setter
public class EmployeeDetails {
    private String name;
    private double monthlySalary;
    private int age;
}
public class EmpBusinessLogic {
    // Calculate the yearly salary of employee
    public double calculateYearlySalary(EmployeeDetails employeeDetails){
        double yearlySalary=0;
        yearlySalary = employeeDetails.getMonthlySalary() * 12;
        return yearlySalary;
    }

    // Calculate the appraisal amount of employee
    public double calculateAppraisal(EmployeeDetails employeeDetails){
        double appraisal=0;
        if(employeeDetails.getMonthlySalary() < 10000){
            appraisal = 500;
        }else{
            appraisal = 1000;
        }
        return appraisal;
    }
}
public class EmpBusinessLogicTest {

    EmpBusinessLogic empBusinessLogic =new EmpBusinessLogic();
    EmployeeDetails employee = new EmployeeDetails();

    @Test
    public void calculateYearlySalary() {
        employee.setName("Rajeev");
        employee.setAge(25);
        employee.setMonthlySalary(8000);
        double appraisal= empBusinessLogic.calculateAppraisal(employee);
        assertEquals(500, appraisal, 0.0);
    }

    @Test
    public void calculateAppraisal() {
        employee.setName("Rajeev");
        employee.setAge(25);
        employee.setMonthlySalary(8000);
        double salary= empBusinessLogic.calculateYearlySalary(employee);
        assertEquals(96000, salary, 0.0);
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZGGDAGvI-1599300583229)(https://raw.githubusercontent.com/xiaobanni/mypicbed/master/img/20200905180849.png)]

4 注释及其执行过程

JUnit 中的注释的列表以及他们的含义:

序号注释和描述
1@Test 这个注释说明依附在 JUnit 的 public void 方法可以作为一个测试案例。
2@Before 有些测试在运行前需要创造几个相似的对象。在 public void 方法加该注释是因为该方法需要在 test 方法前运行。
3@After 如果你将外部资源在 Before 方法中分配,那么你需要在测试运行后释放他们。在 public void 方法加该注释是因为该方法需要在 test 方法后运行。
4@BeforeClass 在 public void 方法加该注释是因为该方法需要在类中所有方法前运行。
5@AfterClass 它将会使方法在所有测试结束后执行。这个可以用来进行清理活动。
6@Ignore 这个注释是用来忽略有关不需要执行的测试的。
public class JunitAnnotation {
    //execute only once, in the starting
    @BeforeClass
    public static void beforeClass() {
        System.out.println("in before class");
    }

    //execute only once, in the end
    @AfterClass
    public static void  afterClass() {
        System.out.println("in after class");
    }

    //execute for each test, before executing test
    @Before
    public void before() {
        System.out.println("in before");
    }

    //execute for each test, after executing test
    @After
    public void after() {
        System.out.println("in after");
    }

    //test case 1
    @Test
    public void testCase1() {
        System.out.println("in test case 1");
    }

    //test case 2
    @Test
    public void testCase2() {
        System.out.println("in test case 2");
    }
}
  • beforeClass() 方法首先执行,并且只执行一次。
  • afterClass() 方法最后执行,并且只执行一次。
  • before() 方法针对每一个测试用例执行,但是是在执行测试用例之前。
  • after() 方法针对每一个测试用例执行,但是是在执行测试用例之后。
  • 在 before() 方法和 after() 方法之间,执行每一个测试用例。
  • testCase1和testCase2的执行顺序不确定

image-20200903211541037image-20200903211607342

5 异常测试

Junit 用代码处理提供了一个追踪异常的选项。你可以测试代码是否它抛出了想要得到的异常。expected 参数和 @Test 注释一起使用。现在让我们看看活动中的 @Test(expected)

public class MessageUtil {

    private String message;
    
    public MessageUtil(String message){
        this.message = message;
    }
    
    public void printMessage(){
        System.out.println(message);
        int a =0;
        int b = 1/a;
    }
}
public class MessageUtilTest {

    String message = "Robert";
    MessageUtil messageUtil = new MessageUtil(message);

    @Test(expected = ArithmeticException.class)
    public void testPrintMessage() {
        System.out.println("Inside testPrintMessage()");
        messageUtil.printMessage();
    }
}

Mockito

5分钟了解Mockito

reference5分钟了解Mockito

一、什么是mock测试,什么是mock对象?

先来看看下面这个示例:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KTUor5WK-1599300583231)(https://raw.githubusercontent.com/xiaobanni/mypicbed/master/img/20200904100236.png)]

从上图可以看出如果我们要对A进行测试,那么就要先把整个依赖树构建出来,也就是BCDE的实例。

一种替代方案就是使用mocks

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eEWdbzw4-1599300583233)(https://raw.githubusercontent.com/xiaobanni/mypicbed/master/img/20200905180820.png)]

mock对象就是在调试期间用来作为真实对象的替代品。

mock测试就是在测试过程中,对那些不容易构建的对象用一个虚拟对象来代替测试的方法就叫mock测试。

知道什么是mock测试后,那么我们就来认识一下mock框架—Mockito

二、什么是Mockito

除了有一个好记的名字外,Mockito尝试用不一样的方法做mocking测试,是能够替代EasyMock的简单轻量级的框架。使用简单,测试代码可读性高,丰富的文档包含在javadoc中,直接在IDE中可查看文档,实例,说明。更多信息:https://github.com/mockito/mockito

三、Helloword

public class SimpleTest {
    @Test
    public void simpleTest(){
        //Create a mock object, the parameter can be a class or an interface
        List<String> list=mock(List.class);
        //Set the expected return value of the method
        when(list.get(0)).thenReturn("hello,world!");
        //Call mock object method
        String result=list.get(0);
        //Verify method call (whether get(0) is called)
        verify(list).get(0);
        //Junit test
        Assert.assertEquals("hello,world!",result);
    }
}

官方使用手册

reference:https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html

1.Let’s verify some behaviour!

The following examples mock a List, because most people are familiar with the interface (such as the add(), get(), clear() methods).
In reality, please don’t mock the List class. Use a real instance instead.

//Let's import Mockito statically so that the code looks clearer
import static org.mockito.Mockito.*;

//mock creation
List mockedList = mock(List.class);

//using mock object
mockedList.add("one");
mockedList.clear();

//verification
verify(mockedList).add("one");
verify(mockedList).clear();

Once created, a mock will remember all interactions. Then you can selectively verify whatever interactions you are interested in.

2. How about some stubbing?

referenceWhat does stubbing mean in programming? (translate:打桩)

askWazery

I often hear the term “stub”, “stub something out”, “stubs”, and so forth.

What does stubbing mean in programming, and where does the word come from? In what contexts can it be used?

authorratchet freak

A stub method is a method that just returns a simple but valid (though not necessarily correct) result.

They are typically made when building the infrastructure(基础架构) and you don’t want to spend time on every method needed right now. Instead you create stubs so everything compiles and your IDE’s auto-complete knows about the methods you are planning to use.

Another place where stubs are used is in mocking when testing, You supply stub methods instead of the normal dependency through dependency injection which return fixed results and then ensure that the code does the right thing with them. This isolates testing to the code you are trying to test and means you don’t need to spin up a database just to run those tests.

remark

//You can mock concrete classes, not just interfaces
LinkedList mockedList = mock(LinkedList.class);

//stubbing
when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(1)).thenThrow(new RuntimeException());

//following prints "first"
System.out.println(mockedList.get(0));

//following throws runtime exception
System.out.println(mockedList.get(1));

//following prints "null" because get(999) was not stubbed
System.out.println(mockedList.get(999));

//Although it is possible to verify a stubbed invocation, usually it's just redundant
//If your code cares what get(0) returns, then something else breaks (often even before verify() gets executed).
//If your code doesn't care what get(0) returns, then it should not be stubbed.
verify(mockedList).get(0);
  • By default, for all methods that return a value, a mock will return either null, a primitive/primitive wrapper value, or an empty collection, as appropriate. For example 0 for an int/Integer and false for a boolean/Boolean.

  • Stubbing can be overridden: for example common stubbing can go to fixture setup but the test methods can override it. Please note that overridding stubbing is a potential code smell that points out too much stubbing

    Fixture setup tests are ordinary tests with all of the usual test functionality.

  • Once stubbed, the method will always return a stubbed value, regardless of how many times it is called.

  • Last stubbing is more important - when you stubbed the same method with the same arguments many times. Other words: the order of stubbing matters but it is only meaningful rarely, e.g. when stubbing exactly the same method calls or sometimes when argument matchers are used, etc.

3. Argument matchers

4. Verifying exact number of invocations / at least x / never

5. Stubbing void methods with exceptions

doThrow(new RuntimeException()).when(mockedList).clear();

//following throws RuntimeException:
mockedList.clear();

Read more about doThrow()|doAnswer() family of methods in section 12.

6. Verification in order

7. Making sure interaction(s) never happened on mock

8. Finding redundant invocations

9. Shorthand for mocks creation - @Mock annotation

  • Minimizes repetitive mock creation code.
  • Makes the test class more readable.
  • Makes the verification error easier to read because the field name is used to identify the mock.
public class ArticleManagerTest {

    @Mock private ArticleCalculator calculator;
    @Mock private ArticleDatabase database;
    @Mock private UserProvider userProvider;

    private ArticleManager manager;

    @org.junit.jupiter.api.Test
    void testSomethingInJunit5(@Mock ArticleDatabase database) {

Important! This needs to be somewhere in the base class or a test runner:

MockitoAnnotations.openMocks(testClass);

You can use built-in runner: MockitoJUnitRunner or a rule: MockitoRule. For JUnit5 tests, refer to the JUnit5 extension described in section 45.

use built-in runner

@RunWith(MockitoJUnitRunner.StrictStubs.class)
 public class ExampleTest {

     @Mock
     private List list;

     @Test
     public void shouldDoSomething() {
         list.add(100);
     }
 }

Read more here: MockitoAnnotations

10. Stubbing consecutive calls (iterator-style stubbing)

11. Stubbing with callbacks

12. doReturn()|doThrow()| doAnswer()|doNothing()|doCallRealMethod() family of methods

13. Spying on real objects

You can create spies of real objects. When you use the spy then the real methods are called (unless a method was stubbed).

Real spies should be used carefully and occasionally, for example when dealing with legacy code.

Spying on real objects can be associated with “partial mocking” concept. Before the release 1.8, Mockito spies were not real partial mocks. The reason was we thought partial mock is a code smell. At some point we found legitimate use cases for partial mocks (3rd party interfaces, interim refactoring of legacy code).

List list = new LinkedList();
List spy = spy(list);

//optionally, you can stub out some methods:
when(spy.size()).thenReturn(100);

//using the spy calls *real* methods
spy.add("one");
spy.add("two");

//prints "one" - the first element of a list
System.out.println(spy.get(0));

//size() method was stubbed - 100 is printed
System.out.println(spy.size());

//optionally, you can verify
verify(spy).add("one");
verify(spy).add("two");
Important gotcha on spying real objects!

Sometimes it’s impossible or impractical to use when(Object) for stubbing spies. Therefore when using spies please consider doReturn|Answer|Throw()family of methods for stubbing. Example:

List list = new LinkedList();
List spy = spy(list);

//Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)
when(spy.get(0)).thenReturn("foo");

//You have to use doReturn() for stubbing
doReturn("foo").when(spy).get(0);

referenceMocking vs. Spying in mocking frameworks

askVivin Paliath

In mocking frameworks, you can mock an object or spy on it. What’s the difference between the two and when would/should I use one over the other?

Looking at Mockito, for example, I see similar things being done using spies and mocks, but I am unsure as to the distinction between the two.

authorTomasz Nurkiewicz

Mock object replace mocked class entirely, returning recorded or default values. You can create mock out of “thin air”(您可以凭空创建模拟). This is what is mostly used during unit testing.

When spying, you take an existing object and “replace” only some methods. This is useful when you have a huge class and only want to mock certain methods (partial mocking).

for moreDifference between Spy and Mock in Mockito

14. Changing default return values of unstubbed invocations (Since 1.7)

15. Capturing arguments for further assertions (Since 1.8.0)

16. Real partial mocks (Since 1.8.0)

Finally, after many internal debates & discussions on the mailing list, partial mock support was added to Mockito. Previously we considered partial mocks as code smells. However, we found a legitimate use case for partial mocks.

Before release 1.8 spy() was not producing real partial mocks and it was confusing for some users. Read more about spying: here or in javadoc for spy(Object) method.

//you can create partial mock with spy() method:
List list = spy(new LinkedList());

//you can enable partial mock capabilities selectively on mocks:
Foo mock = mock(Foo.class);
//Be sure the real implementation is 'safe'.
//If real implementation throws exceptions or depends on specific state of the object then you're in trouble.
when(mock.someMethod()).thenCallRealMethod();

As usual you are going to read the partial mock warning: Object oriented programming is more less tackling complexity by dividing the complexity into separate, specific, SRPy objects. How does partial mock fit into this paradigm? Well, it just doesn’t… Partial mock usually means that the complexity has been moved to a different method on the same object. In most cases, this is not the way you want to design your application.

However, there are rare cases when partial mocks come handy: dealing with code you cannot change easily (3rd party interfaces, interim refactoring of legacy code etc.) However, I wouldn’t use partial mocks for new, test-driven & well-designed code.

Mockito官方团队不推荐使用部分模拟,当使用部分模拟时,就意味着缺乏合理的设计。

17. Resetting mocks (Since 1.8.0)

18. Troubleshooting & validating framework usage (Since 1.8.0)

19. Aliases for behavior driven development (Since 1.8.0)

20. Serializable mocks (Since 1.8.1)

21. New annotations: @Captor, @Spy, @InjectMocks (Since 1.8.3)

Release 1.8.3 brings new annotations that may be helpful on occasion:

  • @Captor simplifies creation of ArgumentCaptor - useful when the argument to capture is a nasty generic class and you want to avoid compiler warnings
  • @Spy - you can use it instead spy(Object).
  • @InjectMocks - injects mock or spy fields into tested object automatically.

Note that @InjectMocks can also be used in combination with the @Spy annotation, it means that Mockito will inject mocks into the partial mock under test. This complexity is another good reason why you should only use partial mocks as a last resort. See point 16 about partial mocks.

All new annotations are *only* processed on MockitoAnnotations.openMocks(Object). Just like for @Mock annotation you can use the built-in runner: MockitoJUnitRunner or rule: MockitoRule.

22. Verification with timeout (Since 1.8.5)

23. Automatic instantiation of @Spies, @InjectMocks and constructor injection goodness (Since 1.9.0)

Mockito will now try to instantiate @Spy and will instantiate @InjectMocks fields using constructor injection, setter injection, or field injection.

To take advantage of this feature you need to use MockitoAnnotations.openMocks(Object), MockitoJUnitRunner or MockitoRule.

Read more about available tricks and the rules of injection in the javadoc for InjectMocks

//instead:
@Spy BeerDrinker drinker = new BeerDrinker();
//you can write:
@Spy BeerDrinker drinker;

//same applies to @InjectMocks annotation:
@InjectMocks LocalPub;

在蚂蚁金服实习期间编写测试用例学习总结

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值