This is the 10th day of my participation in the More text Challenge. For more details, see more text Challenge

This article is participating in the “Java Theme Month – Java Development in action”. See the link to the event for more details

One, foreword

Requirements: a testing framework is required to test an IM system while adapting current business logic.


Jmeter can also test Websockets. Why develop your own testing framework?

The most important point: THE TCP protocol is custom and does not integrate well with frameworks such as Jmeter. PS: Of course, Jmeter is not deep enough.


So for an IM system, its corresponding test framework should meet the following requirements:

  1. With continuity, context: While maintaining the connection, certain business logic processing is performed, such as: sending messages, adding groups, adding friends, etc
  2. Easy access: easy to develop docking code
  3. Generate many different reports
  4. extensible

Well, give it a try.




Ii. V1 version

Initial thinking, such an automated test, needs to meet:

  1. Automatic execution: All test cases can be executed after startup. Failure of one test case does not affect other test cases
  2. You can output custom reports: the console prints and generates files
  3. Simple enough: Easy to integrate business, developable, and fast


Thinking process

Thought process:

  1. A test case should be an object
  2. Test cases should be isolated from each other
  3. How do I run multiple test cases?
  4. How do I save test case runtime exceptions?


(1) The test case should be an object

Run: is a capability that can be abstracted as an interface:

public interface Test {

    void run(a);
}
Copy the code

A test case should be an object, and each use case is derived from a template:

Has property: use case name name

public abstract class TestCase implements Test {
    protected String name;
    
    public TestCase(a) {}public TestCase(String name) {
        this.name = name;
    }
    
    @Override
    public void run(a) {}}Copy the code


(2) Test cases should be isolated from each other

Test cases should be isolated from each other:

  1. The running of one use case should not affect another
  2. Some preparation is required before the test cases can be run
  3. Some cleanup is required after the test cases are run

Elicits the design pattern – template method pattern, code as follows:

public abstract class TestCase implements Test {
    protected String name;
    
    public TestCase(a) {}public TestCase(String name) {
        this.name = name;
    }
    
    @Override
    public void run(a) {
        setUp();
        
        try{
            runTest();
        } finally{ tearDown(); }}protected void setUp(a) throws Exception {}
    protected void runTest(a) throws Exception {}
    protected void tearDown(a) throws Exception {}}Copy the code


(3) How to run multiple test cases?

So far, the framework can only run custom TestCases one at a time.

How about:

  1. Is it possible to run multiple “transparently”?
  2. The caller doesn’t have to care if there’s one call or more calls, right?


This section uses the combination pattern in the design pattern, corresponding to the class diagram, as shown in figure:

The code is as follows:

public class TestSuite extends TestCase {

    private final List<Test> fTests = new ArrayList<>();
    
    @Override
    public void run(a) {
        for (Test test : this.fTests) { test.run(); }}public void addTest(Test test) {
        this.fTsets.add(test); }}Copy the code

Actual run, for example:

TestSuite testSuite = new TestSuite();
testSuite.addTest(new CalculatorTest('test1'));
testSuite.addTest(new CalculatorTest('test2'));

TestSuite ts = new TestSuite();
ts.addTest(new CalculatorTest('test3'));
testSuite.addTest(ts);

testSuite.run();
Copy the code


(4) How to save the exceptions that occur during the running of test cases?

The test fails, and the code throws an exception: Error.

At this point, introduce the: TestResult class to collect the results.

importantTestResultAs follows:

public class TestResult {
    privateList<TestFailure> errors; . .Important: / /
    void run(final TestCase test) {
        startTest(test);
        try {
            test.doRun();
        } catch (Throwable e) {
            addError(test, e);
        }
        endTest(test);
    }

    private void startTest(Test test) {
        int count = test.countTestCases();
        testCount += count;
    }

    private void endTest(Test test) {}}Copy the code


Other corresponding modifications, the code is as follows:

// test.java:
public interface Test {
    void run(TestResult tr);
    int countTestCases(a);
}


// Testcase.java:
public abstract class TestCase implements Test {... .Important: / /
    @Override
    public void run(TestResult testResult) {
        testResult.run(this); }... . }Copy the code

So to summarize, the current invocation process:

summary

What design patterns have been used so far:

Each thought step corresponds to a design pattern.

  1. Command mode: Express a test case, i.eTest
  2. Template mode: Complete data preparation and cleaning,setUp()tearDown()
  3. Combination pattern: masking differences between one and more,List<Test>
  4. Gather parameters mode: Use parameters to gather information to isolate test cases and test results


Careful students have found that this is not the castrated Junit!!

Yes. After imitating Junit 3, Junit 4 has started a new version.

Here is the framework of junit3:

butJunitThe big idea is that convention is better than configuration.

That is, Junit loads methods beginning with Test as test cases. Now it seems simple, but ideas are important!!