2018年4月30日 星期一

ImportSelector、ImportBeanDefinitionRegistrar (Spring 4.x 二)

@Import、ImportSelector、ImportBeanDefinitionRegistrar (源碼的 @Import 有說明)
這三個在 3.x 就已經有了,但 ImportSelector 在 4.2 可以不加 annotation
@Import 是對沒有 @Component 相關的 annotaion,也可以像有加一樣,而其他兩個也是差不多的功能,只不過不是 annotation

※ImportSelector

public class ISPractice implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// ... 一些判斷
        return new String[]{"bruce.home.bean.Ppp"};
    }
}

※回傳時,要寫包名+類別名

※AnnotationMetadata 參數表示誰加載它的所有 annotation,如下面的 Animal,可以取得 @Configuration 和 @Import


※bean

@Configuration
@Import({ISPractice.class})
public class Animal {}
    
    
    
//@Component 4.2
public class Ppp {}

※Ppp 類別在 4.2 (含)以上可以不寫


※測試

@Test
public void testApp() {
    AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Animal.class);
    for (String s : app.getBeanDefinitionNames()) {
        System.out.println(s);
    }
    app.close();
}





※ImportBeanDefinitionRegistrar

public class IBDRPractice implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        if (registry.containsBeanDefinition("animal")) {
            registry.registerBeanDefinition("xxx", new RootBeanDefinition(Ppp.class));
        }
    }
}

※如果 spring 的 IOC 裡有 animal 這個 bean,那就將 Ppp 這個類別註冊進來,id 叫 xxx


※bean

@Configuration
@Import({IBDRPractice.class})
public class Animal {}
    
    
    
public class Ppp {}

※測試類和 ImportSelector 一樣

※Ppp 類別,從3.x 版就可以不寫了

2018年4月29日 星期日

@Conditional (Spring 4.x 一)

依條件判斷,是否將某個 class 註冊到 bean 裡面
假設 java 版本是 1.7 就將 Animal 註冊;1.8 就將 Bird 註冊
@Conditional 可以寫在 Type 或是方法上



※寫在 Type

※條件

public class Java7Condition implements Condition {
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String version = context.getEnvironment().getProperty("java.specification.version");
        if ("1.7".equals(version)) {
            return true;
        }
        return false;
    }
}
    
    
    
public class Java8Condition implements Condition {
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String version = context.getEnvironment().getProperty("java.specification.version");
        if ("1.8".equals(version)) {
            return true;
        }
        return false;
    }
}



※使用

@Conditional(Java7Condition.class)
@Component
public class Animal {}
    
    
    
@Conditional(Java8Condition.class)
@Component
public class Bird {}



※測試

ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
for (String s : app.getBeanDefinitionNames()) {
    System.out.println(s);
}
app.close();

※可將 vm 參數改成需要的,按右鍵如下:






※寫在方法上



@Configuration
public class JavaVersionCondition {
    @Conditional(Java7Condition.class)
    @Bean
    public Animal a1() {
        Animal a = new Animal();
        a.setId(1);
        a.setName("tiger");
        return a;
    }
    
    @Conditional(Java7Condition.class)
    @Bean
    public Animal a2() {
        Animal a = new Animal();
        a.setId(2);
        a.setName("lion");
        return a;
    }
    
    @Conditional(Java8Condition.class)
    @Bean
    public Bird b1() {
        Bird b = new Bird();
        b.setId(3);
        b.setName("crow");
        return b;
    }
    
    @Conditional(Java8Condition.class)
    @Bean
    public Bird b2() {
        Bird b = new Bird();
        b.setId(4);
        b.setName("sparrow");
        return b;
    }
}

※是 java7 就有 a1、a2;是 java8 就有 b1、b2


※測試

// System.getProperties().list(System.out);
// System.out.println(app.getEnvironment().getProperty("java.specification.version"));
    
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Animal.class);
for (String s : app.getBeanDefinitionNames()) {
    System.out.println(s);
}
    
