DEV Community

jtenner
jtenner

Posted on • Updated on

Testing with AssemblyScript, and the Usefulness of Value

AssemblyScript is not your ordinary TypeScript compiler. It compiles TypeScript to Web Assembly, which makes it a new and exciting piece of technology that will revolutionize how developers solve CPU intensive tasks in node.js and the browser. It makes well-formatted TypeScript code very fast. I've become a very early adopter of this language, and I proudly place it on my resume right next to my TypeScript experience. When employers ask what this language means, I tell them that I have the power to bring a binary compiled version of TypeScript to the table as an option.

GitHub logo AssemblyScript / assemblyscript

Definitely not a TypeScript to WebAssembly compiler 🚀

AssemblyScript logo

Test status Publish status npm compiler version npm loader version Discord online

AssemblyScript compiles a strict variant of TypeScript (basically JavaScript with types) to WebAssembly using Binaryen. It generates lean and mean WebAssembly modules while being just an npm install away.

About  ·  Introduction  ·  Quick start  ·  Development instructions

Contributors

Contributor logos

Thanks to our sponsors!

Most of the core team members and most contributors do this open source work in their free time. If you use AssemblyScript for a serious task or plan to do so, and you'd like us to invest more time on it, please donate to our OpenCollective. By sponsoring this project, your logo will show up below. Thank you so much for your support!

Sponsor logos

One of the problems this language currently faces is getting support from the community which is currently growing, but slowly. It also has a lack of standardized tooling. Without a proper toolchain, a solid testing framework, and the hard work of many individuals coming together, it's very difficult for new languages to get traction. Thankfully, @dcodeIO and @MaxGraey are two very dedicated and passionate developers. With any luck, AssemblyScript will continue to grow under their leadership.

You can follow the primary creator of AssemblyScript here:

dcode image

One of the more recent contributions I wanted to develop is called as-pect which is located at https://github.com/jtenner/as-pect.

GitHub logo jtenner / as-pect

🔥Blazing🔥 fast testing with AssemblyScript

jtenner/as-pect

This package is a monorepo that contains the cli and the core for the @as-pect packages.

Greenkeeper badge Build Status Coverage Status lerna

Write your module in AssemblyScript and get blazing fast bootstrapped tests with WebAssembly speeds!

Documentation

To view the documentation, it's located here on the gitbook. If there are any issues with the docs, please feel free to file an issue!

Contributors

To contribute please see CONTRIBUTING.md.

Thanks to @willemneal and @MaxGraey for all their support in making as-pect the best software it can be.

Other Contributors:

Special Thanks

Special thanks to the AssemblyScript team for creating AssemblyScript itself.

It's a configurable command-line tool that enables developers to test AssemblyScript modules using Web Assembly, instead of requiring that developers mock their modules in frameworks like jest and Jasmine. The need for testing tools like jest is immeasurable at this point, and it's obvious that none of these tools can be replaced for testing JavaScript. However, asking tools like jest to verify web assembly application state can become cumbersome, and the only viable alternative is to compile a single test module yourself. This is especially true even if you only want to test a single hot CPU-bound code path in your project.

Instead, it's wiser to explore a framework to help with these sorts of problems. So, without further ado, let's explore how we can use as-pect to jump start your Web Assembly development.

Getting Started With as-pect

The first thing I recommend to literally everyone around me is this: add a testing suite to your project! Spend the time to verify that the tests even run in the first place. You owe it to yourself, and to your users, to set up and clean off your tool-bench before you use it to build your idea.

As for AssemblyScript, the following commands are a great way to bootstrap yourself into a testing environment.

You will notice that some files are created in your project when using the --init flag. These are:

The as-pect.d.ts file is a collection of the global functions provided to you in your AssemblyScript test modules. These functions include the expect(), test(), it() and describe() functions you've come to know when doing test-driven development in JavaScript. This types file is kept up to date with commented documentation so that your development environment can give you tips and usage information directly next to the function call itself inline in your development environment.

The next file is the as-pect.config.js file which contains a set of defaults I personally felt were useful. These options should feel similar to how jest is configured because as-pect was designed to be a spiritual successor to it. All the properties are commented, and anyone is free to open an issue at the GitHub repo to ask questions about how they work.

Also, an example test is copied to the assembly/__tests__/ directory, which should be a short and sweet introduction to how tests could be structured within your project.

The expect function is typed with a generic parameter T, which must be provided for the compiler to understand how it should handle the Expectation. This syntax might be familiar to those who use TypeScript to transpile their tests using ts-jest and doesn't take very long to get used to.

When an expectation fails, there is a developer provided "optional message" that is displayed on the screen next to the test description to aid yourself in finding which part the failing test does not pass. However, having access to the failing test information isn't the only important part about having tests. For instance, having a solid test structure can help others find the most important parts of your code and display the edge cases front and center in your project.

Having some kind of test structure is great for many reasons, and as-pect gives you many tools to help you put things in their place, so others can find them. Often, developers will rely on a set of conventions to understand the code you wrote. In testing, most developers look for the describe() function calls in a test suite to understand the top level things that are important about your software.

