Archive

Posts Tagged ‘JavaScript’

Pisanie testów w JavaScript

10/02/2013 Dodaj komentarz

JavaScript potrafi być upierdliwe ;).
Pomijając problem z manipulacja DOM, ciężko się tworzy w tym kod, który możesz od razu sprawdzić czy debgować. Teraz trochę się to zmieniło, porównując to do czasów kiedy zaczynałem się poważniej zajmować JavaScriptem: przykładowo mamy FireBug_a i inne dodatki do przeglądarek, które ułatwiają nam zrozumienie co się dzieje w kodzie.
Jednak problemy typu: brak średnika lub sama literówka w nazwie zmiennej są najczęściej odkrywane w przeglądarce kiedy uruchamiamy daną stronę i widzimy że wyrzucane są wyjątki. Taki błędy odkrywane są powoli.

Rozwiązanie tych problemów może być pisanie testów. Wymaga to jednak pewnego zachodu np. porządku w plikach js, tak aby nie ciągnąć za sobą zbyt dużo zależności. Do końca sam nie wiem jak się do tego dobrze zabrać.
Sposób jaki postaram się tu przedstawić jest dobry do testowania prostych koncepcji lub nauki JavaScriptu.

Wszystko zaczyna się od narzędzia jakim jest js-test-driver. Pod podanym linkiem jest strona tego narzędzia, a na niej filmik prezentujący sposób pracy z nim (polecam go obejrzeć, to co opiszę dalej w dużym stopniu opera się o to co zostało tam pokazane). Używanie tego wymaga pewnego zachodu z ustawieniem środowiska pracy, tak aby testować kod we wskazanych przeglądarkach.

Tutaj jest dobre miejsce do startu, są tu przykładowe testy wraz ze wszystkim co jest potrzebne do uruchomienia, Potrzebne pliki (jary) można jednak pobrać z innego miejsca. Także idąc za podanym przykładem, testy będę pisał w oparciu o ten sam problem „gra w życie” (szczegóły).

Jak uruchomić serwer testów
A więc zaczynajmy od uruchomienia serwera, należy mieć odpowiedni jar (tutaj jest to JsTestDriver-1.3.5.jar). Serwer można uruchomić poniższą komendą:

java -jar JsTestDriver-1.3.5.jar --port 9876

Lub za pomocą skryptu, zawierającą tą komendę. Jest to najprostsza konfiguracja gdzie jedynym istotnym parametrem jest port pod którym będzie dostępny serwer. Po wystartowaniu serwera należy otworzyć przeglądarkę, w której chce się testować (można testować jednocześnie pod kilkoma), i podać adres:

http://localhost:9876

Następnie kliknąć link „Capture This Browser” i przeglądarka jest przechwycona do testów.

Jak uruchomić testy
Testy uruchamia się podobną komendą, choć jest ona trochę bardziej skomplikowana. U mnie upchnięta do pliku bat wygląda tak:

set BASE_DIR=D:\game-of-life
java -jar "%BASE_DIR%\JsTestDriver-1.3.5.jar" ^
--config "%BASE_DIR%\jsTestDriver.conf" ^
--basePath "%BASE_DIR%" ^
--tests all --reset

W podanym przykładzie wszystkie pliki są w tym samym katalogu. Plik jsTestDriver.conf z konfiguracją mówiący o tym co ma być wczytane podczas uruchamiania testów i pod jakim adresem działa serwer testów. Wygląda on tak:

server: http://localhost:9876

load:
- js/jasmine.js
- js/JasmineAdapter.js
- http://underscorejs.org/underscore-min.js
- game-of-life.js
- game-of-life-tests.js

Kończąc ten opis jak uruchomić testy.
Całość kodu jest dostępna tutaj. A sama symulacja życia może być podziwiana tutaj. Fajny sposób do dzielenia się kawałkami kodu ;). Można na żywo edytować i sprawdzać jak zmiany się zachowują. Polecam pobawić się zmianą reguł życia.