Map<String, Bird> type = app.getBeansOfType(Bird.class); // 取得 class 是 Bird 的 id 及內容
System.out.println(type);
    
for (String s : app.getBeanNamesForType(Bird.class)) { // 取得 class 是 Bird 的 id
    System.out.println(s);
}
app.close();



2018年4月28日 星期六

JUnit 5 整合 Spring 5


<properties>
    <junit.platform.version>1.1.1</junit.platform.version>
    <junit.version>5.1.1</junit.version>
    <spring.version>5.0.5.RELEASE</spring.version>
</properties>
    
<dependencies>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>${junit.version}</version>
        <scope>test</scope>
    </dependency>
    
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-engine</artifactId>
        <version>${junit.version}</version>
        <scope>test</scope>
    </dependency>
    
    <dependency>
        <groupId>org.junit.platform</groupId>
        <artifactId>junit-platform-launcher</artifactId>
        <version>${junit.platform.version}</version>
        <scope>test</scope>
    </dependency>
    
    <dependency>
        <groupId>org.junit.platform</groupId>
        <artifactId>junit-platform-engine</artifactId>
        <version>${junit.platform.version}</version>
        <scope>test</scope>
    </dependency>
    
    
    
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${spring.version}</version>
        <scope>test</scope>
    </dependency>
</dependencies>

※如果要用 spring-web ,必需多個 spring-web 時;如果要用 spring mvc,必需多個 spring-webmvc


import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import lt.SpringMaven.bean.Animal;
import lt.SpringMaven.bean.Bird;
import lt.SpringMaven.bean.Chicken;
    
@ExtendWith(SpringExtension.class)
// @SpringJUnitConfig(Animal.class)
@SpringJUnitConfig(locations = "classpath:applicationContext.xml")
public class AppTest {
    @Autowired(required = false)
    private Animal animal;
    
    @Autowired(required = false)
    private Bird bird;
    
    @Autowired(required = false)
    private Chicken chicken;
    
    @Test
    public void testBruce() {
        System.out.println("animal=" + animal);
        System.out.println("bird=" + bird);
        System.out.println("chicken=" + chicken);
    }
}

※@SpringJUnitConfig 可以寫設定檔或 annotation,其實裡面呼叫的還是 2.5 的 @ContextConfiguration,所以也可以使用這個 annotation

※如果是 web,要使用 @SpringJUnitWebConfig



Junit 5 整合 Spring 4


<properties>
    <junit.platform.version>1.1.1</junit.platform.version>
    <junit.version>5.1.1</junit.version>
    <spring.version>4.3.16.RELEASE</spring.version>
</properties>
    
<repositories>
    <repository>
        <id>jitpack.io</id>
        <url>https://jitpack.io</url>
    </repository>
</repositories>
    
<dependencies>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>${junit.version}</version>
        <scope>test</scope>
    </dependency>
    
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-engine</artifactId>
        <version>${junit.version}</version>
        <scope>test</scope>
    </dependency>
    
    <dependency>
        <groupId>org.junit.platform</groupId>
        <artifactId>junit-platform-launcher</artifactId>
        <version>${junit.platform.version}</version>
        <scope>test</scope>
    </dependency>
    
    <dependency>
        <groupId>org.junit.platform</groupId>
        <artifactId>junit-platform-engine</artifactId>
        <version>${junit.platform.version}</version>
        <scope>test</scope>
    </dependency>
    
    
    
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    
    <dependency>
        <groupId>com.github.sbrannen</groupId>
        <artifactId>spring-test-junit5</artifactId>
        <version>1.0.3</version>
        <scope>test</scope>
    </dependency>
    
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${spring.version}</version>
        <scope>test</scope>
    </dependency>
</dependencies>

※好心人提供的 Junit5-Spring4 的 jar 檔,這樣子才有 SpringExtension 可以用

※如果要用 spring-web ,必需多個 spring-web 時;如果要用 spring mvc,必需多個 spring-webmvc