describe()

This function literally describes a block of tests that can be nested to help logically group sets of expectations by what they test.

Testing a library with class Foo that contains methods: bar() and baz() might have three describe blocks in total.

Note that #foo() and #bar() have their own describe blocks within the Foo class block. This is for organization reasons, including:

  1. Users can now find examples faster
  2. Things are logically grouped together
  3. and, it reads like a specification

The inner describe("#bar") and describe("#baz") blocks are executed first, then finally the outer block is executed.

Describe blocks are the top level unit of test structure, but these blocks must contain a set of tests to go with them.

test() and it()

These functions describe each test that will be run by the asp command-line program inside a describe() block. The it() function is an alias for test(), so these two functions are treated exactly the same way.

Often, developers don't know that the it() function can be useful. Aside from being a shorter identifier, the it() function enables developers to use natural language in their code to describe the meaning of a test to others.

Having self-documenting code is something that developers could have that just makes things clearer. In no way does self-documenting code replace actual code documentation, however, it helps to make things more understandable. In the case of tests, it's nice to stop writing algorithms and code something that remotely resembles a natural language expression of truth. It often describes a meaningful expect()ation of your software.

expect()

The expect().[assertion]() functions are the primary unit of testing and validating application state. They use the built-in assert() function provided by the AssemblyScript std library to generate an unreachable() web assembly instruction that halts code execution because something is wrong.

This is a place where as-pect deviates from testing frameworks like jest in syntax. The assert() function accepts a message parameter and can help report a description of the Expectation that failed.

This is the closest thing we have as developers to "Error Messages" in AssemblyScript while using as-pect. This will notify the JavaScript host that something is wrong about the state of the program, where the problem might exist, and hopefully can help you discover why.

Just about the only bad part about testing AssemblyScript modules is asserting that runtime errors are thrown when they need to be thrown. For this, we can create a special test that is supposed to error and verify that code execution throws() an error.

throws()

The as-pect test suite has a special function that wraps a WebAssembly function inside a Javascript try/catch block and returns 1 if the function executed without any problems.

In the case of the throws() function, it creates a test that describes a condition where your code should throw an error. Here is an example.

The alternative to this is to write a test() and use an expectation with the expectFn() function.

The expectFn() function is just a shorthand for expect<() => void>(callback), and returns an expectation, just like the regular expect() function.

Testing Module State Using expect()

The easiest way to test the application state is to verify that a pointer is the same pointer or that a value matches another value. To do this, use the #toBe function.

This is the simplest comparison but does not scale when comparing to mocked reference values.

Comparing the values of two references using a memory.compare(), use the expect().toStrictEqual() function. In jest using JavaScript, this function will perform a JavaScript Object comparison. Testing frameworks in dynamic languages usually don't have access to direct memory and must compare the properties of the reference values.

In AssemblyScript, this cannot be done (yet.) Instead, this is delegated to memory.compare().

In order to verify that child references strictly equal each other, those nested references need to be memory.compare()d directly using a custom compare function.

As-pect's Current Usage

I have had the absolute pleasure of working with @sirwillem on his WebAssemblyOS project. One of the cornerstones of that project was a heavy reliance on as-pect to develop a virtual operating system and teach his students how to test program output.

He had this to say:

"Working with AssemblyScript and WebAssembly can be daunting at first. There isn’t even a standard way to do a “hello world.” With its builtin log function as-pect makes it easy to get started and makes debugging your code a breeze. It was instrumental in teaching students how to write in AssemblyScript and how to test their programs"

sirwillem image

I met some very amazing people online in the AssemblyScript community too. The development team at NEARProtocol recently started using as-pect to test their Smart Contract APIs.

The user @BowenWang18 was one of the first developers who picked up as-pect and ran with it.

He said this:

"At Near Protocol, we use as-pect to test our smart contract APIs written for AssemblyScript. It makes my development process much more streamlined because as-pect provides a very smooth experience for testing. It nearly eliminated all of the boilerplate code otherwise needed to hook up a reasonable testing framework. It just works."

You can follow a recent pull request that was created by @sirwillem for JSON parsing here that is actively using as-pect as a real-world example of how to use it.

My Personal Takeaway

There is something special about being confident in the things you create. For creative types, the little dopamine boost you get when you make something that is used by people is worth every ounce of energy you put into your work. The same creative types, like art students in art school, should learn that meaningful constraint of creativity leads directly to more wildly successful ideas and outcomes. Tools created with these sorts of limitations redefine how we work in our world every day.

This is something that we all strive to achieve as open source developers, so when an opportunity arises to prove ourselves, we should jump at it with everything we have. If we truly value the success of the software we write, we should respect ourselves enough to write software that we are proud of.

Personally, the biggest takeaway I had on my journey making as-pect was that there's a huge story waiting to be told. If you work hard enough you could be the first chapter in someone else's success. Have enough self-respect to constrain your own development process meaningfully so that you never let yourself feel awful about making software again.

Discussion (0)