12

Here's one example: My web application contains draggable elements. When dragging an element, the browser produces a "ghost image". I want to remove the "ghost image" when dragging and I write a test for this behaviour.

My problem is that I initially have no idea how to fix this bug and the only way I can write a test is after I have fixed it.

In a simple function such as let sum = (a, b) => a - b, you can write a test as to why sum(1, 2) does not equal 3 before writing any code.

In the case I am describing, I can't test, because I don't know what the verification is (I don't know what the assertion should be).

A solution to the problem described is:

let dataTransfer = e.dataTransfer
let canvas = document.createElement('canvas');
canvas.style.opacity = '0';
canvas.style.position = 'absolute';
canvas.style.top = '-1000px';
dataTransfer.effectAllowed = 'none';

document.body.appendChild(canvas);
dataTransfer.setDragImage(canvas, 0, 0);

I could not have known that this was the solution. I could not even have written the test after finding the solution online, because the only way I could have known if it really worked, was to add this code into my codebase and verify with the browser if it had the desired effect. The test had to be written after the code, which goes against TDD.

What would be the TDD approach to this problem? Is writing the test before the code mandatory or optional?

2
  • 2
    Do your research then. Find a solution... then write your test, fix, and refactor. Tests are not meant to (only) verify that your code works, but to future proof your complete solution. Initially you create your test to fail so, what property will you be testing? That's one way to begin. May 23, 2018 at 22:10
  • @Kilian Foth: I see your good intentions by changing the question title, but your edit invalidated parts of my answer. Moreover, your new title did IMHO not fit well to the question body. So I made a rollback, no offense.
    – Doc Brown
    May 25, 2018 at 12:20

4 Answers 4

27

When I understood you correctly, you cannot even write a reliable automated test for your "ghost image" example after you found a solution, since the only way of verifying the correct behaviour is to look at the screen and check if there is no ghost image any more. That gives me the impression your original headline asked the wrong question. The real question should be

  • how to automatically test a certain behaviour of a graphical user interface?

And the answer is - for several kind of UI issues, you don't. Sure, one can try to automate making the UI showing the problem somehow, and try to implement something like a screenshot comparison, but this is often error-prone, brittle and not cost-effective.

Especially "test driving" UI design or UI improvements by automated tests written in advance is literally impossible. You "drive" UI design by making an improvement, show the result to a human (yourself, some testers or a user) and ask for feedback.

So accept the fact TDD is not a silver bullet, and for some kind of issues still manual testing makes more sense than automated tests. If you have a systematic testing process, maybe with some dedicated testers, best thing you can do is to add the case to their test plan.

4
  • In general UI testing is non-trivial; you can pin i.e. hash a generated image, you can simulate/automate, you can record macros, you can use a proprietary solution, you can use manual testing, depends on the situation and how necessary automated UI testing is for your project.
    – esoterik
    May 23, 2018 at 21:33
  • 1
    @esoterik: yep, and all these automated techniques are error-prone and brittle, as I already wrote. The only non-brittle approach I know is manual testing.
    – Doc Brown
    May 23, 2018 at 21:36
  • 3
    Thanks for answering. I think you are right, I'm wrongly hoping to find a silver bullet in TDD. There doesn't seem to be an efficient way to test what I want to test - screenshot comparison and all the above do not seem to give a sufficient ROI. Screenshot comparison in particular seems to be very time consuming and, as you said, error prone. May 23, 2018 at 21:46
  • 1
    @maximedupre: found this advertisement for a tool which tries to address the problem, but the nevertheless the article seems to agree with my answer in general.
    – Doc Brown
    May 23, 2018 at 21:51
5

What would be the TDD approach to this problem? Is writing the test before the code mandatory or optional?

One way is to apply an analog of a spike solution.

James Shore described it this way:

We perform small, isolated experiments when we need more information.

The basic idea is that you put down the design tools while you are figuring out what is going on. Once you have your bearings, you pick up the design tools again.

The trick: you bring the knowledge from your investigation back into your production code base, but you don't bring the code. Instead, you recreate it while using your disciplined design techniques.

Horses for courses.

EDIT:

How can you automate a test if the defect can only be seen by a human eye?

I'd like to suggest a slightly different spelling: "How can you automated a test if automating the test isn't cost effective?"

The answer, of course, is "you don't". Instead, strive to separate your implementation into two parts - a large part where testing is cost effective, and a smaller part that is too simple to break.

There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies -- C.A.R. Hoare

So when working with third party code, we'll have a very thin shell of code that acts as an proxy for the third party library. In test, we replace that shell with a "test double", that verifies the protocol, without worrying that it produces the desired effects.

For the testing of our codes integration with the real third party code, we rely on other techniques (visual verification, tech support calls, optimism....)

It can be useful to keep some demonstration artifacts around, so that you can check that your assumptions still hold when you upgrade the third party library.

2
  • I love what James Shore has to say. I'm currently following the www.letscodejavascript.com screencast and I'm learning a lot. I'll read the links you pointed me to. May 23, 2018 at 21:50
  • You are right, I read more about TDD and spikes. You actually need to know how the code you are trying to check looks like before attempting to test it. TDD can't teach you anything you don't already know, but it can potentially show you some things that you need to learn, paraphrasing James Shore. On that note, I would like to propose an addition step in TDD: Spike, Test, Fail, Pass, Refactor. May 25, 2018 at 14:28
0

Just a different perspective, testing around the UI/GUI can be done somewhat better in respect to acceptance testing (feature/business work flow centered tests).

For the web, I think frameworks like selenium webdriver have the potential to get close to the correct test, but the overhead to get started could be a bit much, and it is a shift in the flow seen with TDD in respect to just unit tests.

The part that would specifically help with your situation is something called Page Object Model (https://www.guru99.com/page-object-model-pom-page-factory-in-selenium-ultimate-guide.html). This achieves an explicit mapping of run-time GUI to some code, either by naming of methods/events/class members.

The main arguments against this would be the overhead, and that this overhead could usually be seen at the end of the cycle for development. The overhead being that the tests require some wrapper that would appear to create duplicated work.

Moving forward, it would depend on the cost/benefit of the team and business, so it could be a beneficial topic to discuss to determine expectations and views.

2
  • I was actually trying out e2e/functional tests (with selenium webdriver) in TDD recently, but the overhead is definitely way too big as you said. You can't be an efficient developer and do TDD with e2e tests. I was using POM, but the only benefit it gave me was an improved architecture in my test codebase. May 24, 2018 at 15:07
  • Yeah, I think a more viable option that I have seen from different companies to different teams would be to incorporate a more manual SQA process, where a team/team member is designated to only preform manual tests from the UI. The tests mostly have screenshots and step-by-step documentation. At the very least, something like that would produce some evidence of testing towards an application. May 24, 2018 at 17:42
0

What does a ghost image look like? If you created a dummy ui of one known color, where you put your draggable component? Would there be a specific color present if there was a ghost image.

Then the test could test for the absense of the color of the ghost image.

Such a test would be reasonable durable and doable.

4
  • Doable - yes. Durable - depends. Just changing the colour/theme of your UI could break your test/s, that doesn't sound too durable to me. May 24, 2018 at 11:39
  • 1
    You wouldn't test you whole UI. You would create a dummy ui for the drag-n-drop component. May 24, 2018 at 11:50
  • I'm not sure how to accomplish what you are suggesting. A ghost image in my case would be a semi-opaque replica of the element that is being dragged. The ghost image is following the cursor at all time while the drag and drop is occurring. May 24, 2018 at 14:56
  • Yes. You would have to automate the dragging. It would not be a unit test but rather an e2e test. May 24, 2018 at 19:22

Not the answer you're looking for? Browse other questions tagged or ask your own question.