In my previous Android development career, barely used unit test, never seen anyone to introduce, as if this is not very important thing in the domestic developers eyes, there is no specific time or most of the development of students to use the unit test framework, perhaps more important reason is my personal informed.
background
What are unit tests?
Unit testing is writing test code for the smallest unit. In Java, the smallest unit of functionality is a method, so unit testing a Java program is testing a single Java method.
Why do unit tests
In foreign countries, the actual development process is often, write the test first, after the test is written, then start to write the implementation code. In the concrete implementation process, while writing while testing, when all the tests passed, it represents the completion of the development task. This is also known as TDD(Test Driven Development).
Introduction to the
Junit is an open source unit testing framework for the Java language, designed specifically for Java and most widely used. (Of course, Kotlin is also ok to use, just pay attention to some small details)
Depend on the way
Maven
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
Copy the code
Gradle
dependencies {
testImplementation 'junit: junit: 4.12'
}
Copy the code
The main method
The main methods in the Assert class are as follows:
The method name | Methods described |
---|---|
assertEquals | Assert that the expected value passed in is equal to the actual value |
assertNotEquals | Assert that the expected value passed in is not equal to the actual value |
assertArrayEquals | Asserts that the expected array passed in is equal to the actual array |
assertNull | Asserts that the passed object is null |
assertNotNull | Asserts that the passed object is not null |
assertTrue | Assert the condition is true |
assertFalse | The assertion condition is false |
assertSame | Assert that two objects refer to the same object, equivalent to “==” |
assertNotSame | Asserting that two objects refer to different objects is equivalent to “! =” |
assertThat | Asserts whether the actual value satisfies the specified condition |
Note that all of the above methods have their own overloaded methods, which can be preceded by a String parameter indicating that the assertion fails.
Commonly used annotations
Execution order: @beforeClass – > @before – > @test – > @after – > @AfterClass
Annotation name | meaning |
---|---|
@Test | Indicates that this method is a test method |
@Before | Executed in front of each test method for initialization |
@After | Execute after each test method to free resources |
@Ignore | Ignored test methods |
@BeforeClass | Runs in front of all methods in the class. The method that this annotation decorates must be static void |
@AfterClass | Runs last in the class. The method that this annotation decorates must be static void |
@RunWith | Specifies that the test class uses a runner |
@Parameters | Specifies the test data collection for the test class |
@Rule | Reformulate the behavior of the methods in the test class |
@FixMethodOrder | Specifies the order in which methods in the test class are executed |
use
Based on using
So let’s say we have an algorithm that’s equivalent to parentheses.
StackExample.kt
Equivalent braces / * * *, such as: given a string represented by the sequence of brackets, contain the following characters: '(',') ', '{','} ', '[' and'] ', to determine whether effective brackets sequence. * parentheses must be in accordance with the "()" order said, "() [] {}" is a valid brackets, but "([]" bracket is invalid. * * Solution idea: * Use stack storage to cut a string into char traversal, first storing symbols in the specified direction, such as '(','{','['). * return false * */ if the stack top symbol is equal to the current char and the stack is not null
fun isBrackets(str: String): Boolean {
if (str.length < 2) return false
val stack = Stack<Char>()
str.forEach {
if ("{([".contains(it)) {
stack.push(it)
} else {
if (stack.isNotEmpty() && isBracketChart(stack.peek(), it)) {
stack.pop()
} else return false}}return stack.isEmpty()
}
private fun isBracketChart(str1: Char, str2: Char): Boolean =
(str1 == '{' && str2 == '} ') ||
(str1 == '[' && str2 == '] ') || (str1 == '(' && str2 == ') ')
Copy the code
Code testing (not using Junit)
If we were not using Junit, we might have written the following test code:
fun main(a) {
println(isBrackets("{}"))
xxxx...
}
Copy the code
In contrast, if we added other methods, the main() method would have to be changed frequently, and the correctness of the test would not be intuitive.
Using Junit
We create a class like StackExampleKtTest under the corresponding test package, or we directly use the following shortcut, using MAC (Option + Enter) and Windows (CTRL + Enter) before the corresponding method, as shown in the figure
The following is an example:
StackExampleKtTest
class StackExampleKtTest {
@Test
fun testIsBrackets(a) {
assertArrayEquals(
booleanArrayOf(
isBrackets(""),
isBrackets("{"),
isBrackets("{{}}"),
isBrackets("{} ({})"),
isBrackets("{{}"),
), booleanArrayOf(
false.false.true.true.false))}}Copy the code
Parametric test
The above method of use, if we have to set the corresponding value every time we test a method, it is relatively tedious, then how to use successive different values to test the same method, so that we can avoid not to modify for many times, save some time. Use @runWith and @parameters.
Add a RunWith(paramterize.class) annotation to the test class, create a static method annotated by @paramters that returns a set of test data, and create a constructor whose parameters are in a sequence corresponding to the set of test data.
The following is an example:
@RunWith(Parameterized::class)
class StackExampleKtTest(private val str: Pair<String, Boolean>) {
// TODO:In turn, this approach makes it hard to find errors
companion object {
@Parameterized.Parameters
@JvmStatic
fun primeStrings(a): Collection<Pair<String, Boolean>> =
listOf(
"" to false."{" to false."{}" to false."{[]}" to true."asd{()}" to false)}@Test
fun testIsBrackets(a) {
// Notice the error message here
assertEquals("Error - current \"${str.first}\" to ${str.second}",
str.second, isBrackets(str.first))
}
}
Copy the code
Note: The corresponding @parameterized. Parameters method used in Kotlin requires the addition of @jVMStatic. In the process of using this parameterized test, if we do not add an error message, it may not be easy to find which test case is wrong when looking for problems, so we need to pay attention to this.
AssertThat usage
Used to improve readability of output after assertion failures. By default, assertion failures only raise an AssertionError, and we have no way of knowing what went wrong, and assertThat is there to solve that problem.
Common matcher arrangement:
matcher | instructions | example |
---|---|---|
is | The assertion argument is equal to the matching expression given later | assertThat(5, is (5)); |
not | The assertion argument is not equal to the matching expression given later | assertThat(5, not(6)); |
equalTo | Assert argument equality | assertThat(30, equalTo(30)); |
equalToIgnoringCase | Assert that strings are equal ignoring case | AssertThat (” Ab “, the equalToIgnoringCase (” Ab “)); |
containsString | The assertion string contains a certain string | AssertThat (” ABC “, containsString (” BC “)); |
startsWith | The assertion string starts with a string | AssertThat (” ABC “, startsWith (” a “)); |
endsWith | The assertion string ends with a string | AssertThat (” ABC “, endsWith (” c “)); |
nullValue | The value of the assertion parameter is NULL | assertThat(null, nullValue()); |
notNullValue | Asserts that the value of the argument is not null | AssertThat (” ABC “, notNullValue ()); |
greaterThan | Assert parameter greater than | assertThat(4, greaterThan(3)); |
lessThan | The assertion parameter is less than | assertThat(4, lessThan(6)); |
greaterThanOrEqualTo | Assert that arguments are greater than or equal to | assertThat(4, greaterThanOrEqualTo(3)); |
lessThanOrEqualTo | Assert arguments are less than or equal to | assertThat(4, lessThanOrEqualTo(6)); |
closeTo | Asserts that floating point numbers are in a certain range | AssertThat (4.0, closeTo (2.6, 4.3)); |
allOf | An assertion meets all criteria and is equivalent to && | assertThat(4,allOf(greaterThan(3), lessThan(6))); |
anyOf | To assert that a condition is met, equivalent to or | assertThat(4,anyOf(greaterThan(9), lessThan(6))); |
hasKey | Assert that the Map collection contains this key | AssertThat (map, hasKey (” key “)); |
hasValue | Assert that the Map collection contains this value | assertThat(map, hasValue(value)); |
hasItem | The assertion iterator contains this element | assertThat(list, hasItem(element)); |
@Rule
During the test process, we can also add @before or @after to provide a prompt Before and After the test, but it may be a bit cumbersome to write this every time. So you can use @rule at this point.
The following is an example:
TestPrompt
class TestPormpt : TestRule {
override fun apply(statement: Statement, description: Description): Statement {
// Get the test method name
val methodName: String = description.methodName
// equivalent to @before
println(methodName + "Before the test begins!")
// The test method to run
statement.evaluate()
// Run complete, equivalent to @after
println(methodName + "Test over!")
return statement
}
}
Copy the code
class StackExampleKtTest {
// TODO: 2020/11/15
// Note: When used in Kotlin, you need to change @rule to @get:Rule
// 或者 使用 @Rule @JvmField
//@get:Rule
@Rule @JvmField
public val prompt = TestPormpt()
// @Before
// fun testStart(){
// println(" test open ")
/ /}
//
// @After
// fun testStop(){
// println("测试关")
/ /}
@Test
fun testThat(a) {
assertThat("123", equalTo("123"))}}Copy the code
reference
- Liao Xuefeng – Writing JUnit tests
- Android Unit Testing part 1: Use of the JUnit framework