Unit Test and Integration Test

Background

It is the first time that I try to write an article in English.

In the past, I didn’t write test code. Just thinking QA is responsible for testing.
As a developer, I don’t need to care about tests.
Although I know tests are essential, I can’t be aware of their importance.

After I joined my current team, it is required for developers to write tests.
So I have to write tests code.

After 8 months, I realize that tests are critical. I need to enforce my ability of testing.

therefore, I recently read a book Vladimir Khorikov - Unit Testing Principles Practices and Patterns.
Learned a lot of unit test and integration best practice and code design.

So I want to share some knowledge with you.

Test

There are two kinds of tests.

  • Unit Test
  • Integration Test
  • end to end test
  • Test Coverage

    Do we need a 100% test coverage?

    No. We don’t.

    For trivial code, we can ignore that. Because they aren’t worth it.
    We should focus on our business logic.

    If a metric shows that there’s too little coverage in your code base—say, only 10%—
    that’s a good indication that you are not testing enough.

    But the reverse isn’t true:
    even 100% coverage isn’t a guarantee that you have a good-quality test suite. A test
    suite that provides high coverage can still be of poor quality.

    Unit Test

    The goal of unit test is to enable sustainable project growth.

    Just as all tests are not created equal, not all parts of your code base are worth the
    same attention in terms of unit testing.

    Unit test should test a unit of behavior rather than code.

    The four pillars of a good unit test

  • Protection against regressions
  • Resistance to refactoring
  • Fast feedback
  • Maintainability
  • Protection against regressions

    The more features you develop, the more chances there are that you’ll break one of those features with a new release.

    Sometimes We don’t awake that the new release will break one existing feature. Even with QA regression test, it also happens many times.

    If we have a good automation test, can reduce these situations.

    So good tests should protect against regressions.

    To maximize the metric of protection against regressions, the test needs to aim at exercising as much code as possible.

    Resistance to refactoring

    Resistance to refactoring — the degree to which a test can sustain a refactoring of the underlying application code without turning red (failing).

    This attribute can give us confidence to refactor.

    How can we do?

    We shouldn’t test the details of code. We should test the observable behavior.
    Aim at the end result instead of implementation details

    There is a example:

    # we shouldn't care what's detail that we get from the data
    test "get user by id" do
      assert "select * from users where id = 1" == User.get(1).to_sql
    end
    
    # we just need to verify the data
    test "get user by id" do
      assert User.get(1).name == "Steven"
      assert User.get(1).id == 1
    end
    

    Fast feedback

    Fast feedback brings a excellent experience.
    Picture the situation, we run a test, it takes 1 minute to show you result.
    I can’t stand it.

    Our project has hundreds of thousands of code. Run slowly and waste my time to wait for result.

    How to run faster?

    With less communications of out of process.

    Async and parallel execution.

    Maintainability

    Using plain English as test titles.

    Simple phrases in plain English do a much better job: they are more expressive
    and don’t box you in a rigid naming structure. With simple phrases, you can describe
    the system behavior in a way that’s meaningful to a customer or a domain expert.

    how to write code to easy test

    Split business logic and out of process communications (side effects).

    def check do
      if User.admin? do
        :error
      else
        :ok  
      end
    end
    
    
    def check_with_side_effect do
      if User.admin? do
        # side effect
        AuditLog.record()
        :error
      else
        :ok  
      end
    end
    

    With side effects, it is hard to compose code and hard to test.
    Pure function is easiest to test.

    Integration Test

    Using integration tests to verify the behavior of the system as a whole.

    Mock

    Integration test verifies database, third-party api, mq and so on.

    All out-of-process dependencies fall into two categories:

  • Managed dependencies (out-of-process dependencies you have full control over)—These
    dependencies are only accessible through your application; interactions with
    them aren’t visible to the external world. A typical example is a database. External systems normally don’t access your database directly; they do that through
    the API your application provides.
  • Unmanaged dependencies (out-of-process dependencies you don’t have full control over)—
    Interactions with such dependencies are observable externally. Examples include
    an SMTP server and a message bus: both produce side effects visible to other
    applications.
  • For those out-of-process dependencies, we should mock unmanaged dependencies.

    Roles of Test

  • Trivial code: we shouldn’t test, it isn’t worth it.
  • Domain model , algorithms: Unit test carefully test
  • Controllers: Integration test, but doesn’t need to test all situation. Happy path and edge cases are enough.
  • Overcomplicated code: We should reduce these code. Split it.
  • Test Pyramid

    Recap

    It simply summary Unit Testing.
    Talk is cheap, we should write more code.

    物联沃分享整理
    物联沃-IOTWORD物联网 » 单元测试与集成测试

    发表评论