DEV Community

Akash Kava for Web Atoms

Posted on

Simple Unit Testing with Web Atoms

Test Class and Method

I know many would be unhappy seeing classes, but classes are only used to decorate and organize code, you can always do more functional things with Web Atoms Unit Tests.


/** Category decorator is optional, it just lets you specify name of test instead of name of class that extends TestItem */
@Category("Test Case 1")
export default class TestCase1 extends TestItem {

    @Test
    public parseTest() {
        Assert.equals(2, parseInt("2", 10));
    }

    /** You can specify name of test */
    @Test("Parse Float Test")
    public parseFloat() {
        Assert.equals(2.5, parseFloat("2.5"));
    }

}

Enter fullscreen mode Exit fullscreen mode

How to run the test?

You can execute @web-atoms/unit-test/index.js script against folder of all scripts. You can also give path to single test file as well.

d:\git\test> node ./node_modules/@web-atoms/unit-test/index.js ./dist

Test Case 1 > parseTest success.
Test Case 1 > Parse Float Test success.
Enter fullscreen mode Exit fullscreen mode

Async Tests

Your test method can also return Promise and it unit test engine will await for the result.


@Category("Async Test 1")
export default class TestCase1 extends TestItem {

    @Test
    public async asyncTestOne() {
        const p = method that returns Promise ....
        await p;
    }

}

Enter fullscreen mode Exit fullscreen mode

Global Separation

Each unit test executes in a separate Global context in Node, this isolates every unit test method. Even same test methods in same class do not share same context.


declare var global: any;

export default class TestCase1 extends TestItem {

    @Test
    public async parseTest() {
        global.a = 1;
        await Atom.delay(100);
        Assert.equal(1, global.a);
    }

    @Test
    public async parseTest2() {
        global.a = 2;
        await Atom.delay(50);
        Assert.equal(2, global.a);
    }

}
Enter fullscreen mode Exit fullscreen mode

Both tests will be successful.

How to test ViewModel?

Since AtomViewModel requires dependency injection, we have created a class AtomTest which sets up dependency injection with Mock Service Container.

This class contains app and it has a method waitForPendingCalls which allows you to wait for all pending initializations.

class TestVM extends AtomViewModel {

   public movies: IMovie[];

   public search: string = null;

   @Inject private movieService: MovieService;

   /** This will be executed when vm is initialized and it will
    * be executed when search will be updated 
    */
   @Load({ init: true, watch: true })
   public async loadItems(ct: CancelToken) {
      const s = this.search;
      this.movies = await this.movieService.loadMovies(s, ct);
   }

}

export default class TestVMCase extends AtomTest {

    @Test
    public async vmTest() {
        // this waits till loading of vm is complete
        const vm = await this.createViewModel(TestVM);
        Assert.equals(5, vm.movies.length);

        // lets update search
        vm.search = "a";

        // lets wait for few milliseconds
        // and we assume that mock will populate
        // results...
        await Atom.delay(100);
        Assert.equals(1, vm.movies.length);
    }

}

Enter fullscreen mode Exit fullscreen mode

Mocks

Every dependency can specify mock as shown below.

@DISingleton({ mock: "./mocks/MockMovieService" })
export default class MovieService extends BaseService {

    @Get("/api/movies")
    public async loadMovies(
        @Query("search") search: string,
        ct: CancelToken) {
        return null;
    }
}
Enter fullscreen mode Exit fullscreen mode

./Mocks/MockMovieService

export default class MockMovieService {

    public async loadMovies(
        search: string,
        ct: CancelToken) {
        // create artificial delay...
        await Atom.delay(1);
        if (ct.cancelled) throw new Error("cancelled");
        return [
           ... movies 
        ];
    }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)