Kategorie:Inne Tags: ,

Jak szybko stworzyć aplikacje webową, która zwraca JSON-a

16/07/2012 Dodaj komentarz

Ostatnio sprawdzałem możliwości JavaScriptu w aplikacjach webowych. Tutaj udział część serwerowej sprowadzał się do obsługi wywołań AJAX-owych (zwracania wyników w JSON-e). W skrócie nie chciałem się zajmować serwerem, wystarczało że zwracał mi jakiś kawałek JSON-a, tak abym mógł skupić się tylko na HTML, CSS i JS.

Bez Javy, ale tak bym chciał
Tak więc na początek trafiłem na takie całkiem fajne rozwiązanie w pythonie.

python -m SimpleHTTPServer 8080

Jak ma się zainstalowanego pythona, to jest to najprostszy sposób na postawienie serwera HTTP. Gdzie wystawioną zawartością, poprzez URL, jest to co znajduje się w katalogu, w którym z linii poleceń uruchomiło się to. Tak więc jeśli mamy tam katalog
dane, a w nim plik test.json, to możemy do tego pliku odwołać się poprzez taki url:

http://localhost:8080/dane/test.json

Zatem statyczne kawałki JSON-a leżą sobie w plikach na dysku, obok może znajdować się plik app.js, który odwołuję się do nich, i który to jest głównym przedmiotem pracy. Całość mogę edytować po dowolnym narzędziem, i każda zmiana od razu jest widoczna w przeglądarce (wystarczy F5). Piękne 🙂 nie trzeba się martwić synchronizacją z serwerem, można się skupić na pracy i od razu widzieć efekty w przeglądarce.
Rozwiązanie to daje jednak tylko statycznego JSON-a. Aby wprowadzić więcej dynamizmu trzeba jednak napisać trochę po stronie serwera. Nie patrzyłam już czy SimpleHTTPServer oferuję jakieś większe możliwość przeszedłem od razu do javy, w końcu jak na razie w tym głównie piszę.

Z Javą, próba odtworzenia
Zacząłem od stworzenia prostego projektu mavenowego, tak aby móc łatwo zarządzać zależnościami, po za tym i tak zwykle jest to projekt tego typu. Idą za wskazówkami zawartymi w tym poście wybrałem SpringMVC. Cała moja praca po stronie serwera ograniczyła się do stworzenia jednej klasy, która produkowała mi JSON-a jakiego chciałem.

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/app")
public class AppController {
	
	private static List<String> names = new ArrayList<String>();
	
	@RequestMapping(value = "/add", method = RequestMethod.GET)
	public @ResponseBody void add(@RequestParam(value="name") String name){
		names.add(name);
	}
	
	@RequestMapping(value = "/listAll", method = RequestMethod.GET)
	public @ResponseBody List<String> listAll(){
		return names;
	}
	
	@RequestMapping(value = "/remove", method = RequestMethod.GET)
	public @ResponseBody void remove(@RequestParam(value="name") String name){
		names.remove(name);
	}

}

Dzięki temu dostałem trzy url-e, z których mogłem korzystać po stornie JavaScript:

http://localhost:8080/RestTest/rest/app/add?name=dwa
http://localhost:8080/RestTest/rest/app/remove?name=dwa
http://localhost:8080/RestTest/rest/app/listAll

Ostatni może zwracać takiego JSON-a:

["jeden","dwa","trzy"]