import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import lt.SpringMaven.bean.Animal;
import lt.SpringMaven.bean.Bird;
import lt.SpringMaven.bean.Chicken;
    
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = Animal.class)
public class AppTest {
    @Autowired(required = false)
    private Animal animal;
    
    @Autowired(required = false)
    private Bird bird;
    
    @Autowired(required = false)
    private Chicken chicken;
    
    @Test
    public void testBruce() {
        System.out.println("animal=" + animal);
        System.out.println("bird=" + bird);
        System.out.println("chicken=" + chicken);
    }
}

※spring5才有 @SpringJUnitConfig,所以使用 2.5 的 @ContextConfiguration

2018年4月27日 星期五

JUnit 4 整合 Spring 3/4/5


<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>
    
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>3.2.18.RELEASE</version>
</dependency>
    
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>3.2.18.RELEASE</version>
    <scope>test</scope>
</dependency>
    
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.0.5.RELEASE</version>
</dependency>
    
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.0.5.RELEASE</version>
</dependency>

※spring-web 使用在 spring web 時;spring-webmvc 使用在 spring mvc


import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import bean.Apple;
import bean.Banana;
    
// @ContextConfiguration(locations = { "classpath*:/applicationContext.xml" })
@ContextConfiguration(classes = { Apple.class })
// @RunWith(SpringRunner.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class AppTest {
    @Autowired
    private Apple apple;
    
    @Autowired
    private Banana banana;
    
    @Test
    public void testSomeLibraryMethod() {
        System.out.println("apple=" + apple);
        System.out.println("banana=" + banana);
    }
}

※@RunWith 二選一

※@ContextConfiguration 有 locations 和 classes,看要用設定檔或是 Annotation

※測 web 或 mvc 時,有可能要抓 WEB-INF 的路徑,可用 ApplicationContext app = new FileSystemXmlApplicationContext("src/main/webapp/WEB-INF/applicationContext.xml");
和 @ContextConfiguration(locations = "file:src/main/webapp/WEB-INF/applicationContext.xml")



※spring-test 包的 annotation

必需加 @ContextConfiguration 和 @RunWith,不加也不會報錯,只是使用 spring 的 annotation時,一點效果都沒有而已


※@Repeat、@Timed、@IfProfileValue

@ContextConfiguration(locations = { "classpath*:/applicationContext.xml" })
@RunWith(SpringJUnit4ClassRunner.class)
public class AppTest {
    @Repeat(3)
    @Test
    public void testRepeat() {
        System.out.println("yeah");
    }
    
    @Timed(millis = 1000L)
    @Test
    @Ignore
    public void testTimed() {
        try {
            System.out.println("yeah1");
            Thread.sleep(2000);
            System.out.println("yeah2");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("yeah3");
    }
    
    @IfProfileValue(name = "java.specification.version", values = "8")
    @Test
    @Ignore
    public void testIfProfileValue() {
        System.getProperties().list(System.out);
    }
}

※@Timed 官網有說在失敗之前會完成測試,我試的結果不管怎麼樣都會成功,可能就是這個原因吧!



※@ProfileValueSourceConfiguration

配合@IfProfileValue,沒有@ProfileValueSourceConfiguration,預設是 SystemProfileValueSource
參數給一個類別,這個類別要實作 ProfileValueSource 介面


public class MyPVS implements ProfileValueSource {
    @Override
    public String get(String key) {
        System.out.println("key=" + key);
        return System.getProperty(key);
    }
}
    
    
    
@ContextConfiguration(locations = { "classpath*:/applicationContext.xml" })
@RunWith(SpringJUnit4ClassRunner.class)
@ProfileValueSourceConfiguration(MyPVS.class)
public class AppTest {
    @Test
    @IfProfileValue(name = "java.specification.version", values = "10")
    public void testXxx() {
        System.out.println("xxx");
    }
}

※get 方法返回值等於 @IfProfileValue 的 values 值就會執行



※@ActiveProfiles、@Profile

.@Profile 裡面寫個字串,每一個 class 都可以有一個 @Profile
.@ActiveProfiles 裡面也是寫字串,只有裡面的字串和 @Profile 裡的字串一樣,才會被加載

@Component
public class App {}
    
@Component
@Profile("dev")
public class Animal {}
    
@Component
@Profile("product")
public class Bird {}
    
    
    
@ContextConfiguration(classes = { App.class, Animal.class, Bird.class })
@RunWith(SpringJUnit4ClassRunner.class)
@ActiveProfiles("product")
public class AppTest {
    @Autowired(required = false)
    private App app;
    
