Most answers disapprove assertions against strings, as of my time of writing. But, I want to say something for the other side.
In essence, to test against strings, is to test against a Domain Specific Language (DSL).
This is a strong tool to use when testing within the normal scope feels no longer handy. For example, you wanted to test a series of dependent events happened in order, instead of mocking quite a few objects and testing that many functions did get called, you could write something like this:
expected_execution_log = """
Change highlight threshold 50 -> 70
Update label value 50 -> 70
Request new batch of data (threshold = 70)
Set grid overlay None -> Loading
Received new batch of data (threshold = 70, count = 2)
Set grid content (count = 2)
Set grid overlay Loading -> None
assert log == expected_execution_log
This pedagogical example illustrates user interaction, async execution, view updates, and content updates. It's very hard to describe the behavior completely in your test.
Taking a step back, if you assert against execution logs, you only have to insert a spy logger, and add some log statements
You should have already got accustomed to logging, with or without tests.
Always remember that you are never able to describe the full set of the behavior of your application, because doing so equals to writing your application again, just with more clumsy syntax and more verifications. The reason why we love tests so much is that by probability, if you accidentally, insanely, messed up with something, you are likely to trigger the trap that your tests are holding to, so that the sober, sensible you would get alarmed and fix that problem.
So, you should remember that by testing, especially by test driving, you are actually setting traps for a future, careless you, to trigger that trap. If the trap itself is so difficult for you to setup, then why not use a weaker, yet simpler one? After all, setting traps is dangerous too! A convoluted test is not that important than a clear test. Just write the feel and it's OK to live with it.
 In this world, we can only know things through their behaviors, or phenomena. If A is able to replicate every possible behavior of B, then A effectively contains B. Since this is not a philosophy lecture, I prefer not to elaborate on this point.
 You might be thinking: there is still a small chance that I don't get alerted! Yes. You might have guessed it. A possibility of 100% of getting alarmed when anything goes wrong? We call this static verification. There are many possible approaches, such as symbolic execution, which aim to verify the program before it gets put to usage. This goal is usually pursued for critical software on cars, planes, civil infrastructure, space exploration and military utilities.