Takie coś w zupełności wystarcza do symulowania logiki po stronie serwera, a jak widać w przedstawionej klasie, rozbudowanie tego nie powinno być trudne.
Oprócz tej klasy potrzebne były jeszcze techniczne pliki, wszytko wziąłem z projektu w przytoczonym poście.
Tak wiec w web.xml potrzebne są takie wpisy aby postawić kontekst Springa.

	<servlet>
		<servlet-name>mvc-dispatcher</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>mvc-dispatcher</servlet-name>
		<url-pattern>/rest/*</url-pattern>
	</servlet-mapping>

	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value>
	</context-param>

	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

Najistotniejsze jest zwrócenie skąd się bierze rest w URL (RestTest to nazwa mego wara), a plik mvc-dispatcher-servlet.xml opisujący co Spring ma robić jest trywialny:

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
        http://www.springframework.org/schema/beans     
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

	<context:component-scan base-package="test.controller" />

	<mvc:annotation-driven />

</beans>

jak widać jedyne co trzeba dodać to wpis włączający interpretacje adnotacji w napisanej klasie. Do dopełnienie wszystkiego jest jeszcze plik mavena, który zaprezentuje na końcu.

Jest Java, ale jakby krok wstecz
Tak więc miałem już dynamicznego JSON-a, i sposób jak go dostosowywać. Mogłem się zabrać za JavaScript. Jednak utraciłem tutaj szybką pętle zwrotną, w weryfikacji wprowadzanych zmian. Teraz pomiędzy zapisaniem zmiany w pliku, a odświeżeniem widoku w przeglądarce, doszedł krok budowy wara i wgrania go na Tomcata lub inny WebSerwer. A ja chciałem tylko zmieniać pliki JS lub HTML, część serwerowa nie zmieniałaby się w ogóle.

Z Javą, już prawie idealnie
Rozwiązań tego pewnie jest parę. Ja zwróciłem się w kierunku wbudowanego kontenera aplikacje webowych, czyli użyłem jetty.
W pom.xml pojawia się taki wpis jak niżej. Jetty uruchamia się taka komendą mvn jetty:run.

			<plugin>
				<groupId>org.mortbay.jetty</groupId>
				<artifactId>maven-jetty-plugin</artifactId>
				<version>6.1.24</version>
				<configuration>
					<stopPort>9966</stopPort>
					<stopKey>RestTest</stopKey>
					<!-- Redeploy every x seconds if changes are detected, 0 for no automatic redeployment -->
					<scanIntervalSeconds>0</scanIntervalSeconds>
					<!-- make sure Jetty also finds the widgetset -->
					<webAppConfig>
						<contextPath>/RestTest</contextPath>
						<!-- -->
						<baseResource implementation="org.mortbay.resource.ResourceCollection">
							<resources>src/main/webapp</resources>
						</baseResource>
					</webAppConfig>
				</configuration>
			</plugin>

Ważny jest parametr scanIntervalSeconds, mówi on o tym czy zmiany w naszych klasach będą automatycznie redeployowane. Trwa to trochę, ale można z tym żyć. To i tak nie dotyczy naszego HTML-a i JS, to co powoduję że zmiany w tych plikach są widoczne od razu w przeglądarce, to parametr baseResource. Wskazanie na katalog webapp, gdzie trzymane są te pliki powoduje, że zmiany są propagowane (wykomentowałem ten parametr i funkcjonalność ta nadal działało co pewnie świadczy o tym że jest to wartość domyśla). Nie wczytywałem się za bardzo w szczegóły ustawień i może trochę podorabiałem teorii do praktyki, ale działa tak jak chciałem. Mam co chciałem i mogę spokojnie pracować nad plikami JS, i zmiany w nich widzieć od razu w przeglądarce.

Taki mały zgrzyt
Pojawia się zgrzyt, podobno tylko dla Windowsa, pliki uaktualniają się tylko za pierwszym razem, kolejny raz nie można już zapisać, bo są blokowane. Ten wpis opisuje problem i możliwe rozwiązania. Ja użyłem dodatkowego wpisu w web.xml, na razie nie martwię się tym że brudzi to ten plik. Dodatkowy element to:

	<servlet>
	    <servlet-name>default</servlet-name>
	    <servlet-class>org.mortbay.jetty.servlet.DefaultServlet</servlet-class>
	    <init-param>
	      <param-name>useFileMappedBuffer</param-name>
	      <param-value>false</param-value>
	    </init-param>
	    <load-on-startup>0</load-on-startup>
	  </servlet>

I to wszytko, można się zabrać za JavaScript. Teraz mam szybką odpowiedź zwrotną w przeglądarce, wiec jak dla mnie bardzo dobre warunki do pracy z tak krnąbrny językiem 😉 , o czym może napisze następnym razem.

A całość pom.xml prezentuję się tak:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>test</groupId>
	<artifactId>RestTest</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>

	<properties>
		<spring.version>3.0.5.RELEASE</spring.version>
	</properties>

	<dependencies>

		<!-- Jackson JSON Mapper -->
		<dependency>
			<groupId>org.codehaus.jackson</groupId>
			<artifactId>jackson-mapper-asl</artifactId>
			<version>1.7.1</version>
		</dependency>
		<!-- Spring 3 dependencies -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
		</dependency>

	</dependencies>

	<build>
		<finalName>RestTest</finalName>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.mortbay.jetty</groupId>
				<artifactId>maven-jetty-plugin</artifactId>
				<version>6.1.24</version>
				<configuration>
					<stopPort>9966</stopPort>
					<stopKey>RestTest</stopKey>
					<!-- Redeploy every x seconds if changes are detected, 0 for no automatic 
						redeployment -->
					<scanIntervalSeconds>0</scanIntervalSeconds>
					<!-- make sure Jetty also finds the widgetset -->
					<webAppConfig>
						<contextPath>/RestTest</contextPath>
						<!-- -->
						<baseResource implementation="org.mortbay.resource.ResourceCollection">
							<resources>src/main/webapp</resources>
						</baseResource>
					</webAppConfig>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

Małe uaktualnienie: taki sam efekt z wbudowanym kontenerem aplikacji webowych można uzyskać z tomcat-maven-plugin. Nie ma się tylko przeładowania klas (w każdym razie nie znalazłem tego). Ale nie trzeba brudzić swego pliku web.xml i nie ma problemu z blokowaniem edytowanych plików. Tak więc w pomie zamiast jetty należy dodać:

			
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>tomcat-maven-plugin</artifactId>
				<version>1.1</version>
			</plugin>

i uruchomić z linii poleceń komendą: mvn tomcat:run

Proxy w JavaScript

25/05/2011 Dodaj komentarz

Trafiłem ostatnio na ciekawe użycie Proxy w JavaScript. Wcześniej nie myślałem że można tak robić. Pisałem (pisze ciągle) bardziej w skryptowy (proceduralny) sposób w tym języku. Jednak JavaScript jest bardziej elegancki w wyrażaniu intencji jakie kryją się za pojęciem proxy.
A więc w kodzie wygląda to tak, prosta funkcja i następna która tworzy proxy do pierwszej:

function executeAlert(){
  alert('powiedz w alert');
} 

function decorateExecuteAlert(){
  var oldExecuteAlert = executeAlert;
  executeAlert = function() {
    alert('przed alert');
    oldExecuteAlert();
  };
} 

Albo jeszcze lepiej można zmienić samego alerta:

function changeAlert(){
  var oldAlert = alert;
  alert = function(value) {
    oldAlert('>'+value+'<');
  };
} 

Jak już zasugerowałem to nazwą drugiej funkcji, jest to też rodzaj dekoratora. Ogólnie taka konstrukcja oferuje duże możliwości. Ja na nią trafiłem idąc za potrzebą stworzenia obserwatora zdarzeń wywołania danej funkcji. Dzięki temu można czasowo dodać (podpiąć się) coś do danej funkcji, a potem usunąć to. Można też zmodyfikować działanie i dodać warunek kiedy właściwa funkcja ma się wykonywać, lub podpiąć dodatkowe logowanie do wywołań jej. Możliwości jest wiele :).

Kategorie:Inne Tags: