How do I check if AJAX nonces are implemented correctly?

Your code is incorrect in some subtle ways but to get why we first need to get back to what is a nonce in wordpress and what does it protect against.

A Nonce by the common definition is a number used only once and it is meant to fight replay attacks. In a replay attack you just replay a transaction (http request) without understanding its component to redo a user action. simple example where it can lead to bad situation is if someone could replay your shopping orders and cause you to order more of the same thing without you being involved. Typical situation you use someone else’s network (not sure if SSL helps or not here).

It was also discovered that nonce are a good way to avoid CSRF attacks as it is hard to predict the URL/parameters the attacker should use.

In wordpress the order of importance is reversed, the first priority is to fight CSRF attacks and fighting replay attacks have just a secondary importance. This leads into wordpress nonces being a “unique per day” number instead of the classical “one time” number.

so how does wordpress uses nonces to protect agaunst CSRF? it generates a nonce based on the context. You can see in the wp_create_nonce code

function wp_create_nonce($action = -1) {
    $user = wp_get_current_user();
    $uid = (int) $user->ID;
    if ( ! $uid ) {
        /** This filter is documented in wp-includes/pluggable.php */
        $uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
    }

    $token = wp_get_session_token();
    $i = wp_nonce_tick();

    return substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
}

That the context is the action name, the user id, and the current login session and this ensures that a different nonce will be generated for different actions, different users and even different sessions of the same user, therefor even if someone knows the nonce used for to do an action (lets say, delete a post) when doing it with his own user, he will not be able to trick you into deleting a post by constructing special URL with the same nonce.

And here is your fist mistake (which many do), which is trying to apply nonces to non logged in users. Without a user context whole of the nonce is derived just from the action, and an “attacker” can just share the same url to be used by non logged in users (or by a bot).

How to test that your code works correctly in preventing CSRF? if it is a simple URL, try to resend it when logged in as another user. It is easier to do with requests in which the url itself contains the nonce, but probably can be done with browser developer tools (change authentication cookies and re-send) or curl/wget.

The other part in which you code is lacking is a protection against a sort of a replay attack. Assuming I know that you liked post X, can I re transmit the request and cause it to be seen like you liked also post Y? Right now all I need to do is just change th post_id parameter of the request.

The way to protect against this kind of attacks is by having the action part of the nonce be dynamic and include in some way the important parameters, in your case the post id. Core usually uses actions of the kind delete-post-{post_id} which ensures that there will be a different nonce generated for different posts.

How to test? Using the browser developer tools, change the post id parameter to ensure the same nonce can not be used to favorite a different posts.

For phpunit type of testing you will probably want to write your own nonce generator that wraps the wordpress core nonce functions and check they do not give the same nonce for different users, different sessions, different actions, and different key request parameters.