    @Autowired(required = false)
    private Animal animal;
    
    @Autowired(required = false)
    private Bird bird;
    
    @Test
    public void testApp() {
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        for (String s : app.getBeanDefinitionNames()) {
            System.out.println(s);
        }
        app.close();
    
        System.out.println("app=" + this.app);
        System.out.println("animal=" + animal);
        System.out.println("bird=" + bird);
    }
}

※使用 ClassPathXmlApplication 只能抓沒有 @Profile 的 App

※寫在 AppTest 的全域變數才是 @ActiveProfiles 參數裡有對應到 @Profile 參數才能抓到的 bean

※還是要加 @ContextConfiguration 和 @RunWith,否則三個全域變數都是 null

2018年4月25日 星期三

常用方法、@DisplayName、@RepeatedTest、條件測試 (JUnit5 二)

※常用方法

@Test
public void xxx() {
    int a = 25;
    int b = 15;
    // Assertions.assertEquals(a, b);
    // Assertions.assertEquals(a, b, "我錯了");
    // Assertions.assertEquals(a, b, () -> "我死了");
    
    Assertions.assertEquals(a, b, () -> {
        if (a > b) {
            return "a大於b";
        }
        return "a小於b";
    });
}
    
@Test
public void ooo() {
    String rtn = Assertions.assertTimeout(Duration.ofMillis(1000), () -> {
        Thread.sleep(1500);
        System.out.println("超時也會印出");
        return "完成時印出";
    });
    
    //    String rtn = Assertions.assertTimeoutPreemptively(Duration.ofMillis(1000), () -> {
    //        Thread.sleep(1500);
    //        System.out.println("超時不會印出");
    //        return "完成時印出";
    //    });
    System.out.println(rtn);
}
    
@Test
public void zzz() {
    ArithmeticException ari = Assertions.assertThrows(ArithmeticException.class, () -> {
        int rtn = 1 / 0;
    });
    System.out.println(ari.getMessage());
}

※assertTimeoutPreemptively 的 preempt 有先發制人的意思,一 timeout 就結束了,所以後面的程式不會執行

※通常 overloading 方法,最後有一個 String,是錯誤時顯示在圖形裡的,JUnit4 是第一個參數,而 JUnit5 還可以用 java8 的 Supplier



※@DisplayName、@RepeatedTest

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.Test;
    
@DisplayName("這樣而已")
public class AppTest2 {
    @Test
    @RepeatedTest(3) // 加跑幾次
    public void xxx() {
        System.out.println("x");
    }
    
    @Test
    @DisplayName("哇哈哈")
    public void ooo() {
        System.out.println("o");
    }
}

※@RepeatedTest 裡的數字是「加跑」幾次的意思

※@DisplayName 要看圖形,如下:



※條件測試

@EnabledOnOs({ OS.WINDOWS, OS.LINUX })
// @DisabledOnOs({ OS.WINDOWS, OS.LINUX })
@EnabledIfSystemProperty(named = "user.language", matches = "en")
// @DisabledIfSystemProperty(named = "user.language", matches = "en")
@Test
public void testXxx() {
    System.out.println("xxx");
}

※可以使用 System.getProperties().list(System.out) 偷看一下

※目前版本是 5.1.1,還有一些 annotation 在實驗的階段

2018年4月24日 星期二

基本的 Annotation (JUnit5 一)

指南:官網中文

※Maven 設定

<properties>
    <junit.platform.version>5.1.1</junit.platform.version>
</properties>
    
<dependencies>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>${junit.platform.version}</version>
        <scope>test</scope>
    </dependency>
    
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-engine</artifactId>
        <version>${junit.platform.version}</version>
        <scope>test</scope>
    </dependency>
    
