
Несколько недель назад компания Hexacta попросила меня разработать приложение для студентов-компьютерщиков. Это приложение планировалось использовать на съезде, где у Hexacta был стенд. Там мы рассказывали о себе и связывались с молодыми людьми, которые хотят делать такие же крутые штуки, как и мы :)
Идея была проста: «давайте создадим веб-приложение, в котором они смогут решать некоторые задачи программирования». Первое, о чем я подумал, было: «Было бы круто, если бы им представили разные функции для реализации, но было бы круче, если бы им дали несколько пробных тестов, чтобы объяснить, как функция должна работать помимо спецификации. Если бы мы могли сделать так, чтобы они могли добавлять свои тесты и запускать их, это было бы очень здорово. А как насчет того, чтобы дать им оценку за выполнение скрытых тестов для каждого упражнения, это определенно было бы очень приятно…»
Я был готов принять вызов, а потом спросил, сколько у меня есть времени… Всего неделя!
Это было что-то действительно крутое, поэтому я сказал себе, что сделаю все возможное, чтобы это произошло, но не было никаких гарантий, было мало времени, и я начал.
Я начал, как всегда, гуглить, чтобы посмотреть, есть ли что-то, что я мог бы использовать с пользой. Мне просто нужно было запустить тесты JavaScript в браузере, но это был не тот тест, в котором у вас есть функции и утверждения. Это были тесты, где ваша реализация является строкой, и ваши тесты тоже являются строками!!!
Очень вероятно, что вы сейчас спрашиваете себя, о чем, черт возьми, говорит Нико?! Помните, я сказал, что пользователь должен реализовать функцию? Может быть, использовать какой-нибудь редактор в качестве Monaco (тот, который поддерживает Visual Studio Code)? Вы также помните, что я сказал, что пользователь также может добавить больше тестов в образцы? Струны и струны, теперь вы идете за мной, мне легче.
Мне нужно было за одну неделю реализовать тестовую среду, которая оценивала бы JavaScript как строки, а также делала бы приложение. Явно нельзя было терять время!!!
У меня был опыт работы с vm-модулем Node, и я сказал: Мне нужно что-то подобное. https://github.com/browserify/vm-browserify был ответ.
Тогда я сказал, что мне нужны некоторые утверждения, но я не хочу изобретать велосипед. Может быть, я смогу найти что-то вроде модуля assert для Node, верно? И снова мне посчастливилось найти https://github.com/browserify/commonjs-assert.
Я действительно должен поблагодарить команду Browserify за единственные две зависимости, которые есть у Chocolatest, без них у меня бы не хватило времени!
Теперь, когда у меня были строительные блоки, мне понадобился способ перехвата вызовов различных методов assert и создания журнала того, что происходило во время выполнения тестов.
Слово «перехват» заставило меня задуматься об аспектно-ориентированном программировании, но все мы знаем, что в JavaScript нет всех этих причудливых вещей! Что у нас есть, так это метапрограммирование благодаря объекту Proxy.
Большая часть волшебства происходит в следующих строках.
import { Assertion } from './types';
import * as assert from 'assert';
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
const applyWrapper = (operator: string, method: (...args: any[]) => any, thisArg: any, args: any[], log: (assertion: Assertion) => void) => {
try {
let result = method.apply(thisArg, args);
log({'ok': true, 'operator': operator, 'args': args, 'type': 'assert'});
return result;
} catch (e) {
log({'ok': false, 'operator': operator, 'args': args, 'type': 'assert'});
// we don't want to fast fail, we want to run all asserts in test
// throw e;
}
}
const generateProxy = (log: (assertion: Assertion) => void) => {
return new Proxy(assert,
{
apply(target, thisArg, args) {
return applyWrapper('assert', target, thisArg, args, log);
},
get(target, propKey: keyof Omit<typeof assert, 'AssertionError'>) {
const origMethod = target[propKey];
return function (...args: any[]) {
return applyWrapper(propKey, origMethod, target, args, log);
};
}
});
}
export {
generateProxy
}
Прежде всего, вы заметите, что код написан на TypeScript. Если вы еще не пробовали, то советую вам это сделать.
Во-вторых, я уверен, вы заметили создание прокси библиотеки assert, где написано «новый прокси(assert, …» и опять же вы, наверное, сказали, какого черта, Нико?!
По сути, я просто перехватываю все вызовы методов assert (даже сам assert, который тоже является методом) и вызываю исходный метод изолированно внутри блока try catch. С помощью этого перехвата я могу регистрировать, когда метод вот-вот будет вызван, когда он был вызван и выдал исключение из-за исключения или когда он был успешно выполнен.
Вызывается метод «generateProxy» с логом в качестве аргумента, это функция-коллектор, куда я буду распределять все описанные выше события и еще некоторые.
Теперь, когда я объяснил вам фреймворк, я дам вам ссылку на случай, если вы захотите поиграть с ним https://github.com/nicoabie/chocolatest и расскажу вам о том, что случилось с приложением.
Приложение было разработано на Vuejs с использованием редактора Monaco, чтобы упростить процесс кодирования, и к съезду было подготовлено несколько упражнений. Съезд начался в 9 утра, а последний коммит был около 9:20 или около того. Я не буду хвастаться его успехом, потому что, в конце концов, ни один студент не использовал его, хахаха! Вокруг гуляло так много людей, что было нелегко остановиться на стенде, и, видимо, они открывали университет, поэтому, к сожалению, они были немного застенчивы.
В любом случае, мы в Hexacta думаем о том, чтобы опубликовать его на нашем веб-сайте, чтобы вы могли выполнить несколько упражнений и проверить свой уровень!!!
Спасибо за чтение и следите за обновлениями!
P.S. Недавно я узнал, что по данным http://www.modulecounts.com/ каждый день на npm публикуется около 500 модулей. Я просто хочу, чтобы вы знали, что прежде чем я начал кодировать, я довольно долго искал, существует ли такая структура, чтобы не загрязнять нашу среду еще больше, хахаха.