New article: āE2E Testing with TestCafe on Docker.ā
Iāll show you how to get started with TestCafe, a framework for performing E2E tests.
https://programadorwebvalencia.com/pruebas-e2e-con-testcafe-sobre-docker/
#docker #testcafe #e2e #testing #javascript #webdev
Today I learned how to use TestCafƩ. It is a E2E framework.
I needed it because I wanted to write a script that would launch a browser in the background, log me in with a username and password, and return the cookie value with the token ID. The goal is to perform tests with the token.
https://testcafe.io/
#testing @ Hi Bob, nice to meet you!
Thanks, @xuu@txt.sour.is, great explanation. In another project Iāve structured it exactly like you wrote. The mock storage over there extends the SQLite storage and provides mechanism to return errors and such for testing purposes:
- storage/ defines the interface
- sqlite/ implements the storage interface
- mock/ extends the SQLite implementation by some mocking capabilities and assertions
- sqlite/ implements the storage interface
Here, however, there are no storage subpackages. Itās just storage
, thatās it. Everything is in there. The only implementation so far is an SQLite backend that resides in storage
. My RAM storage is exactly that SQLite storage, but with :memory:
instead a backing file on disk. I do not have a mock storage (yet).
I have to think about it a bit more, but I probably have to do exactly that in my tt
rewrite, too. Sigh. I just have the feeling that in storage/sqlite/sqlite_test.go I cannot import storage/mock for the helper because storage/mock/mock.go imports and embeds the type from storage/sqlite. But Iām too tired right now to think clearly.
@lyse@lyse.isobeef.org OK. So how I have worked things like this out is to have the interface in the root package from the implementations. The interface doesnāt need to be tested since itās just a contract. The implementations donāt need to import storage.Storage
- storage/ defines the
Storage
interface (no tests!)
- storage/sqlite for the sqlite implementation tests for sqlite directly
- storage/ram for the ram implementation and tests for RAM directly
- storage/sqlite for the sqlite implementation tests for sqlite directly
- controller/ can now import both storage and the implementation as needed.
So now I am guessing you wanted the RAM test for testing queries against sqlite and have it return some query response?
For that I usually would register a driver for SQL that emulates sqlite. Then itās just a matter of passing the connection string to open the registered driver on setup.
https://github.com/glebarez/go-sqlite?tab=readme-ov-file#connection-string-examples
@xuu@txt.sour.is My layout looks like this:
- storage/
- storage.go: defines a
Storage
interface
- sqlite.go: implements the
Storage
interface
- sqlite_test.go: originally had a function to set up a test storage to test the SQLite storage implementation itself:
newRAMStorage(testing.T, $initialData) *Storage
- storage.go: defines a
- controller/
- feeds.go: uses a
Storage
- feeds_test.go: here I wanted to reuse the
newRAMStorage(ā¦)
function
- feeds.go: uses a
I then tried to relocate the newRAMStorage(ā¦)
into a
- teststorage/
- storage.go: moved here as
NewRAMStorage(ā¦)
- storage.go: moved here as
so that I could just reuse it from both
- storage/
- sqlite_test.go: uses
testutils.NewRAMStorage(ā¦)
- sqlite_test.go: uses
- controller/
- feeds_test.go: uses
testutils.NewRamStorage(ā¦)
- feeds_test.go: uses
But that results into an import cycle, because the teststorage
package imports storage
for storage.Storage
and the storage
package imports testutils
for testutils.NewRAMStorage(ā¦)
in its test. Iām just screwed. For now, I duplicated it as newRAMStorage(ā¦)
in controller/feeds_test.go.
I could put NewRAMStorage(ā¦)
in storage/testutils.go, which could be guarded with //go:build testutils
. With go test -tags testutils ā¦
, in storage/sqlite_test.go could just use NewRAMStorage(ā¦)
directly and similarly in controller/feeds_test.go I could call storage.NewRamStorage(ā¦)
. But I donāt know if I would consider this really elegant.
The more I think about it, the more appealing it sounds. Because I could then also use other test-related stuff across packages without introducing other dedicated test packages. Build some assertions, converters, types etc. directly into the same package, maybe even make them methods of types.
If I went that route, I might do the opposite with the build tag and make it something like !prod
instead of testing. Only when building the final binary, I would have to specify the tag to exclude all the non-prod stuff. Hmmm.
maybe even an internal that has the shared test stuff
Dang it! I ran into import cycles with shared test utilities again. :-( Either I have to copy this function to set up an in-memory test storage across packages or I have to put it in the storage package itself and guard it with a build tag that is only used in tests (otherwise I end up with this function in my production binary as well). I donāt like any of the alternatives. :-(
HeliBoard might be the first one of these fully open source Android keyboards, that doesnāt suck, idk, Iām still in the process of testing it, but I already like it a lot more than any of the ones I used before it.
Setting it up was somewhat clunky, but once you set it all up and dile in the settings, the keyboard itself, feels really great to use.
Amd of course, TDD! I tried that, but it doesnāt work all that great for me in its strict form. I have the feeling that coming up with a single new failing test, making it pass, maybe some refactoring, rinse and repeat wastes significantly more time than doing it in ā what they call ā the ābundleā approach. Coming up with several tests in advance and then writing the code or vise versa is usually much quicker. I do find that more enjoyable, it also helps me to reduce smaller context switches. I can focus on either the tests or the production code.
As for the potentially reduced code coverage with a non-TDD approach, I can easily see which parts are lacking tests and hand them in later. So, thatās largely a specious argument. Granted, I can forget to check the coverage or simply ignore it.
I agree with John, TDD results in less elegant code or requires more refactoring to tidy it up. Sometimes, itās also not entirely clear at the beginning how the API should really look like. It doesnāt happen often, but it does happen. Especially when experimenting or trying out different approaches. With TDD, I then also have to refactor the tests which is not only annoying, but also involves the danger of accidentally breaking them.
TDD only works really well, if you have super tiny functions. But we already established that I typically donāt like tiny methods just for the purpose of them being extremely short.
When fixing a bug, I usually come up with a failing test case first to verify that my repaired code later actually resolves the problem. For new code, it depends, sometimes tests first, sometimes the productive code first. Starting off with the tests requires the API to be well defined beforehand.
@andros@twtxt.andros.dev Just before the pandemic, we watched Uncle Bob videos once a week in the lunch break. While almost all of my old teammates agreed with his views, I partially found them to be very odd and even counterproductive.
I didnāt come across John Ousterhout or any of his work before, at least not deliberately. So, this document is my first contact.
I only finished the chapter on comments and I totally agree with John so far. This document just manifests to me how weird Bobās view is on certain subjects.
I always disagreed with the concept of a maximum method length. Sure, generally, shorter functions are probably better, but it always depends. And Iāve certainly seen super short methods that just made the code flow even worse to follow. While āone function should only do one thingā is a nice general rule, Iām 100% in team John with the shown examples. There are cases, where this doesnāt help readability at all. Not even close.
To me, a function always has to justify its existence. Either by reusing it at least at another place or by coming up with dedicated tests for it. But if it is just called once and there are no tests, I almost always decide against it. Personally, I donāt mind longer methods. We just recently had a discussion about that and I lost against two other workmates who are more in Uncle Bobās camp, they refactored one medium sized method into three very short ones. Luckily, we agree on most other topics.
Lol, what!? The shorter the method, the longer the variables inside? I first thought I misread or the writeup mixed it up. Iāll always do it the other way around.
Iāve been also bitten badly by outdated comments in the past, but Bob must have worked on really terrible projects to end up with such an attitude to dislike comments. Oh well. No doubt, Iāve come across by several orders of magnitude more useless comments, in my experience (autogenerated) JavaDocs fall in the category more frequently than not. So, I know that there are different types of comments. A comment doesnāt automatically mean that it is good and justified.
But I also partially agree with Bob and John and think that a good name has a proper chance to save a comment. Though, when in doubt, I go Johnās route and use a shorter name with a comment rather than use a kilometer long identifier. Writing good comments typically takes some time, sometimes much longer than writing the code. It regularly takes me several minutes. Itās a hard art.
I perhaps should read up on Johnās work. He seems to be more reasonable and likeminded. :-) Let me continue to complete this document.
This document is the result of a series of discussions between Robert āUncle Bobā Martin and John Ousterhout, held between September 2024 and February 2025. The text addresses three main topics: method length, comments, and Test Driven Development (TDD).
https://github.com/johnousterhout/aposd-vs-clean-code/blob/main/README.md
This is something to read and reflect on for days.
Testing out this image upload featureā¦
My hypothesis about that thing breaking my twts is that it might have something to do with the parenthesis surrounding the root twt hash in the replay twt-A
when I replay to it with fork-twt-B
; I imagine elisp interpreting those as a s-expression thus breaking the generation precess of hash (#twt-A) before prepending it to for-twt-B
⦠but then Iām too ignorant to figure out how to test my theory (heck I couldnāt even recalculate the hashes myself correctly in bash xD). Iāll keep trying tho.
@andros@twtxt.andros.dev yes, that usually happens when twts get edited and we just made a gentlemen agreement to avoid edits as much as possible (at least for the time being). But the thing is, That is not whatās happening with my broken twtsā hashes. Since Iāve bee mostly replaying to my own twts as a test and I know for sure that I havenāt edited any. (I usually fork-replay instead of edit a twt when needed)
the hash in that test replay should have been s243lua
instead š¤·
@andros@twtxt.andros.dev Hereās that twtxt-el test replay to my last twt! letās see how it goes.
@andros@twtxt.andros.dev hmmm⦠pretty strange, isnāt it? replaying to threads worked perfectly, Iāve only had that problem trying to replay to a twt that was part of a thread.
As an example, this one is a Fork-Replay from Jenny. My next twt will be a replay to this exact twt but from twtxt-el as a test.
Then Iāwill file an issue if it doesnāt behave the way itās supposed to. Cheers!
@bender@twtxt.net Donāt panic. Iāve just been testing my implementation. The great advantage of Twtxt is itās openness, I think. So DM spamming would contradict to this feature I like. ā¤
@andros@twtxt.andros.dev Here is everything written down I know to this stuff: https://github.com/upputter/testing-twtxt-dm/blob/main/README.md
@eapl.me@eapl.me @andros@twtxt.andros.dev Eureka! It works! https://github.com/upputter/testing-twtxt-dm
PBKDF2_KEY_SIZE = 48
was the turning point! My dirty little crypt.class.php
can en- and decrypt, accoridng to the OpenSSL standard and options used in https://twtxt.dev/exts/direct-message.html
[ ā³ Reply to twt ]
button?
I donāt think so, at least the tests I did passed. If youāre pretty sure itās a bug, please create an issue in the repository with the specific case and Iāll investigate it.
There are 2 buttons to make replicas, one makes a replica in the thread where the twt is located (this is the one that should be used the most, as it serves a thread), the other creates a replica to a specific twt.
Iāll let you know a bit about the status: Iām just now implementing the thread screen. There you can be sure where you are. Itās a bit confusing right now, sorry. I think the client is still in alpha. When Iāve finished what Iām doing, and the direct message system, Iāll freeze development and focus on creating more tests, looking for bugs and making small visual adjustments.
Test Test⦠is this Borked too!? š¤
@eapl.me@eapl.me Here is what Iāve got so far: https://github.com/upputter/testing-twtxt-dm
There is a ā00_well_known_message.encā file, which I have the encryption paremters for (https://github.com/upputter/testing-twtxt-dm/blob/9fdf3be6aa8fe810a4cb275375dbb3d4a2a958ee/wellknown_test.php#L28).
According to my finding, I assume, that the saltsize
in openssl is ā8ā and the PBKDF2
algo is āsha256ā.
@arne@uplegger.eu Hi! I love that youāre implementing it! Maybe, when weāre both done, we could test the clients by communicating both.
I donāt think Iām going to be able to help you much, my knowledge of OpenSSL and PHP is not as high as Iād like it to be.
Maybe the OpenSSL version uses SHA-1 by default in PHP. Or that the IV is derived together with the key (not generated separately). But Iām not able to answer your questions, sorry.
Iām invoking the commands directly, without any libraries in between. Maybe that would help you?
@andros@twtxt.andros.dev I have really tried to get behind it. For an implementation for my TwtxtReader (PHP) I simply lack the knowledge of the standard-openssl parameters.
All my solution approaches require ānonceā or āinitialization vectorā on one or the other side. In addition, the āmagic numbersā (āSalted__ā) were not consistent in my tests.
The project is a POC (Proof of Concept) that went into production and the company has customers who are using it. The developers had been working for several years, without testing, structure, isolation and so on. The company hired me to transform the project into a real product. There are in my hands 422 python files to transform that they beg me a refactore, architecture and testing. Every developerās bad dream.
My first step is to read and understand the tree because there are apps inside other apps call each other. I am very determined to work on a new repository.
1st thought⦠Run!
Well, Iāve heard you have plenty of experience with Unit Testing and TDD. Perhaps designing a few tests before refactoring?
Iāve heard of Snapshot testing, but have never tried it: https://github.com/spatie/phpunit-snapshot-assertions
Also, what kind of refactor are you trying to do?
@andros@twtxt.andros.dev I suggest to not touch it and work on a different project instead. :-D
No, in all seriousness, thatās a tough one. Try to figure out the requirements and write tests to cover them. In my experience, if there is no good documention, tests might also be lacking. It goes without saying that you have to understand the code segments first before you can begin to refactor them. Commit even earlier and more often than usual, this will help you bisecting potentially introduced bugs later on. Basically baby steps.
But it also depends on the amount of refactoring required. Maybe just scrap it entirely and start from scratch. This might not be feasible due to e.g. the overall project size, though.
Testing the limits of our new 5G internet connection at home with pushing 1.5GB docker images into the cloud a bunch of times dayā¦
NASA tests new nuclear rocket fuel + 2 more stories
NASA and General Atomics test nuclear fuel; Ukraine employs more drones for war effort; engineered bacteria shows promise in recycling nylon waste. ā Read more
For many years I have found Flask to be too basic a tool for modern development. But since I create APIs using Flask with Pydantic to validate the input data, some middlewares for parsing and Blueprint to separate the code into modules⦠I must admit that I am super comfortable, fast and easy to test.
#flask #python #pydantic
@prologic@twtxt.net Or databag self-hosted on a RaspberryPi you can throw on a corner of your basement (or a small vps if one is willing to pay for threema) and never look back. The hardest part is getting others to at least test anything other than the already mainstream apps.
@andros@twtxt.andros.dev Sweeeeet! Just gave it a try, youāve done a wonderful work š«” I wanted to replay from there but couldnāt go past the first page of the feed. It kept freezing on me and complaining about some bad Url (as mentioned on the test twt), so Iāll have to dig through my follow list and see where I effed up this time. š
Skill Issues
of course, but that's going away next as soon as I get my php-fpm shi_ together.
@doesnm.p.psf.lt@doesnm.p.psf.lt yes, thatās what I did while testing things out on locally but Timeline
kept freaking out on me. Iām still RTFMing trying to wrap my head around the rewrites and such.
What is clean architecture? Thatās a good question.
You think of a pattern for ordering code with good decisions isolating technologies (you can change the web framework or database without break the business logic), easy to test (you only test interfaces and use cases), sharing code between frameworks (entities and use cases), scalability, modulations and standardizing names. Clean architecture is not perfect, it has a learning curve and some abstraction in each technology. You can even find rejection with yours colleagues.
I have a good article on this topic.
https://programadorwebvalencia.com/implementando-arquitectura-limpia-en-python/
#python
@prologic@twtxt.net @lyse@lyse.isobeef.org First, please leave me your comments on the repository! Even if itās just to give your opinion on what shouldnāt be included. The more variety, the better.
Second, Iām going to try to do tests with Elliptic keys and base64. Thanks for the advice @eapl@eapl.me
Finally, Iād like to give my opinion. Secure direct messages are a feature that ActivityPub and Mastodon donāt have, to give an example. By including it as an extension, weāre already taking a significant leap forward from the competition. Does it make sense to include it in a public feed? In fact, weāre already doing that. When we reply to a user, mentioning them at the beginning of the message, itās already a direct message. The message is within a thread, perhaps breaking the conversation. Direct messages would help isolate conversations between 2 users, as well as keeping a thread cleaner and maintaining privacy. I insist, itās optional, it doesnāt break compatibility with any client and implementing it isnāt complex. If you donāt like it, youāre free to not use it. If you donāt have a public key, no one can send you direct messages.
@doesnmppsflt@doesnm.p.psf.lt It looks like it⦠Although they shouldnāt be empty since Timeline took care of sending those. I believe I have an idea as to why that happened, but will have to test before filing an issue.
@doesnm.p.psf.lt@doesnm.p.psf.lt Testing Testing⦠123⦠Ouiiiiiii⦠š
Test reply for webmention tester!
@movq@www.uninformativ.de I think it was user error. Testing again.
Testing ⦠(yeah!)
The editor can launch a new shell now:
https://movq.de/v/6ec68b50dd/los86-edit-shell.mp4
Trivial to implement but super useful. It allows for simple but meaningful dev cycles: Edit source code, run/test it, back to editor. Thatās what I do in the video.
(The Brainfuck program is silly, but I got nothing else at the moment.)
The I/O cache is also getting better. All that back and forth doesnāt hit the disk at all, once cached.
This whole thing is much more fun and interesting when you run it from a real floppy disk. Itās a 5.25ā floppy in the video (so itās actually floppy š ). Disk seek times can be catastrophic and you donāt notice any of this on modern disks.
Test twtxt parser (please remind me to delete this)
Iām still making progress with the Emacs client. Iām proud to say that the code that is responsible for reading the feeds is almost finished, including: Twt Hash Extension, Twt Subject Extension, Multiline Extension and Metadata Extension. Iām fine-tuning some tests and will soon do the first buffer that displays the twts.
Meh, I hit an import cycle while writing tests. Now I have to relocate some code. What do we conclude from that: donāt write tests. ;-)
tz test