    <dependency>
        <groupId>org.junit.platform</groupId>
        <artifactId>junit-platform-launcher</artifactId>
        <version>1.1.1</version>
        <scope>test</scope>
    </dependency>
</dependencies>

※尤於 JUnit5 只支援 java8 以上,所以 eclipse 要按專案右鍵內容,然後如下設定



※IntelliJ 預設會抓最高版本,想改的話,如下圖:


※gradle 設定

test {
    systemProperty 'junit.jupiter.conditions.deactivate', '*'
    systemProperties = [
        'junit.jupiter.extensions.autodetection.enabled': 'true',
        'junit.jupiter.testinstance.lifecycle.default': 'per_class'
    ]
}
    
dependencies {
    testCompile("org.junit.jupiter:junit-jupiter-api:5.1.1")
    testRuntime("org.junit.jupiter:junit-jupiter-engine:5.1.1")
}




※測試

@BeforeAll
public static void start1() {
    System.err.println("class之前執行1");
}
    
@BeforeAll
public static void start2() {
    System.err.println("class之前執行2");
}
    
@BeforeEach
public void begin1() {
    System.out.println("方法之前執行1");
}
    
@BeforeEach
public void begin2() {
    System.out.println("方法之前執行2");
}
    
@AfterEach
public void end1() {
    System.out.println("方法之後執行1");
}
    
@AfterEach
public void end2() {
    System.out.println("方法之後執行2\r\n");
}
    
@AfterAll
public static void theEnd1() {
    System.err.println("class之後執行1");
}
    
@AfterAll
public static void theEnd2() {
    System.err.println("class之後執行2");
}
    
@Test
public void bruceTest1() {
    System.out.println("測試1");
}
    
@Test
public void bruceTest2() {
    System.out.println("測試2");
}
    
@Disabled
@Test
public void bruceTest3() {
    System.out.println("測試3");
}

※結果:
class之前執行1
class之前執行2
方法之前執行1
方法之前執行2
測試1
方法之後執行1
方法之後執行2

方法之前執行1
方法之前執行2
測試2
方法之後執行1
方法之後執行2

class之後執行1
class之後執行2

※JUnit4 和 JUnit5 對應:
@Test--------------@Test
@Before-----------@BeforeEach
@After-------------@AfterEach
@BeforeClass-----@BeforeAll
@AfterClass-------@AfterAll
@Ignore------------@Disabled

※除了 @Disabled 外,其他的都沒有屬性了

※@BeforeAll 和 @AfterAll 印出來是紅色的

※一樣都是 public void xxx(),或者 public static void xxx() 才可以測試

※注意以上的 annotation 除了 @Disabled 沒有繼承關係外,其他 5 個都有

2018年4月22日 星期日

@Rule、常用方法、測試其他類別 (JUnit4 二)

※@Rule


※內鍵的 Rule

※所謂內鍵,就是在 org.junit.rules 這包裡

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.Timeout;
    
public class AppTest3 {
    @Rule
    public Timeout timeout1 = Timeout.millis(10);
    // public Timeout timeout2 = new Timeout(10, TimeUnit.MILLISECONDS);
    
    @Rule
    public final ExpectedException thrown = ExpectedException.none();
    
    @Test
    public void xxx() throws InterruptedException {
        Thread.sleep(5);
    }
    
    @Test
    public void ooo() throws InterruptedException {
        Thread.sleep(5);
    }
    
