Junit-In-Action

Part 1. Junit

1. JUnit jump-start

1.1 Proving that a program works

Framework Definition

Unit Test Difinition

API Contract Definition

The notion of an API contract arises from the practice of Design by Contract,popularized by the Eiffel programming language (http://archive.eiffel.com/doc/manuals/technology/contract).

1.2.1 Understanding unit testing frameworks

Unit testing has several best practices that frameworks should follow. Three rules that (in my experience) all unit testing frameworks should follow:

  1. Each unit test should run independently of all other unit tests.
  2. The framework should detect and report errors test by test.
  3. It should be easy to define which unit tests will run.

1.4 Testing with JUnit

JUnit annotations to provide resource initialization and cleanup methods:
@BeforeEach, @BeforeAll, @AfterEach, and @AfterAll (starting from version 5);
and @Before, @BeforeClass, @After, and @AfterClass (up to version 4)

Summary

This chapter has covered the following:
 Why every developer should perform some type of test to see if code actually
works. Developers who use automatic unit tests can repeat these tests on
demand to ensure that new code works and does not break existing tests.
 Writing simple unit tests, which are not difficult to create without JUnit.
 As tests are added and become more complex, writing and maintaining tests
becomes more difficult.
 Introduction to JUnit as a unit testing framework that makes it easier to create,
run, and revise unit tests.
 Stepping through a simple JUnit test.

2. Exploring core JUnit

2.1 Core annotations

These are the most important concepts:

  • A test class may be a top-level class, a static member class, or an inner class annotated as @Nested that contains one or more test methods.
  • Test classes cannot be abstract and must have a single constructor.
  • The constructor must have no arguments, or arguments that can be dynamically resolved at runtime through
  • dependency injection.
  • A test class is allowed to be package-private as a minimum requirement for visibility. It is no longer required that test classes be public, as was the case up to JUnit 4.x.
  • A test method is an instance method that is annotated with @Test, @RepeatedTest, @ParameterizedTest, @TestFactory, or @TestTemplate.
  • A life cycle method is a method that is annotated with @BeforeAll, @AfterAll,@BeforeEach, or @AfterEach.

2.1.1 The @DisplayName annotation

2.1.2 The @Disabled annotation

It signals that the annotated test class or test method is disabled and should not be executed.

2.2 Nested tests

The typical use case is when two classes are tightly coupled, and it’s logical to provide direct
access from the inner class to all instance variables of the outer class.

2.3 Tagged tests

If you are familiar with JUnit 4, tagged tests are replacements for JUnit 4 categories.

2.4 Assertions

To perform test validation, you use the assert methods provided by the JUnit
Assertions class.

After the heading parameter from the assertAll method, we provide the rest of
the arguments as a collection of executables—a shorter, more convenient way to assert
that supplied executables do not throw exceptions.

The assertAll method will always check all the assertions that are provided to it, even
if some of them fail—if any of the executables fail, the remaining ones will still be run.

The advantage of using lambda expressions as arguments for assertion methods is that
all of them are lazily created, resulting in improved performance.

2.5 Assumptions

Sometimes tests fail due to an external environment configuration or a date or time
zone issue that we cannot control. We can prevent our tests from being executed
under inappropriate conditions.

2.6 Dependency injection in JUnit 5

JUnit 5 allows test constructors and methods to have parameters, but they
need to be resolved through dependency injection.

2.6.1 TestInfoParameterResolver

2.6.2 TestReporterParameterResolver

2.6.3 RepetitionInfoParameterResolver

If a parameter in a method annotated with @RepeatedTest, @BeforeEach, or
@AfterEach is of type RepetitionInfo, RepetitionInfoParameterResolver
supplies an instance of this type. Then RepetitionInfo gets information about the
current repetition and the total number of repetitions for a test annotated with
@RepeatedTest.

2.7 Repeated test

2.8 Parameterized tests

Parameterized tests allow a test to run multiple times with different arguments. The
great benefit is that we can write a single test to be performed using arguments that
check various input data.

@ValueSource lets us specify a single array of literal values.

@EnumSource enables us to use enum instances.

@CsvSource to express argument lists as comma-separated values (CSV)

@CsvFileSource allows us to use CSV files from the classpath.

2.9 Dynamic tests

JUnit 5 introduces a dynamic new programming model that can generate tests at runtime. We write a factory method, and at runtime, it creates a series of tests to be executed. Such a factory method must be annotated with @TestFactory.

A dynamic test has a different life cycle than a standard test annotated with @Test.
The methods annotated with @BeforeEach and @AfterEach are executed for the
@TestFactory method but not for each dynamic test; other than these methods,
there are no life cycle callbacks for individual dynamic tests. The behavior of
@BeforeAll and @AfterAll remains the same; they are executed before all tests
and at the end of all tests.

2.10 Using Hamcrest matchers

As we write more unit tests and assertions, we inevitably find that some assertions
are big and hard to read.The goal is to simplify the assertion made in the test method.

此图像的alt属性为空;文件名为image-14-1024x525.png

The Hamcrest library
Hamcrest is not a testing framework itself, but it helps us declaratively specify simple
matching rules. These matching rules can be used in many situations, but they are
particularly helpful for unit testing.

Summary

  • The core JUnit 5 classes related to assertions and assumptions.
  • Using JUnit 5 methods and annotations: the methods from the assertions and assumptions classes, and annotations like @Test, @DisplayName, and @Disabled
  • The life cycle of a JUnit 5 test, and controlling it through the @BeforeEach, @AfterEach, @BeforeAll, and @AfterAll annotations
  • Applying JUnit 5 capabilities to create nested tests and tagged tests (annotation:@Tag)
  • Implementing dependency injection with the help of test constructors and methods with parameters
  • Applying dependency injection by using different parameter resolvers (TestInfoParameterResolver, TestReporterParameterResolver)
  • Implementing repeated tests (annotation: @RepeatedTest) as another application of dependency injection
  • Parameterized tests, a very flexible tool for testing that consumes different data sets and dynamic tests created at runtime (annotations: @ParameterizedTest, @TestFactory)
  • Using Hamcrest matchers to simplify assertions

Bootstrapping Microservices


Chapter 1: Why microservices?


1.5 What is a microservice?

DEFINITION A microservice is a tiny and independent software process that runson its own deployment schedule and can be updated independently.

1.6 What is a microservices application?

DEFINITION A microservices application is a distributed program composed ofmany tiny services that collaborate to achieve the features and functionality ofthe overall project.

Chapter 2: Creating your first microservice


2.6.8 Live reloading for fast iteration
In development mode, we’d like to optimize for fast iterations and productivity.Alternately, in production mode, we’d like to optimize for performance and security.These needs are at odds with each other; hence, these must be treated separately.

Chapter 3: Publishing your first microservice


3.9.1 Creating a private container registry在阿里云创建了一个私有镜像,并且可以push,可以pull自己构建的镜像,以前都是用共有的DockerHub.

Chapter 4: Data management for microservices


4.4 Adding file storage to our application使用了ali-oss,并且使用Node.js的SDK来完成文件的浏览。

Chapter 5: Communication between microservices


5.5 Live reload for fast iterations

NOTE Not being able to quickly update the code in a running application is aterrible thing for our development process and can be a huge drain on ourproductivity. We’ll address this early and find a way to restore our live reloadcapability.


5.8 Indirect messaging with RabbitMQ
5.8.6 Single-recipient indirect messaging

NOTE Single-recipient messages are one-to-one: a message is sent from onemicroservice and received by only a single other. This is a great way of makingsure that a particular job is done only once within your application.

5.8.7 Multiple-recipient messages

NOTE Multiple-recipient messages are one-to-many: a message is sent fromonly a single microservice but potentially received by many others. This is agreat way of publishing notifications within your application.

Chapter 6: Creating your production environment


6.4 Infrastructure as codeIt’s called infrastructure as code because rather than manually creating infrastructure, we will write code that creates our infrastructure.The fact that this code both describes and builds our infrastructure makes it a form of executable documentation.


6.7 Creating infrastructure with Terraform
6.7.1 Why Terraform?Terraform is a tool and a language for configuring infrastructure for cloud-based applications.


由于在国内,访问外网极其慢,使用aliyun 时terraform init都无法继续下去……,在这一块就可以确定要想Infrastructure as code这种比较前沿技术在国内应用并流行起来,这一定是容器的盛行才能带得动,像aliyun这样的云提供商就要想办法解决这样的一系列问题才能使其更好的流行起来。
当然,如果只是简单的应用部署,而且没有使用k8s集群的方式,使用GUI的控制台来说也是可以的,但是站在更高的抽象程度上来看,那些都是资源,如果可以解决开发,测试及上产上的一些问题,使用抽象的资源来看,比起一台台服务器,网络端口,持久化卷等等具体的资源,使用代码的方式,更直观,宜用。
真正使用的技术一定是视具体的环境和场景和条件来定夺的。
经过很多次尝试后,运行成功了。

Chapter 7: Getting to continuous delivery


7.4 Continuous delivery (CD)Continuous delivery (CD) is a technique in software development where we do frequentautomated deployments of our updated code to a production (or testing) environment.
This is an important aspect of our application because it’s how we reliably andfrequently deliver features into the hands of our customers. Getting feedback fromcustomers is vital to building a product that’s relevant. CD allows us to quickly andsafely get code changes into production and promotes a rapid pace of development.


7.7 Continuous delivery with Bitbucket PipelinesWe don’t want to manually invoke Terraformfor every change to our infrastructure or microservices. We’d like to deploy changes frequently, and we want it to be automated and streamlined so that we can spend the majority of our time building features rather than deploying our software. In addition,automation also greatly reduces the potential for human error.

Chapter 8: Automated testing for microservices


8.5 Testing with Jest

8.5.10 Mocking with Jest

DEFINITION Mocking is where we replace real dependencies in our code withfake or simulated versions of those.


The purpose of mocking is to isolate the code we are testing.Isolating particular sections of code allows us to focus on just testing only that codeand nothing else. Isolation is important for unit testing and test-driven development.
DI (dependency injection) is a technique where we inject dependencies into our code ratherthan hard-coding them.

function square(n, multiply) {     
     return multiply(n, n); 
}

 test("can square two", () => {     
     const mockMultiply = (n1, n2) => {         
                      expect(n1).toBe(2);         
                      expect(n2).toBe(2);         
                      return 4;     
};          

  const result = square(2, mockMultiply);     
  expect(result).toBe(4); 
}) 


You might note at this point that we have just implemented the square function,tested it, and proved that it works—and the real version of the multiply functiondoesn’t even exist yet!
This is one of the superpowers of test-driven development(TDD). TDD allows us to reliably test incomplete versions of our code.

真正测试的时候,发现:

Chapter 9: Exploring FlixTube


9.7 FlixTube deep dive9.7.2 Mocking storageFor convenience during development, we replaced the Azure version of the videostorage microservice with a mock version.
When running in development, we’d prefer to eliminate external dependencies likeconnections to cloud storage. In this case, limiting our storage to the local filesystemmakes the setup for development easier. Performance is improved because videos arestored locally and not sent out to the cloud.

NOTE Removing or replacing big complex microservices—possibly evenwhole groups of microservices—is an important technique for reducing thesize of our application so that it can fit on a single computer and be able torun during development.


9.11 FlixTube in the future

学习到两个新词——

Recommendations/ Likes / Favorites

喜欢—>likes收藏—->favorites
9.12 Continue your learningPracticing the art of development is what takes you to the next level.
Development is not without challenges. In fact, it is a never-ending rollercoaster ofproblems and solutions.
The references at the end of each chapter will help you continue your learning journey. But just remember that your key to success and your key to retaining these skills is consistent practice.

Chapter 10: Healthy microservices

10.2 Monitoring your microservices

  • Logging
  • Error handling
  • Log aggregation
  • Automatic health checks

10.2.7 Automatic restarts with Kubernetes health checksKubernetes has a great feature for automated health checks that allows us to automatically detect and restart unhealthy microservices.


The readiness probe shows if the microservice has started and is ready to start acceptingrequests. The liveness probe then shows whether the microservice is still alive and is stillaccepting requests.


First, if we didn’t use readiness and liveness probes, our history microservice will constantly start up, crash, and restart while RabbitMQ is down. This constant restarting isn’t an efficient use of our resources, and it’s going to generate a ton of error logging that we’d have to analyze (in case there’s a real problem buried in there!).


This would save the microservice from constantly crashing and restarting, but it requiressignificantly more sophisticated code in our microservice to handle the disconnectionand reconnection to RabbitMQ. We don’t need to write such sophisticated codebecause that‘s what the probes are doing for us.

Chapter 11: Pathways to scalability


11.2 Scaling the development process11.2.6 Creating multiple environments
现在使用多环境开发是一件比较容易的事情,但是有时要考虑数据问题。比如生产环境的数据是否就可以直接导入到仿真,测试,开发环境呢?不一定,如果直接使用的是生产的数据,可能导致的一个问题就是每个环境都有自己的资源库,如果是生产环境的数据,我们可能有时候不能直接在仿真黄静使用,比如OSS文件,可能每个环境都有自己的bucket等等,如果链接直接使用生产环境的,可能就访问不了了。

当让所有的问题如果存在肯定是有解决方案的,只是成本问题。我们在引入多环境的初衷是好的,但是同时我们也要注意到我们同时也引入了一些额外的成本。

11.5 Refactoring to microservices
DO YOU REALLY NEED MICROSERVICES?

  1. Is it really worth the cost of doing the conversion?
  2. Do you really need to scale?
  3. Do you really need the flexibility of microservices?

PLAN YOUR CONVERSION AND INVOLVE EVERYONEKNOW YOUR LEGACY CODE

IMPROVE YOUR AUTOMATION
With microservices, you can’t get away from automation. If you can’t afford to investin automation, you probably can’t afford to convert to microservices.

BUILD YOUR MICROSERVICES PLATFORMCARVE ALONG NATURAL

SEAMSEXTRACT THE PARTS THAT CHANGE MOST FREQUENTLYAND

REPEAT . . .

IT DOESN’T HAVE TO BE PERFECT
A SPECTRUM OF POSSIBILITIES

11.7 From simple beginnings . . .

这本书的阅读笔记和小结至此结束,下面总结一下这本书介绍的技术。

ToolVersionPurpose
Git2.27.0Version control
Node.js12.18.1Runtime environment
Visual Studio (VS) Code1.46.1code editor
Docker19.03.12package, publish, and test our microservices.
Docker Compose1.26.2configure, build, run, and manage
multiple containers at the same time.
Azure Storage2.10.3store files in the cloud
MongoDB4.2.8NoSQL type of database
RabbitMQ3.8.5message queuing software
amqplib0.5.6 configure RabbitMQ and to send and receive me-ssages from JavaScript.
Kubernetes1.18.8 computing platform that we use to host our microservices in production.
Terraform0.12.29script the creation of cloud resources and applica-tion infrastructure.
Kubectl1.18.6command-line tool for interacting with a Kubernetes cluster
Azure CLI2.9.1managing Azure accounts and cloud resources
Bitbucket Pipelines The hosted service from Atlassian that we’ll use for CD to automate the deployment of our application
Jest26.2.2a tool for automated testing of JavaScript code.
Cypress4.12.1a tool for automated testing of web pages.

这本书通过一个简单的FlixTube的例子来一步步将微服务从开发,测试,部署等一个个环节打通,最终通过Terraform+Kubernetes+Bitbuket Pipeline实现CICD.