    @Test
    public void zzz() throws InterruptedException {
        thrown.expect(ArithmeticException.class);
        int i = 1 / 0;
    }
}

※這個annotation是 4.7 版增加的,宣告時一定要 public

※@Test 裡的 timeout 和 expected 都有,但宣告 @Rule 是全域的,每個方法都會遵從

※ExpectedException.none() 表示不能有例外


※Verifier

原本的測試結束後,會執行這裡的邏輯,這裡的邏輯過了,測試才會過

@Rule
public Verifier verifier = new Verifier() {
    @Override
    protected void verify() {
        Assert.assertEquals(1, 1);
    }
};

※可看高手寫的文章

※自訂 Rule

import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
    
public class RuleTest implements TestRule {
    public Statement apply(Statement base, Description des) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                System.out.println("className=" + des.getClassName());
                System.out.println("displayName=" + des.getDisplayName());
                System.out.println("methodName=" + des.getMethodName());
                System.out.println("children=" + des.getChildren());
                System.out.println("annotations=" + des.getAnnotations());
                base.evaluate();
            }
        };
    }
}

※實作 TestRule 後,覆寫 apply 方法

※測試

@Rule
public RuleTest ruleTest = new RuleTest();
    
@Test
public void xxx() throws InterruptedException {
    Thread.sleep(5);
    System.out.println("aaa\r\n");
}
    
@Test
public void ooo() throws InterruptedException {
    Thread.sleep(5);
    System.out.println("bbb\r\n");
}

※如果不想寫 class 去實作,還可以用匿名和 lambda,如下:
@Rule
public TestRule xxx = new TestRule() {
    @Override
    public Statement apply(Statement base, Description des) {
        System.out.println("className=" + des.getClassName());
        System.out.println("displayName=" + des.getDisplayName());
        System.out.println("methodName=" + des.getMethodName());
        System.out.println("children=" + des.getChildren());
        System.out.println("annotations=" + des.getAnnotations());
//        try {
//            base.evaluate();
//        } catch (Throwable e) {
//            e.printStackTrace();
//        }
        return base;
    }
};
    
    
    
@Rule
public TestRule ooo = (base, des) -> {
    System.out.println("className=" + des.getClassName());
    System.out.println("displayName=" + des.getDisplayName());
    System.out.println("methodName=" + des.getMethodName());
    System.out.println("children=" + des.getChildren());
    System.out.println("annotations=" + des.getAnnotations());
    return base;
};

※此時不用寫 base.evaluate(),否則會執行兩次 @Test 裡的方法



※常用方法

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import org.hamcrest.CoreMatchers;
import org.junit.Test;
    
public class AppTest {
    @Test
    public void testApp() {
        assertEquals(5, 5);
        assertEquals("我錯了", 5, 5);
    
        AppTest t = new AppTest();
        assertSame(t, t);
    
        // Assert.fail();//一定不會過
    
        final Integer FIVE = 5;
        assertThat(FIVE, CoreMatchers.anything());// 一定會過
        assertThat(FIVE, CoreMatchers.is(5));// 5等於5嗎?
        assertThat(FIVE, CoreMatchers.not(10));// 5是10嗎
        assertThat(FIVE, CoreMatchers.notNullValue());// 5不是null嗎
    
        // 全部是5才會過
        assertThat(FIVE, CoreMatchers.allOf(CoreMatchers.is(5), CoreMatchers.is(5), CoreMatchers.is(5)));
    
        // 有一個是5就會過
        assertThat(FIVE, CoreMatchers.anyOf(CoreMatchers.is(1), CoreMatchers.is(5), CoreMatchers.is(2)));
    }
}

※很多 assertXxx 有很多的 overloading 方法,有些第一個參數都是String,可以打一些字,出錯時會顯示裡面的內容



※測試其他類別

@RunWith(Suite.class)
@Suite.SuiteClasses({ 
    TestXxx.class ,TestOoo.class
})
public class TestHaHaHa {
    
}

※可以測試 TestXxx 和 TestOoo 這兩個類別

※經過測試,TestHaHaHa 裡面只能有一個 @Test,但也不會被執行

2018年4月1日 星期日

設定檔的 server、log、location(nginx 二)

※server

server {
    listen       11111;
    server_name  localhost;
    
    location / {
        root   xxx;
        index  one.html;
    }
}
    
    
    
server {
    listen       22222;
    server_name  localhost;
    
    location / {
        root /usr/local/nginx/xxx
        #root D:\xxx
        index  two.html;
    }
}
    
    
    
server {
    listen       80;
    server_name ooo.com
    
    location / {
        root   xxx;
        index  name.html;
    }
}

※改好可用 ./nginx -t 測試設定檔有沒有錯

※http 裡的 server 可以有很多

※可以有不同的 port

※root 可以寫絕對路徑和相對路徑
但絕對路徑可能會有權限問題
Linux 的相對路徑預設是 /usr/local/nginx;Windows 為 nginx.exe 同層

※server_name 的網址必需寫在 hosts 檔裡,Linux 在 /etc;Windows 在 %SystemRoot%\system32\drivers\etc


※log

http {
    include mime.types;
    default_type application/octet-stream;
    
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';
    
    log_format xxx $remote_addr
    
    sendfile on;
    
    server {
        listen 11111;
        server_name localhost;
    
        location / {
            root   one;
            index  one.html;
        }
        access_log logs/access_11111.log main;
    }
    
    server {
        listen 22222;
        server_name localhost;
    
        location / {
            root D:;
            index two.html;
        }
        access_log logs/access_22222.log xxx;
    }
}

※每一個 server 裡都可以有一個自己的 log,log_format 後面定義一個名字,然後格式是 $開頭的東西,想知道更多可參考官網,使用瀏覽器或 curl 指令請求後,就可以看到 log 寫出來了



※locatioon

官網
.如果操作過程出現錯誤,請看 logs/error.log
.預設的 location 全部刪除,用 curl 指令或網址輸入 localhost 還是能抓到 html/index.html
.root 不寫,預設為 html,也就是 /usr/local/nginx/html,官網
.index 預設為 index.html,可以寫多個,用空隔隔開,找到第一個就用,找不到才往後找;但如果寫在 location 後面,只能寫一個,官網
.如果 location / 後面接檔案,index 的設定沒有效果,但不會報錯
.如果 location / 後面接目錄,index 的設定沒有效果,執行時會報 301 Moved Permanently

= ~ ~* ^~
=精準匹配
^~只匹配開頭
~正則匹配,大小寫不一樣
~*正則匹配,大小寫一樣
不加為一般匹配


※優先級

以完全相同為例
精準匹配 > 開頭匹配 > 正則匹配 > 一般匹配
開頭匹配和一般匹配不能同時寫,啟動伺服器時會報錯
正則匹配有兩個,誰寫前面就抓誰


#location /one.txt {
#    root one;
#}
    
location ~* /one.txt {
    root regex_not_i;
}
    
location ~ /one.txt {
    root regex_have_i;
}
    
location ^~ /one.txt {
    root headXXX;
}



※匹配目錄

location = / {
    root   /usr;
    index  index.html;
}
    
location / {
    root   one;
    index  one.txt;
}
    
#location /index.html {
#    root   two;
#    index  two.txt;
#}

※如果只有一個精準的 location = /,那 curl localhost 會將精準匹配的 /加上 index.html,然後再次發出 /index.html;如果只有一個一般的 location /,那就會直接將檔案顯示出來或 404

※此例使用 curl localhost 會匹配到精準匹配的 /,一定要有 /usr/index.html (檔案或資料夾都可以),不然會 403 Forbidden

然後再次匹配 /index.html ,最適合的只有一般匹配的 /,所以會找 /usr/local/nginx/one/index.html (因為 location 和 index 都有檔案,會忽略 index 的 one.txt)
但如果連第二個也註解,那會去找預設的 /usr/local/nginx/html/index.html

※如果將最後一個 location 註解打開,那 /index.html 會匹配到註解的 location,此時會找 /usr/local/nginx/two/index.html

※如果想執行的是第一個 location 裡的檔案,要寫成 = /index.html 才可以,而 index 可以不寫,因為 location 和 index 都寫,index 會被忽略